ObjFW  Check-in [a2973418de]

Overview
Comment:Merge trunk into branch "wii-u"
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wii-u
Files: files | file ages | folders
SHA3-256: a2973418de4df1231674470bb430703604a1a6799de2bed087b4077e3d07091b
User & Date: js on 2022-07-15 18:09:53
Other Links: branch diff | manifest | tags
Context
2022-07-15
18:14
tests: Add target to generate tests.rpx check-in: 834ddf94fd user: js tags: wii-u
18:09
Merge trunk into branch "wii-u" check-in: a2973418de user: js tags: wii-u
2022-07-14
15:15
utils/ofhttp: Improve Unicode detection on Windows check-in: 0c7c78f397 user: js tags: trunk
2020-11-03
01:07
configure: Add flags for Wii U check-in: 5b8a7e4d0d user: js tags: wii-u
Changes

Modified .fossil-settings/clean-glob from [8cc139c114] to [0c0ff942fb].

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
*.a
*.bundle
*.dep
*.dll
*.dylib
*.exe
*.framework
*.library
*.map
*.o

*.so
*.so.*
*/.deps
.deps
aclocal.m4
autom4te.cache
boot.dol
buildsys.mk
config.h
config.h.in
config.log
config.status
configure
docs
extra.mk

generators/gen_tables
src/Info.plist
src/bridge/Info.plist

src/objfw-defs.h
src/runtime/Info.plist
src/runtime/amiga-library-functable.inc
src/runtime/inline.h

tests/DerivedData
tests/EBOOT.PBP
tests/Info.plist
tests/PARAM.SFO
tests/objc_sync/objc_sync
tests/plugin/Info.plist
tests/terminal/terminal_tests
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
utils/objfw-config
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp
utils/ofsock/ofsock










>















>
|


>


<
|
>
















<
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

*.a
*.bundle
*.dep
*.dll
*.dylib
*.exe
*.framework
*.library
*.map
*.o
*.sl
*.so
*.so.*
*/.deps
.deps
aclocal.m4
autom4te.cache
boot.dol
buildsys.mk
config.h
config.h.in
config.log
config.status
configure
docs
extra.mk
generators/library/gen_libraries
generators/unicode/gen_tables
src/Info.plist
src/bridge/Info.plist
src/libobjfw.*
src/objfw-defs.h
src/runtime/Info.plist

src/runtime/libobjfwrt.*
src/tls/Info.plist
tests/DerivedData
tests/EBOOT.PBP
tests/Info.plist
tests/PARAM.SFO
tests/objc_sync/objc_sync
tests/plugin/Info.plist
tests/terminal/terminal_tests
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
utils/objfw-config
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp

Modified .fossil-settings/ignore-glob from [9af6ddf1c8] to [13af81fd69].

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
*.a
*.bundle
*.dep
*.dll
*.dylib
*.exe
*.framework
*.library
*.map
*.o
*.orig

*.so
*.so.*
*/.deps
.deps
.git
aclocal.m4
autom4te.cache
boot.dol
buildsys.mk
config.h
config.h.in
config.log
config.status
configure
docs
extra.mk

generators/gen_tables
src/Info.plist
src/bridge/Info.plist

src/objfw-defs.h
src/runtime/Info.plist
src/runtime/amiga-library-functable.inc
src/runtime/inline.h

tests/DerivedData
tests/EBOOT.PBP
tests/Info.plist
tests/PARAM.SFO
tests/iOS.xcodeproj/*.pbxuser
tests/iOS.xcodeproj/project.xcworkspace
tests/iOS.xcodeproj/xcuserdata
tests/objc_sync/objc_sync
tests/plugin/Info.plist
tests/terminal/terminal_tests
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
utils/objfw-config
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp
utils/ofsock/ofsock











>
















>
|


>


<
|
>



















<
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

*.a
*.bundle
*.dep
*.dll
*.dylib
*.exe
*.framework
*.library
*.map
*.o
*.orig
*.sl
*.so
*.so.*
*/.deps
.deps
.git
aclocal.m4
autom4te.cache
boot.dol
buildsys.mk
config.h
config.h.in
config.log
config.status
configure
docs
extra.mk
generators/library/gen_libraries
generators/unicode/gen_tables
src/Info.plist
src/bridge/Info.plist
src/libobjfw.*
src/objfw-defs.h
src/runtime/Info.plist

src/runtime/libobjfwrt.*
src/tls/Info.plist
tests/DerivedData
tests/EBOOT.PBP
tests/Info.plist
tests/PARAM.SFO
tests/iOS.xcodeproj/*.pbxuser
tests/iOS.xcodeproj/project.xcworkspace
tests/iOS.xcodeproj/xcuserdata
tests/objc_sync/objc_sync
tests/plugin/Info.plist
tests/terminal/terminal_tests
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
utils/objfw-config
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp

Modified .github/ISSUE_TEMPLATE/config.yml from [c578ed1d2f] to [ee9213e864].

11
12
13
14
15
16
17















18





19
20
21
  - name: ObjFW Forum
    url: https://objfw.nil.im/forum
    about: Please use this if you have questions or want support.
  - name: ObjFW Matrix Room
    url: https://matrix.to/#/%23objfw:nil.im
    about: Please use this for interactive questions and support.
  - name: ObjFW IRC Channel















    url: https://webchat.freenode.net/?channels=objfw





    about:
      Please use this for interactive questions and support - it is bridged to 
      the Matrix room above.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>



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
  - name: ObjFW Forum
    url: https://objfw.nil.im/forum
    about: Please use this if you have questions or want support.
  - name: ObjFW Matrix Room
    url: https://matrix.to/#/%23objfw:nil.im
    about: Please use this for interactive questions and support.
  - name: ObjFW IRC Channel
    url: https://webchat.oftc.net/?channels=%23objfw
    about:
      Please use this for interactive questions and support - it is bridged to 
      the Matrix room above.
  - name: ObjFW Slack Channel
    url: https://objfw.nil.im/slack
    about:
      Please use this for interactive questions and support - it is bridged to 
      the Matrix room above.
  - name: ObjFW Discord Channel
    url: https://objfw.nil.im/discord
    about:
      Please use this for interactive questions and support - it is bridged to 
      the Matrix room above.
  - name: ObjFW Telegram Room
    url: https://t.me/objfw
    about:
      Please use this for interactive questions and support - it is bridged to 
      the Matrix room above.
  - name: ObjFW Gitter Room
    url: https://gitter.im/ObjFW/ObjFW
    about:
      Please use this for interactive questions and support - it is bridged to 
      the Matrix room above.

Added .github/workflows/amiga-gcc.yml version [16471286d2].











































































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

Added .github/workflows/ios.yml version [dd34f22809].







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
name: ios
on: [push, pull_request]
jobs:
  build:
    runs-on: macos-latest
    strategy:
      matrix:
        arch:
          - arm64
          - x86_64
        configure_flags:
          - 
          - --disable-shared
    steps:
    - name: Install dependencies
      run: brew install autoconf automake
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: |
        export IPHONEOS_DEPLOYMENT_TARGET="9.0"
        if [ "${{ matrix.arch}}" = "x86_64" ]; then
          sdk="iphonesimulator"
        else
          sdk="iphoneos"
        fi
        export OBJC="clang -isysroot $(xcrun --sdk $sdk --show-sdk-path)"
        export OBJC="$OBJC -arch ${{ matrix.arch }}"
        ./configure --host=${{ matrix.arch }}-apple-darwin \
          ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(sysctl -n hw.logicalcpu)
    - name: make install
      run: sudo make install

Added .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-11.yml version [d79ffc1fb9].































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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-11
on: [push, pull_request]
jobs:
  tests:
    runs-on: macos-11
    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/morphos.yml version [0f27c2ab37].











































































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

Added .github/workflows/nintendo-3ds.yml version [67061ee611].







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
name: nintendo-3ds
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Install dependencies
      run: docker pull devkitpro/devkitarm
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: |
        docker run \
          -e DEVKITPRO=/opt/devkitpro \
          -e PATH="/opt/devkitpro/devkitARM/bin:$PATH" \
          -v "$PWD:/objfw" \
          devkitpro/devkitarm \
          sh -c 'cd /objfw && ./configure --host=arm-none-eabi --with-3ds'
    - name: make
      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)"
    - 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)"

Added .github/workflows/nintendo-ds.yml version [3d41e0f5f7].







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
name: nintendo-ds
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Install dependencies
      run: docker pull devkitpro/devkitarm
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: |
        docker run \
          -e DEVKITPRO=/opt/devkitpro \
          -e PATH="/opt/devkitpro/devkitARM/bin:$PATH" \
          -v "$PWD:/objfw" \
          devkitpro/devkitarm \
          sh -c 'cd /objfw && ./configure --host=arm-none-eabi --with-nds'
    - name: make
      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)"
    - 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)"

Added .github/workflows/ubuntu-18.04-32bit.yml version [96c9a8ae41].











































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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: ubuntu-18.04, 32 bit
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-18.04
    strategy:
      matrix:
        configure_flags:
          - --without-tls
          - --without-tls --enable-seluid24
          - --without-tls --disable-compiler-tls
          - --without-tls --disable-threads
          - --without-tls --disable-threads --disable-sockets
          - --without-tls --disable-threads --disable-files
          - --without-tls --disable-threads --disable-sockets --disable-files
          - --without-tls --disable-sockets
          - --without-tls --disable-sockets --disable-files
          - --without-tls --disable-files
          - --without-tls --disable-shared
          - --without-tls --disable-shared --enable-seluid24
          - --without-tls --disable-compiler-tls --disable-threads
    steps:
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install gcc-multilib
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: ./configure OBJC="clang -m32" ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(nproc)
    - name: make check
      run: make check
    - name: make install
      run: sudo make install

Added .github/workflows/ubuntu-18.04-gcc-32bit.yml version [0e720701c6].











































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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: ubuntu-18.04, GCC, 32 bit
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-18.04
    strategy:
      matrix:
        configure_flags:
          - --without-tls
          - --without-tls --enable-seluid24
          - --without-tls --disable-compiler-tls
          - --without-tls --disable-threads
          - --without-tls --disable-threads --disable-sockets
          - --without-tls --disable-threads --disable-files
          - --without-tls --disable-threads --disable-sockets --disable-files
          - --without-tls --disable-sockets
          - --without-tls --disable-sockets --disable-files
          - --without-tls --disable-files
          - --without-tls --disable-shared
          - --without-tls --disable-shared --enable-seluid24
          - --without-tls --disable-compiler-tls --disable-threads
    steps:
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install gcc-multilib gobjc
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: ./configure OBJC="gcc -m32" ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(nproc)
    - name: make check
      run: make check
    - name: make install
      run: sudo make install

Added .github/workflows/ubuntu-18.04-gcc.yml version [9e75cc2f04].















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
name: ubuntu-18.04, GCC
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-18.04
    strategy:
      matrix:
        configure_flags:
          -
          - --enable-seluid24
          - --disable-compiler-tls
          - --disable-threads
          - --disable-threads --disable-sockets
          - --disable-threads --disable-files
          - --disable-threads --disable-sockets --disable-files
          - --disable-sockets
          - --disable-sockets --disable-files
          - --disable-files
          - --disable-shared
          - --disable-shared --enable-seluid24
          - --disable-compiler-tls --disable-threads
          - --with-tls=gnutls
          - --with-tls=gnutls --disable-shared
    steps:
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install gobjc libssl-dev gnutls-dev
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: ./configure OBJC="gcc" ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(nproc)
    - name: make check
      run: make check
    - name: make install
      run: sudo make install

Added .github/workflows/ubuntu-18.04.yml version [d0186b3566].















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
name: ubuntu-18.04
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-18.04
    strategy:
      matrix:
        configure_flags:
          -
          - --enable-seluid24
          - --disable-compiler-tls
          - --disable-threads
          - --disable-threads --disable-sockets
          - --disable-threads --disable-files
          - --disable-threads --disable-sockets --disable-files
          - --disable-sockets
          - --disable-sockets --disable-files
          - --disable-files
          - --disable-shared
          - --disable-shared --enable-seluid24
          - --disable-compiler-tls --disable-threads
          - --with-tls=gnutls
          - --with-tls=gnutls --disable-shared
    steps:
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install libssl-dev gnutls-dev
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: ./configure ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(nproc)
    - name: make check
      run: make check
    - name: make install
      run: sudo make install

Added .github/workflows/ubuntu-20.04-32bit.yml version [6af581a780].











































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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: ubuntu-20.04, 32 bit
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-20.04
    strategy:
      matrix:
        configure_flags:
          - --without-tls
          - --without-tls --enable-seluid24
          - --without-tls --disable-compiler-tls
          - --without-tls --disable-threads
          - --without-tls --disable-threads --disable-sockets
          - --without-tls --disable-threads --disable-files
          - --without-tls --disable-threads --disable-sockets --disable-files
          - --without-tls --disable-sockets
          - --without-tls --disable-sockets --disable-files
          - --without-tls --disable-files
          - --without-tls --disable-shared
          - --without-tls --disable-shared --enable-seluid24
          - --without-tls --disable-compiler-tls --disable-threads
    steps:
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install gcc-multilib
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: ./configure OBJC="clang -m32" ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(nproc)
    - name: make check
      run: make check
    - name: make install
      run: sudo make install

Added .github/workflows/ubuntu-20.04-gcc-32bit.yml version [16adbb51e7].











































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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: ubuntu-20.04, GCC, 32 bit
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-20.04
    strategy:
      matrix:
        configure_flags:
          - --without-tls
          - --without-tls --enable-seluid24
          - --without-tls --disable-compiler-tls
          - --without-tls --disable-threads
          - --without-tls --disable-threads --disable-sockets
          - --without-tls --disable-threads --disable-files
          - --without-tls --disable-threads --disable-sockets --disable-files
          - --without-tls --disable-sockets
          - --without-tls --disable-sockets --disable-files
          - --without-tls --disable-files
          - --without-tls --disable-shared
          - --without-tls --disable-shared --enable-seluid24
          - --without-tls --disable-compiler-tls --disable-threads
    steps:
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install gcc-multilib gobjc
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: ./configure OBJC="gcc -m32" ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(nproc)
    - name: make check
      run: make check
    - name: make install
      run: sudo make install

Added .github/workflows/ubuntu-20.04-gcc.yml version [dd262654f6].















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
name: ubuntu-20.04, GCC
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-20.04
    strategy:
      matrix:
        configure_flags:
          -
          - --enable-seluid24
          - --disable-compiler-tls
          - --disable-threads
          - --disable-threads --disable-sockets
          - --disable-threads --disable-files
          - --disable-threads --disable-sockets --disable-files
          - --disable-sockets
          - --disable-sockets --disable-files
          - --disable-files
          - --disable-shared
          - --disable-shared --enable-seluid24
          - --disable-compiler-tls --disable-threads
          - --with-tls=gnutls
          - --with-tls=gnutls --disable-shared
    steps:
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install gobjc libssl-dev gnutls-dev
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: ./configure OBJC="gcc" ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(nproc)
    - name: make check
      run: make check
    - name: make install
      run: sudo make install

Added .github/workflows/ubuntu-20.04.yml version [051ed40d72].















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
name: ubuntu-20.04
on: [push, pull_request]
jobs:
  tests:
    runs-on: ubuntu-20.04
    strategy:
      matrix:
        configure_flags:
          -
          - --enable-seluid24
          - --disable-compiler-tls
          - --disable-threads
          - --disable-threads --disable-sockets
          - --disable-threads --disable-files
          - --disable-threads --disable-sockets --disable-files
          - --disable-sockets
          - --disable-sockets --disable-files
          - --disable-files
          - --disable-shared
          - --disable-shared --enable-seluid24
          - --disable-compiler-tls --disable-threads
          - --with-tls=gnutls
          - --with-tls=gnutls --disable-shared
    steps:
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install libssl-dev gnutls-dev
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: ./configure ${{ matrix.configure_flags }}
    - name: make
      run: make -j$(nproc)
    - name: make check
      run: make check
    - name: make install
      run: sudo make install

Added .github/workflows/wii.yml version [39c842705b].







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
name: wii
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Install dependencies
      run: docker pull devkitpro/devkitppc
    - uses: actions/checkout@v2
    - name: autogen.sh
      run: ./autogen.sh
    - name: configure
      run: |
        docker run \
          -e DEVKITPRO=/opt/devkitpro \
          -e PATH="/opt/devkitpro/devkitPPC/bin:$PATH" \
          -v "$PWD:/objfw" \
          devkitpro/devkitppc \
          sh -c 'cd /objfw && ./configure --host=powerpc-eabi --with-wii'
    - name: make
      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)"
    - 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)"

Modified .gitignore from [1a9a1a32ef] to [fdb1296647].

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
*.a
*.bundle
*.dep
*.dll
*.dylib
*.exe
*.framework
*.library
*.map
*.o
*.orig

*.so
*.so.*
.deps
.fslckout
_FOSSIL_
aclocal.m4
autom4te.cache
boot.dol
buildsys.mk
config.h
config.h.in
config.log
config.status
configure
docs
extra.mk

generators/gen_tables
src/Info.plist
src/bridge/Info.plist

src/objfw-defs.h
src/runtime/Info.plist
src/runtime/amiga-library-functable.inc
src/runtime/inline.h

tests/DerivedData
tests/EBOOT.PBP
tests/Info.plist
tests/PARAM.SFO
tests/iOS.xcodeproj/*.pbxuser
tests/iOS.xcodeproj/project.xcworkspace
tests/iOS.xcodeproj/xcuserdata
tests/objc_sync/objc_sync
tests/plugin/Info.plist
tests/terminal/terminal_tests
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
utils/objfw-config
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp
utils/ofsock/ofsock











>
















>
|


>


<
|
>



















<
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

*.a
*.bundle
*.dep
*.dll
*.dylib
*.exe
*.framework
*.library
*.map
*.o
*.orig
*.sl
*.so
*.so.*
.deps
.fslckout
_FOSSIL_
aclocal.m4
autom4te.cache
boot.dol
buildsys.mk
config.h
config.h.in
config.log
config.status
configure
docs
extra.mk
generators/library/gen_libraries
generators/unicode/gen_tables
src/Info.plist
src/bridge/Info.plist
src/libobjfw.*
src/objfw-defs.h
src/runtime/Info.plist

src/runtime/libobjfwrt.*
src/tls/Info.plist
tests/DerivedData
tests/EBOOT.PBP
tests/Info.plist
tests/PARAM.SFO
tests/iOS.xcodeproj/*.pbxuser
tests/iOS.xcodeproj/project.xcworkspace
tests/iOS.xcodeproj/xcuserdata
tests/objc_sync/objc_sync
tests/plugin/Info.plist
tests/terminal/terminal_tests
tests/tests
tests/tests.3dsx
tests/tests.arm9
tests/tests.nds
utils/objfw-config
utils/ofarc/ofarc
utils/ofdns/ofdns
utils/ofhash/ofhash
utils/ofhttp/ofhttp

Deleted .travis.yml version [0619e15749].

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
language: c

matrix:
  include:
    # Linux
    - os: linux
      compiler: clang
      dist: precise
      sudo: required

    - os: linux
      compiler: gcc
      dist: precise
      sudo: required

    - os: linux
      arch: arm64
      compiler: clang
      dist: precise
      sudo: required

    - os: linux
      arch: arm64
      compiler: gcc
      dist: precise
      sudo: required

    - os: linux
      arch: ppc64le
      compiler: clang
      dist: precise
      sudo: required

    - os: linux
      arch: ppc64le
      compiler: gcc
      dist: precise
      sudo: required

    # Clang seems to have broken exceptions on s390x
    #- os: linux
    #  arch: s390x
    #  compiler: clang
    #  dist: precise
    #  sudo: required

    - os: linux
      arch: s390x
      compiler: gcc
      dist: precise
      sudo: required

    - os: linux
      compiler: clang
      dist: trusty
      sudo: required

    - os: linux
      compiler: gcc
      dist: trusty
      sudo: required

    - os: linux
      compiler: clang
      dist: xenial
      sudo: required

    - os: linux
      compiler: gcc
      dist: xenial
      sudo: required

    - os: linux
      compiler: clang
      dist: bionic
      sudo: required

    - os: linux
      compiler: gcc
      dist: bionic
      sudo: required

    # macOS
    - os: osx
      osx_image: xcode11.2
      language: objective-c
      env:
        - no32bit=1
        - noruntime=1  # Broken compiler in this version of Xcode
    - os: osx
      osx_image: xcode11.1
      language: objective-c
      env:
        - no32bit=1
        - noruntime=1  # Broken compiler in this version of Xcode
    - os: osx
      osx_image: xcode11
      language: objective-c
      env:
        - no32bit=1
        - noruntime=1  # Broken compiler in this version of Xcode
    - os: osx
      osx_image: xcode10.3
      language: objective-c
      env:
        - no32bit=1
    - os: osx
      osx_image: xcode10.2
      language: objective-c
      env:
        - no32bit=1
    - os: osx
      osx_image: xcode10.1
      language: objective-c
    - os: osx
      osx_image: xcode10
      language: objective-c
    - os: osx
      osx_image: xcode9.4
      language: objective-c
    - os: osx
      osx_image: xcode9.3
      language: objective-c
    - os: osx
      osx_image: xcode9.2
      language: objective-c
    - os: osx
      osx_image: xcode9.1
      language: objective-c
    - os: osx
      osx_image: xcode9
      language: objective-c
    - os: osx
      osx_image: xcode8.3
      language: objective-c
    - os: osx
      osx_image: xcode8
      language: objective-c
    - os: osx
      osx_image: xcode7.3
      language: objective-c

    # iOS
    - os: osx
      osx_image: xcode11.2
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode11.1
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode11
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode10.3
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode10.2
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode10.1
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode10
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode9.4
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode9.3
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode9.2
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode9.1
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode9
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode8.3
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode8
      language: objective-c
      env:
        - config=ios
    - os: osx
      osx_image: xcode7.3
      language: objective-c
      env:
        - config=ios

    # AmigaOS
    - os: linux
      dist: trusty
      env:
        - config=amigaos

    # Nintendo 3DS
    - os: linux
      dist: bionic
      env:
        - config=nintendo_3ds

    # Nintendo DS
    - os: linux
      dist: bionic
      env:
        - config=nintendo_ds

    # Nintendo Wii
    - os: linux
      dist: bionic
      env:
        - config=wii

services: docker

before_install:
  - .travis/before_install.sh

script:
  - .travis/script.sh
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































































































































































































































































































































































































Deleted .travis/before_install.sh version [479fa2374a].

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
#!/bin/sh
if [ "$TRAVIS_OS_NAME" = "linux" -a -z "$config" ]; then
	case "$TRAVIS_CPU_ARCH" in
		amd64 | s390x)
			pkgs="gobjc-multilib"
			;;
		*)
			pkgs="gobjc"
			;;
	esac

	pkgs="$pkgs libsctp-dev"

	if grep precise /etc/lsb-release >/dev/null; then
		pkgs="$pkgs ipx"
	fi

	# We don't need any of them and they're often broken.
	sudo rm -f /etc/apt/sources.list.d/*

	if ! sudo apt-get -qq update >/tmp/apt_log 2>&1; then
		cat /tmp/apt_log
		exit 1
	fi

	if ! sudo apt-get -qq install -y $pkgs >>/tmp/apt_log 2>&1; then
		cat /tmp/apt_log
		exit 1
	fi

	if grep precise /etc/lsb-release >/dev/null; then
		sudo ipx_internal_net add 1234 123456
	fi
fi

if [ "$config" = "nintendo_3ds" -o "$config" = "nintendo_ds" ]; then
	docker pull devkitpro/devkitarm
fi

if [ "$config" = "wii" ]; then
	docker pull devkitpro/devkitppc
fi

if [ "$config" = "amigaos" ]; then
	wget -q https://franke.ms/download/amiga-gcc.tgz
	tar -C / -xzf amiga-gcc.tgz
fi
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































Deleted .travis/build.sh version [02fcddbdad].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/sh
cd $(dirname $0)/..

echo ">> Configuring with $@"
if ! ./configure ac_cv_path_TPUT= "$@"; then
	cat config.log
	exit 1
fi

echo ">> Building (configured with $@)"
if ! make -j4 >/tmp/make_log 2>&1; then
	cat /tmp/make_log
	exit 1
fi

echo ">> Installing (configured with $@)"
if ! sudo PATH="$PATH" make install >/tmp/install_log 2>&1; then
	cat /tmp/install_log
	exit 1
fi
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































Deleted .travis/script.sh version [6432052d0a].

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
#!/bin/sh
build() {
	if ! git clean -fxd >/tmp/clean_log 2>&1; then
		cat /tmp/clean_log
		exit 1
	fi

	./autogen.sh || exit 1
	.travis/build.sh "$@" || exit 1
}

if [ "$TRAVIS_OS_NAME" = "linux" -a -z "$config" ]; then
	build_32_64() {
		build OBJC="$CC" $@

		case "$TRAVIS_CPU_ARCH" in
			amd64)
				build OBJC="$CC -m32" \
					--host=i686-pc-linux-gnu $@
				;;
			s390x)
				build OBJC="$CC -m31" \
					--host=s390-pc-linux-gnu $@
				;;
		esac
	}

	build_32_64
	build_32_64 --enable-seluid24
	build_32_64 --disable-compiler-tls

	# The following are not CPU-dependent, so only run them on amd64
	if [ "$TRAVIS_CPU_ARCH" = "amd64" ]; then
		build_32_64 --disable-threads
		build_32_64 --disable-threads --disable-sockets
		build_32_64 --disable-threads --disable-files
		build_32_64 --disable-threads --disable-sockets --disable-files
		build_32_64 --disable-sockets
		build_32_64 --disable-sockets --disable-files
		build_32_64 --disable-files
		build_32_64 --disable-shared
		build_32_64 --disable-shared --enable-seluid24
		build_32_64 --disable-compiler-tls --disable-threads
	fi
fi

if [ "$TRAVIS_OS_NAME" = "osx" -a -z "$config" ]; then
	build_mac_32_64() {
		build $@

		if [ -z "$no32bit" ]; then
			build OBJC="clang -m32" --host=i386-apple-darwin $@
		fi
	}

	if xcodebuild -version | grep 'Xcode 6' >/dev/null; then
		export CPPFLAGS="-D_Null_unspecified=__null_unspecified"
		export CPPFLAGS="$CPPFLAGS -D_Nullable=__nullable"
		export CPPFLAGS="$CPPFLAGS -D_Nonnull=__nonnull"
	fi

	build_mac_32_64
	build_mac_32_64 --disable-threads
	build_mac_32_64 --disable-threads --disable-sockets
	build_mac_32_64 --disable-threads --disable-files
	build_mac_32_64 --disable-threads --disable-sockets --disable-files
	build_mac_32_64 --disable-sockets
	build_mac_32_64 --disable-sockets --disable-files
	build_mac_32_64 --disable-files
	build_mac_32_64 --disable-shared

	if [ -z "$noruntime" ]; then
		build_mac_32_64 --enable-runtime
		build_mac_32_64 --enable-runtime --enable-seluid24
		build_mac_32_64 --enable-runtime --disable-threads
		build_mac_32_64 --enable-runtime --disable-threads \
				--disable-sockets
		build_mac_32_64 --enable-runtime --disable-threads \
				--disable-files
		build_mac_32_64 --enable-runtime --disable-threads \
				--disable-sockets --disable-files
		build_mac_32_64 --enable-runtime --disable-sockets
		build_mac_32_64 --enable-runtime --disable-sockets \
				--disable-files
		build_mac_32_64 --enable-runtime --disable-files
		build_mac_32_64 --enable-runtime --disable-shared
		build_mac_32_64 --enable-runtime --disable-shared \
				--enable-seluid24
	fi
fi

if [ "$config" = "ios" ]; then
	if xcodebuild -version | grep 'Xcode 6' >/dev/null; then
		export CPPFLAGS="-D_Null_unspecified=__null_unspecified"
		export CPPFLAGS="$CPPFLAGS -D_Nullable=__nullable"
		export CPPFLAGS="$CPPFLAGS -D_Nonnull=__nonnull"
	fi

	export IPHONEOS_DEPLOYMENT_TARGET="9.0"
	clang="clang -isysroot $(xcrun --sdk iphoneos --show-sdk-path)"
	export OBJC="$clang -arch armv7 -arch arm64"
	export OBJCPP="$clang -arch armv7 -E"
	build --host=arm-apple-darwin --enable-static

	sysroot="$(xcrun --sdk iphonesimulator --show-sdk-path)"
	clang="clang -isysroot $sysroot"
	export OBJC="$clang -arch i386 -arch x86_64"
	export OBJCPP="$clang -arch i386 -E"
	build WRAPPER=true --host=i386-apple-darwin --enable-static
fi

if [ "$config" = "amigaos" ]; then
	export PATH="/opt/amiga/bin:$PATH"

	build --host=m68k-amigaos
	build --host=m68k-amigaos --disable-amiga-lib
	build --host=m68k-amigaos --enable-static
fi

if [ "$config" = "nintendo_3ds" ]; then
	./autogen.sh
	docker run -e DEVKITPRO=/opt/devkitpro				\
		-e PATH="/opt/devkitpro/devkitARM/bin:$PATH"		\
		-v $TRAVIS_BUILD_DIR:/objfw devkitpro/devkitarm		\
		/objfw/.travis/build.sh --host=arm-none-eabi --with-3ds
fi

if [ "$config" = "nintendo_ds" ]; then
	./autogen.sh
	docker run -e DEVKITPRO=/opt/devkitpro				\
		-e PATH="/opt/devkitpro/devkitARM/bin:$PATH"		\
		-v $TRAVIS_BUILD_DIR:/objfw devkitpro/devkitarm		\
		/objfw/.travis/build.sh --host=arm-none-eabi --with-nds
fi

if [ "$config" = "wii" ]; then
	./autogen.sh
	docker run -e DEVKITPRO=/opt/devkitpro				\
		-e PATH="/opt/devkitpro/devkitPPC/bin:$PATH"		\
		-v $TRAVIS_BUILD_DIR:/objfw devkitpro/devkitppc		\
		/objfw/.travis/build.sh ac_cv_prog_wiiload=		\
		--host=powerpc-eabi --with-wii
fi
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































































































































































































































Modified Doxyfile from [51b1f8245c] to [f4ff92fbc3].

1
2
3
4
5

6
7
8

9


10
11
12
13
14
15
16
PROJECT_NAME = "ObjFW"
OUTPUT_DIRECTORY = docs/
INPUT = src src/exceptions src/runtime
FILE_PATTERNS = *.h *.m
HTML_OUTPUT = .

GENERATE_LATEX = NO
HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_MEMBERS = YES

PREDEFINED = __OBJC__				\


	     DOXYGEN				\
	     OF_BOXABLE				\
	     OF_CONSUMED			\
	     OF_DESIGNATED_INITIALIZER		\
	     OF_GENERIC(...)=			\
	     OF_HAVE_BLOCKS			\
	     OF_HAVE_FILES			\





>



>

>
>







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

Modified Makefile from [d58557a5a6] to [63e28abf9d].

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

include buildsys.mk

.PHONY: docs release

utils tests: src




docs:
	rm -fr docs
	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*,.travis*' | \
		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







>
>
>









|
<







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

include buildsys.mk

.PHONY: docs release

utils tests: src

check: tests
	cd tests && ${MAKE} -s run

docs:
	rm -fr docs
	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

Modified PLATFORMS.md from [d1e497fd99] to [aaef200c73].

63
64
65
66
67
68
69











70
71
72
73
74
75
76
-----

  * OS version: r1-alpha4
  * Architectures: x86
  * Compilers: Clang 3.2, GCC 4.6.3
  * Runtimes: ObjFW













iOS
---

  * Architectures: ARMv7, ARM64
  * Compilers: Clang
  * Runtimes: Apple







>
>
>
>
>
>
>
>
>
>
>







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

  * OS version: r1-alpha4
  * Architectures: x86
  * Compilers: Clang 3.2, GCC 4.6.3
  * Runtimes: ObjFW


HP-UX
-----

  * OS versions: 11i v1 (PA-RISC 2.0), 11i v3 (Itanium)
  * Architectures: Itanium, PA-RISC 2.0
  * Compilers: GCC 4.7.2, GCC 7.5.0
  * Runtimes: ObjFW
  * Notes: Exception handling on Itanium in 32 bit mode is broken, you need to
           use 64 bit mode by passing `OBJC="gcc -mlp64"` to `configure`.


iOS
---

  * Architectures: ARMv7, ARM64
  * Compilers: Clang
  * Runtimes: Apple
90
91
92
93
94
95
96










97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
-----

  * OS Versions: 10.5, 10.7-10.15, Darling
  * Architectures: PowerPC, PowerPC64, x86, x86_64
  * Compilers: Clang 3.1-10.0, Apple GCC 4.0.1 & 4.2.1
  * Runtimes: Apple, ObjFW












MorphOS
-------

  * OS Versions: 3.9-3.11
  * Architectures: PowerPC
  * Compilers: GCC 5.3.0, GCC 5.4.0
  * Runtimes: ObjFW
  * Notes: libnix and ixemul are both supported


NetBSD
------

  * OS Versions: 5.1-9.0
  * Architectures: ARM, ARM (big endian, BE8 mode), MIPS (O32), PowerPC, SPARC,







>
>
>
>
>
>
>
>
>
>




|

|

<







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

  * OS Versions: 10.5, 10.7-10.15, Darling
  * Architectures: PowerPC, PowerPC64, x86, x86_64
  * Compilers: Clang 3.1-10.0, Apple GCC 4.0.1 & 4.2.1
  * Runtimes: Apple, ObjFW


MiNT
----

  * OS Versions: FreeMiNT 1.19
  * Architectures: m68k
  * Runtimes: ObjFW
  * Compilers: GCC 4.6.4 (MiNT 20130415)
  * Limitations: No shared libraries, no threads


MorphOS
-------

  * OS Versions: 3.14
  * Architectures: PowerPC
  * Compilers: GCC 9.3.0
  * Runtimes: ObjFW



NetBSD
------

  * OS Versions: 5.1-9.0
  * Architectures: ARM, ARM (big endian, BE8 mode), MIPS (O32), PowerPC, SPARC,
181
182
183
184
185
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
  * Runtimes: ObjFW
  * Limitations: No threads


Windows
-------

  * OS Versions: 98 SE, NT 4.0, XP (x86), 7 (x64), 8 (x64), 8.1 (x64), 10,
                 Wine (x86 & x64)
  * Architectures: x86, x86_64
  * Compilers: GCC 5.3.0 & 6.2.0 from msys2 (x86 & x64),
               Clang 3.9.0 from msys2 (x86),
               Clang 10.0 from msys2 (x86 & x86_64)

  * Runtimes: ObjFW


Others
------

Basically, it should run on any POSIX system to which GCC >= 4.6 or a recent







|

|


|
>







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  * Runtimes: ObjFW
  * Limitations: No threads


Windows
-------

  * OS Versions: 98 SE, NT 4.0, XP (x86), 7 (x64), 8 (x64), 8.1 (x64), 10, 11,
                 Wine (x86 & x64)
  * Architectures: x86, x86_64, AArch64
  * Compilers: GCC 5.3.0 & 6.2.0 from msys2 (x86 & x64),
               Clang 3.9.0 from msys2 (x86),
               Clang 10.0 from msys2 (x86 & x86_64),
               Clang 14.0.4 from msys2 (AArch64)
  * Runtimes: ObjFW


Others
------

Basically, it should run on any POSIX system to which GCC >= 4.6 or a recent

Modified README.md from [f866fd61d7] to [939967b8be].

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
There are three ways you are probably reading this right now:

 * On [ObjFW](https://objfw.nil.im/)'s homepage, via Fossil
 * On [GitHub](https://github.com/ObjFW/ObjFW)
 * Via an editor or pager, by opening `README.md` from a checkout or tarball

ObjFW is developed using Fossil, so if you are reading this on GitHub or any
other place, you are most likely using a mirror.


<h1 id="table-of-contents">Table of Contents</h1>

 * [What is ObjFW?](#what)
 * [License](#license)
 * [Releases](#releases)
 * [Cloning the repository](#cloning)
 * [Installation](#installation)
   * [macOS and iOS](#macos-and-ios)
     * [Building as a framework](#building-framework)
     * [Using the macOS or iOS framework in Xcode](#framework-in-xcode)
     * [Broken Xcode versions](#broken-xcode-versions)
   * [Windows](#windows)
     * [Getting MSYS2](#getting-msys2)
     * [Updating MSYS2](#updating-msys2)
     * [Installing MinGW-w64 using MSYS2](#installing-mingw-w64)
     * [Getting, building and installing ObjFW](#steps-windows)
   * [Nintendo DS, Nintendo 3DS and Wii](#nintendo)
     * [Nintendo DS](#nintendo-ds)
     * [Nintendo 3DS](#nintendo-3ds)
     * [Wii](#wii)
   * [Amiga](#amiga)
 * [Writing your first application with ObjFW](#first-app)
 * [Documentation](#documentation)
 * [Bugs and feature requests](#bugs)
 * [Support and community](#support)


 * [Commercial use](#commercial-use)


<h1 id="what">What is ObjFW?</h1>

  ObjFW is a portable, lightweight framework for the Objective-C language. It
  enables you to write an application in Objective-C that will run on any


|

|


















|
<










>
>







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
There are three ways you are probably reading this right now:

 * On [ObjFW](https://objfw.nil.im/)'s homepage, via Fossil's web interface
 * On [GitHub](https://github.com/ObjFW/ObjFW)
 * Via an editor or pager, by opening `README.md` from a clone or tarball

ObjFW is developed using Fossil, so if you are reading this on GitHub or any
other place, you are most likely using a mirror.


<h1 id="table-of-contents">Table of Contents</h1>

 * [What is ObjFW?](#what)
 * [License](#license)
 * [Releases](#releases)
 * [Cloning the repository](#cloning)
 * [Installation](#installation)
   * [macOS and iOS](#macos-and-ios)
     * [Building as a framework](#building-framework)
     * [Using the macOS or iOS framework in Xcode](#framework-in-xcode)
     * [Broken Xcode versions](#broken-xcode-versions)
   * [Windows](#windows)
     * [Getting MSYS2](#getting-msys2)
     * [Setting up MSYS2](#setting-up-msys2)

     * [Getting, building and installing ObjFW](#steps-windows)
   * [Nintendo DS, Nintendo 3DS and Wii](#nintendo)
     * [Nintendo DS](#nintendo-ds)
     * [Nintendo 3DS](#nintendo-3ds)
     * [Wii](#wii)
   * [Amiga](#amiga)
 * [Writing your first application with ObjFW](#first-app)
 * [Documentation](#documentation)
 * [Bugs and feature requests](#bugs)
 * [Support and community](#support)
 * [Donating](#donating)
 * [Thanks](#thanks)
 * [Commercial use](#commercial-use)


<h1 id="what">What is ObjFW?</h1>

  ObjFW is a portable, lightweight framework for the Objective-C language. It
  enables you to write an application in Objective-C that will run on any
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
  The main advantage of cloning the Fossil repository over cloning the Git
  repository is that you also get all the tickets, wiki pages, etc.

<h2 id="cloning-fossil">Fossil</h2>

  Clone the Fossil repository like this:

    $ fossil clone https://objfw.nil.im objfw.fossil
    $ mkdir objfw && cd objfw
    $ fossil open ../objfw.fossil

  You can then use Fossil's web interface to browse the timeline, tickets,
  wiki pages, etc.:


    $ fossil ui

  It's also possible to open the same local repository multiple times, so that
  you have multiple working directories all backed by the same local
  repository.

  In order to verify the signature of the currently checked out checkin, you
  can use:

    $ fossil artifact current | gpg --verify







<h2 id="cloning-git">Git</h2>

  To clone the Git repository, use the following:

    $ git clone https://github.com/ObjFW/ObjFW

  Git commits are not signed, so if you want to check the signature of an
  individual commit, branch head or tag, please use Fossil.

<h1 id="installation">Installation</h1>

  To install ObjFW, just run the following commands:

    $ ./configure
    $ make
    $ make install


  In case you checked out ObjFW from the Fossil or Git repository, you need to
  run the following command first:

    $ ./autogen.sh

<h2 id="macos-and-ios">macOS and iOS</h2>

<h3 id="building-framework">Building as a framework</h3>

  When building for macOS or iOS, everything is built as a `.framework` by
  default if `--disable-shared` has not been specified to `configure`.






  To build for iOS, use something like this:

    $ clang="clang -isysroot $(xcrun --sdk iphoneos --show-sdk-path)"
    $ export OBJC="$clang -arch armv7 -arch arm64"
    $ export OBJCPP="$clang -arch armv7 -E"
    $ export IPHONEOS_DEPLOYMENT_TARGET="9.0"
    $ ./configure --prefix=/usr/local/ios --host=arm-apple-darwin

  To build for the iOS simulator, use something like this:


    $ clang="clang -isysroot $(xcrun --sdk iphonesimulator --show-sdk-path)"
    $ export OBJC="$clang -arch i386 -arch x86_64"
    $ export OBJCPP="$clang -arch i386 -E"
    $ export IPHONEOS_DEPLOYMENT_TARGET="9.0"
    $ ./configure --prefix=/usr/local/iossim --host=i386-apple-darwin

<h3 id="framework-in-xcode">Using the macOS or iOS framework in Xcode</h3>

  To use the macOS framework in Xcode, you need to add the `.framework`s to
  your project and add the following flags to `Other C Flags`:

    -fconstant-string-class=OFConstantString -fno-constant-cfstrings







|
<
<




>










>
>
>
>
>
>
















|
>











|
>

>
>
>
>
|





|

|
>


|
|

|







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
  The main advantage of cloning the Fossil repository over cloning the Git
  repository is that you also get all the tickets, wiki pages, etc.

<h2 id="cloning-fossil">Fossil</h2>

  Clone the Fossil repository like this:

    $ fossil clone https://objfw.nil.im



  You can then use Fossil's web interface to browse the timeline, tickets,
  wiki pages, etc.:

    $ cd objfw
    $ fossil ui

  It's also possible to open the same local repository multiple times, so that
  you have multiple working directories all backed by the same local
  repository.

  In order to verify the signature of the currently checked out checkin, you
  can use:

    $ fossil artifact current | gpg --verify

  Please note that not all checkins are signed, as the signing key only resides
  on trusted systems. This means that checkins I perform on e.g. Windows are
  unsigned. However, usually it should not take long until there is another
  signed checkin. Alternatively, you can go back until the last signed checkin
  and review changes from there on.

<h2 id="cloning-git">Git</h2>

  To clone the Git repository, use the following:

    $ git clone https://github.com/ObjFW/ObjFW

  Git commits are not signed, so if you want to check the signature of an
  individual commit, branch head or tag, please use Fossil.

<h1 id="installation">Installation</h1>

  To install ObjFW, just run the following commands:

    $ ./configure
    $ make
    $ make check
    $ sudo make install

  In case you checked out ObjFW from the Fossil or Git repository, you need to
  run the following command first:

    $ ./autogen.sh

<h2 id="macos-and-ios">macOS and iOS</h2>

<h3 id="building-framework">Building as a framework</h3>

  When building for macOS or iOS, everything is built as a `.framework` by
  default if `--disable-shared` has not been specified to `./configure`. The
  frameworks will end up in `$PREFIX/Library/Frameworks`.

  To build for macOS, just follow the
  <a href="#installation">regular instructions</a> above.

  To build for iOS, follow the regular instructions, but instead of
  `./configure` do something like this:

    $ clang="clang -isysroot $(xcrun --sdk iphoneos --show-sdk-path)"
    $ export OBJC="$clang -arch armv7 -arch arm64"
    $ export OBJCPP="$clang -arch armv7 -E"
    $ export IPHONEOS_DEPLOYMENT_TARGET="9.0"
    $ ./configure --prefix=/usr/local/ios --host=arm64-apple-darwin

  To build for the iOS simulator, follow the regular instructions, but instead
  of `./configure` use something like this:

    $ clang="clang -isysroot $(xcrun --sdk iphonesimulator --show-sdk-path)"
    $ export OBJC="$clang -arch arm64 -arch x86_64"
    $ export OBJCPP="$clang -arch arm64 -E"
    $ export IPHONEOS_DEPLOYMENT_TARGET="9.0"
    $ ./configure --prefix=/usr/local/iossim --host=arm64-apple-darwin

<h3 id="framework-in-xcode">Using the macOS or iOS framework in Xcode</h3>

  To use the macOS framework in Xcode, you need to add the `.framework`s to
  your project and add the following flags to `Other C Flags`:

    -fconstant-string-class=OFConstantString -fno-constant-cfstrings
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
  there are many MinGW versions that behave slightly differently and often
  cause problems.

<h3 id="getting-msys2">Getting MSYS2</h3>

  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 and there is no way to verify their integrity, so only download this
  from a trusted connection. Everything else you will download using MSYS2
  later will be cryptographically signed.

<h3 id="updating-msys2">Updating MSYS2</h3>

  The first thing to do is updating MSYS2. It is important to update things in
  a certain order, as `pacman` (the package manager MSYS2 uses, which comes
  from Arch Linux) does not know about a few things that are special on
  Windows.

  First, update the mirror list:

    $ pacman -Sy pacman-mirrors


  Then proceed to update the `msys2-runtime` itself, `bash` and `pacman`:


    $ pacman -S msys2-runtime bash pacman mintty

  Now close the current window and restart MSYS2, as the current window is now
  defunct. In a new MSYS2 window, update the rest of MSYS2:


    $ pacman -Su

  Now you have a fully updated MSYS2. Whenever you want to update MSYS2,
  proceed in this order. Notice that the first `pacman` invocation includes
  `-y` to actually fetch a new list of packages.

<h3 id="installing-mingw-w64">Installing MinGW-w64 using MSYS2</h3>

  Now it's time to install MinGW-w64. If you want to build 32 bit binaries:


    $ pacman -S mingw-w64-i686-clang mingw-w64-i686-gcc-objc

  For 64 bit binaries:


    $ pacman -S mingw-w64-x86_64-clang mingw-w64-x86_64-gcc-objc

  There is nothing wrong with installing them both, as MSYS2 has created two
  entries in your start menu: `MinGW-w64 Win32 Shell` and
  `MinGW-w64 Win64 Shell`. So if you want to build for 32 or 64 bit, you just
  start the correct shell.

  Finally, install a few more things needed to build ObjFW:

    $ pacman -S autoconf automake fossil make

<h3 id="steps-windows">Getting, building and installing ObjFW</h3>

  Start the MinGW-w64 Win32 or Win64 Shell (depening on what version you want
  to build - do *not* use the MSYS2 Shell shortcut, but use the MinGW-w64 Win32
  or Win64 Shell shortcut instead!) and check out ObjFW:

    $ fossil clone https://objfw.nil.im objfw.fossil
    $ mkdir objfw && cd objfw
    $ fossil open ../objfw.fossil

  You can also download a release tarball if you want. Now go to the newly
  checked out repository and build and install it:

    $ ./autogen.sh && ./configure && make -j16 install

  If everything was successfully, you can now build projects using ObjFW for
  Windows using the normal `objfw-compile` and friends.

<h2 id="nintendo">Nintendo DS, Nintendo 3DS and Wii</h2>

  Download and install [devkitPro](https://devkitpro.org/wiki/Getting_Started).

<h3 id="nintendo-ds">Nintendo DS</h3>







|
<
|

|

<
|
<
<
|
<
|
<
>

<
>

|

<
<
>

|

|
<
<

|

<
>

|

<
|
>
|

|
|
<
|

|

|



|
<
|

|
<
<

|




|







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
  there are many MinGW versions that behave slightly differently and often
  cause problems.

<h3 id="getting-msys2">Getting MSYS2</h3>

  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.

  Finally, install a few more things that are common between all environments:

    $ pacman -S autoconf automake make

<h3 id="steps-windows">Getting, building and installing ObjFW</h3>

  Start the MSYS2 using the shortcut for the environment you want to use and

  check out ObjFW:

    $ fossil clone https://objfw.nil.im



  You can also download a release tarball if you want. Now `cd` to the newly
  checked out repository and build and install it:

    $ ./autogen.sh && ./configure && make -j16 install

  If everything was successful, you can now build projects using ObjFW for
  Windows using the normal `objfw-compile` and friends.

<h2 id="nintendo">Nintendo DS, Nintendo 3DS and Wii</h2>

  Download and install [devkitPro](https://devkitpro.org/wiki/Getting_Started).

<h3 id="nintendo-ds">Nintendo DS</h3>
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
    $ 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":

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

  You can compile your new app using `objfw-compile`:

    $ objfw-compile -o MyFirstApp MyFirstApp.m

  `objfw-compile` is a tool that allows building applications and libraries
  using ObjFW without needing a full-blown build system. If you want to use
  your own build system, you can get the necessary flags from `objfw-config`.


<h1 id="documentation">Documentation</h1>

  You can find the documentation for released versions of ObjFW
  [here](https://objfw.nil.im/docs/).

  In order to build the documentation yourself (necessary to have documentation
  for trunk / master), you need to have [Doxygen](https://www.doxygen.nl)
  installed. Once installed, you can build the documentation from the root
  directory of the repository:

    $ doxygen >/dev/null


<h1 id="bugs">Bugs and feature requests</h1>

  If you find any bugs or have feature requests, please
  [file a new bug](https://objfw.nil.im/tktnew) in the
  [bug tracker](https://objfw.nil.im/reportlist).

  Alternatively, feel free to send a mail to js@nil.im!


<h1 id="support">Support and community</h1>

  If you have any questions about ObjFW or would like to talk to other ObjFW
  users, the following venues are available:

   * The [forum](https://objfw.nil.im/forum)
   * A [Matrix](https://matrix.to/#/%23objfw:nil.im) room
   * An [IRC channel](irc://chat.freenode.net/#objfw) on Freenode (`#objfw`,
     [web chat](https://webchat.freenode.net/?channels=objfw)), bridged to the
     Matrix room above








  Please don't hesitate to join any or all of those!

















<h1 id="commercial-use">Commercial use</h1>

  If for whatever reason neither the terms of the QPL nor those of the GPL work
  for you, a proprietary license for ObjFW including support is available upon
  request. Just write a mail to js@nil.im and we can find a reasonable solution
  for both parties.







|




















|

















|
|
|

>
>
>
>
>
>
>



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







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
    $ 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!"];

  You can compile your new app using `objfw-compile`:

    $ objfw-compile -o MyFirstApp MyFirstApp.m

  `objfw-compile` is a tool that allows building applications and libraries
  using ObjFW without needing a full-blown build system. If you want to use
  your own build system, you can get the necessary flags from `objfw-config`.


<h1 id="documentation">Documentation</h1>

  You can find the documentation for released versions of ObjFW
  [here](https://objfw.nil.im/docs/).

  In order to build the documentation yourself (necessary to have documentation
  for trunk / master), you need to have [Doxygen](https://www.doxygen.nl)
  installed. Once installed, you can build the documentation from the root
  directory of the repository:

    $ make docs


<h1 id="bugs">Bugs and feature requests</h1>

  If you find any bugs or have feature requests, please
  [file a new bug](https://objfw.nil.im/tktnew) in the
  [bug tracker](https://objfw.nil.im/reportlist).

  Alternatively, feel free to send a mail to js@nil.im!


<h1 id="support">Support and community</h1>

  If you have any questions about ObjFW or would like to talk to other ObjFW
  users, the following venues are available:

   * The [forum](https://objfw.nil.im/forum)
   * A [Matrix room](https://matrix.to/#/%23objfw:nil.im)
   * An IRC channel named `#objfw` on `irc.oftc.net`
     ([Web chat](https://webchat.oftc.net/?channels=%23objfw)), bridged to the
     Matrix room above
   * A [Slack channel](https://objfw.nil.im/slack), bridged to the Matrix room
     above
   * A [Discord channel](https://objfw.nil.im/discord), bridged to the Matrix
     room above
   * A [Telegram room](https://t.me/objfw), bridged to the Matrix room above
   * A [Gitter room](https://gitter.im/ObjFW/ObjFW), bridged to the Matrix room
     above

  Please don't hesitate to join any or all of those!


<h1 id="donating">Donating</h1>

  If you want to donate to ObjFW, you can read about possible ways to do so
  [here](https://objfw.nil.im/wiki?name=Donating).


<h1 id="thanks">Thanks</h1>

  * Thank you to [Jonathan Neuschäfer](https://github.com/neuschaefer) for
    reviewing the *entirety* (all 84k LoC at the time) of ObjFW's codebase in
    2017!
  * Thank you to [Hill Ma](https://github.com/mahiuchun) for donating an M1 Mac
    Mini to the project!


<h1 id="commercial-use">Commercial use</h1>

  If for whatever reason neither the terms of the QPL nor those of the GPL work
  for you, a proprietary license for ObjFW including support is available upon
  request. Just write a mail to js@nil.im and we can find a reasonable solution
  for both parties.

Modified build-aux/config.guess from [3b75d9d5f8] to [a7bb5831a2].

1
2
3
4
5
6
7
8
9
10
11
12
#! /bin/sh
# Attempt to guess a canonical system name.
#   Copyright 1992-2018 Free Software Foundation, Inc.

timestamp='2018-03-08'

# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but


|

|







1
2
3
4
5
6
7
8
9
10
11
12
#! /bin/sh
# Attempt to guess a canonical system name.
#   Copyright 1992-2019 Free Software Foundation, Inc.

timestamp='2019-01-03'

# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

Report bugs and patches to <config-patches@gnu.org>."

version="\
GNU config.guess ($timestamp)

Originally written by Per Bothner.
Copyright 1992-2018 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="
Try \`$me --help' for more information."








|







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

Report bugs and patches to <config-patches@gnu.org>."

version="\
GNU config.guess ($timestamp)

Originally written by Per Bothner.
Copyright 1992-2019 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="
Try \`$me --help' for more information."

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
done

if test $# != 0; then
  echo "$me: too many arguments$help" >&2
  exit 1
fi

trap 'exit 1' 1 2 15

# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
# compiler to aid in system detection is discouraged as it requires
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.

# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated.

# Portable tmp directory creation inspired by the Autoconf team.





set_cc_for_build='
trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
: ${TMPDIR=/tmp} ;

 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
 { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
 { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
 { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
dummy=$tmp/dummy ;
tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
case $CC_FOR_BUILD,$HOST_CC,$CC in
 ,,)    echo "int x;" > "$dummy.c" ;
	for c in cc gcc c89 c99 ; do
	  if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
	     CC_FOR_BUILD="$c"; break ;

	  fi ;
	done ;
	if test x"$CC_FOR_BUILD" = x ; then
	  CC_FOR_BUILD=no_compiler_found ;
	fi
	;;
 ,,*)   CC_FOR_BUILD=$CC ;;
 ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
esac ; set_cc_for_build= ;'


# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
# (ghazi@noc.rutgers.edu 1994-08-24)
if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
	PATH=$PATH:/.attbin ; export PATH
fi

UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown

case "$UNAME_SYSTEM" in
Linux|GNU|GNU/*)
	# If the system lacks a compiler, then just pick glibc.
	# We could probably try harder.
	LIBC=gnu

	eval "$set_cc_for_build"
	cat <<-EOF > "$dummy.c"
	#include <features.h>
	#if defined(__UCLIBC__)
	LIBC=uclibc
	#elif defined(__dietlibc__)
	LIBC=dietlibc
	#else







<
<










>
>
>
>
|
<
<
|
>
|
|
|
|
|
<
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
>


|














|







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
done

if test $# != 0; then
  echo "$me: too many arguments$help" >&2
  exit 1
fi



# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
# compiler to aid in system detection is discouraged as it requires
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.

# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated.

# Portable tmp directory creation inspired by the Autoconf team.

tmp=
# shellcheck disable=SC2172
trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15

set_cc_for_build() {


    : "${TMPDIR=/tmp}"
    # shellcheck disable=SC2039
    { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
	{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
	{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
	{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
    dummy=$tmp/dummy

    case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
	,,)    echo "int x;" > "$dummy.c"
	       for driver in cc gcc c89 c99 ; do
		   if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
		       CC_FOR_BUILD="$driver"
		       break
		   fi
	       done
	       if test x"$CC_FOR_BUILD" = x ; then
		   CC_FOR_BUILD=no_compiler_found
	       fi
	       ;;
	,,*)   CC_FOR_BUILD=$CC ;;
	,*,*)  CC_FOR_BUILD=$HOST_CC ;;
    esac
}

# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
# (ghazi@noc.rutgers.edu 1994-08-24)
if test -f /.attbin/uname ; then
	PATH=$PATH:/.attbin ; export PATH
fi

UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown

case "$UNAME_SYSTEM" in
Linux|GNU|GNU/*)
	# If the system lacks a compiler, then just pick glibc.
	# We could probably try harder.
	LIBC=gnu

	set_cc_for_build
	cat <<-EOF > "$dummy.c"
	#include <features.h>
	#if defined(__UCLIBC__)
	LIBC=uclibc
	#elif defined(__dietlibc__)
	LIBC=dietlibc
	#else
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
	# portion of the name.  We always set it to "unknown".
	sysctl="sysctl -n hw.machine_arch"
	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
	    "/sbin/$sysctl" 2>/dev/null || \
	    "/usr/sbin/$sysctl" 2>/dev/null || \
	    echo unknown)`
	case "$UNAME_MACHINE_ARCH" in

	    armeb) machine=armeb-unknown ;;
	    arm*) machine=arm-unknown ;;
	    sh3el) machine=shl-unknown ;;
	    sh3eb) machine=sh-unknown ;;
	    sh5el) machine=sh5le-unknown ;;
	    earmv*)
		arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`


		endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
		machine="${arch}${endian}"-unknown
		;;
	    *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
	esac
	# The Operating System including object format, if it has switched
	# to ELF recently (or will in the future) and ABI.
	case "$UNAME_MACHINE_ARCH" in
	    earm*)
		os=netbsdelf
		;;
	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
		eval "$set_cc_for_build"
		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
			| grep -q __ELF__
		then
		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
		    # Return netbsd for either.  FIX?
		    os=netbsd
		else
		    os=netbsdelf
		fi
		;;
	    *)
		os=netbsd
		;;
	esac
	# Determine ABI tags.
	case "$UNAME_MACHINE_ARCH" in
	    earm*)
		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
		abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
		;;
	esac
	# The OS release
	# Debian GNU/NetBSD machines have a different userland, and
	# thus, need a distinct triplet. However, they do not need
	# kernel version information, so it can be replaced with a
	# suitable tag, in the style of linux-gnu.
	case "$UNAME_VERSION" in
	    Debian*)
		release='-gnu'
		;;
	    *)
		release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
		;;
	esac
	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
	# contains redundant information, the shorter form:
	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
	echo "$machine-${os}${release}${abi}"
	exit ;;
    *:Bitrig:*:*)
	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
	echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
	exit ;;
    *:OpenBSD:*:*)
	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`







>





|
|
>
>












|

















|



















|







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
	# portion of the name.  We always set it to "unknown".
	sysctl="sysctl -n hw.machine_arch"
	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
	    "/sbin/$sysctl" 2>/dev/null || \
	    "/usr/sbin/$sysctl" 2>/dev/null || \
	    echo unknown)`
	case "$UNAME_MACHINE_ARCH" in
	    aarch64eb) machine=aarch64_be-unknown ;;
	    armeb) machine=armeb-unknown ;;
	    arm*) machine=arm-unknown ;;
	    sh3el) machine=shl-unknown ;;
	    sh3eb) machine=sh-unknown ;;
	    sh5el) machine=sh5le-unknown ;;
	    earm*)
		arch="${UNAME_MACHINE_ARCH#e}"
		arch="${arch%eb}"
		arch="${arch%hf}"
		endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
		machine="${arch}${endian}"-unknown
		;;
	    *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
	esac
	# The Operating System including object format, if it has switched
	# to ELF recently (or will in the future) and ABI.
	case "$UNAME_MACHINE_ARCH" in
	    earm*)
		os=netbsdelf
		;;
	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
		set_cc_for_build
		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
			| grep -q __ELF__
		then
		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
		    # Return netbsd for either.  FIX?
		    os=netbsd
		else
		    os=netbsdelf
		fi
		;;
	    *)
		os=netbsd
		;;
	esac
	# Determine ABI tags.
	case "$UNAME_MACHINE_ARCH" in
	    earm*)
		expr='s/v[0-9]//;s/earm/-eabi/;s/eb$//'
		abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
		;;
	esac
	# The OS release
	# Debian GNU/NetBSD machines have a different userland, and
	# thus, need a distinct triplet. However, they do not need
	# kernel version information, so it can be replaced with a
	# suitable tag, in the style of linux-gnu.
	case "$UNAME_VERSION" in
	    Debian*)
		release='-gnu'
		;;
	    *)
		release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
		;;
	esac
	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
	# contains redundant information, the shorter form:
	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
	echo "$machine-${os}${release}${abi-}"
	exit ;;
    *:Bitrig:*:*)
	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
	echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
	exit ;;
    *:OpenBSD:*:*)
	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
379
380
381
382
383
384
385













386
387
388
389
390
391
392
393
394
395
396
397
398
399
    s390x:SunOS:*:*)
	echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
	exit ;;
    sun4H:SunOS:5.*:*)
	echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
	exit ;;
    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)













	echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
	exit ;;
    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
	echo i386-pc-auroraux"$UNAME_RELEASE"
	exit ;;
    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
	eval "$set_cc_for_build"
	SUN_ARCH=i386
	# If there is a compiler, see if it is configured for 64-bit objects.
	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
	# This test works for both compilers.
	if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
		(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \







>
>
>
>
>
>
>
>
>
>
>
>
>
|





|







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
    s390x:SunOS:*:*)
	echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
	exit ;;
    sun4H:SunOS:5.*:*)
	echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
	exit ;;
    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
	set_cc_for_build
	SUN_ARCH=sparc
	# If there is a compiler, see if it is configured for 64-bit objects.
	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
	# This test works for both compilers.
	if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
	    if (echo '#ifdef __sparcv9'; echo IS_64BIT_ARCH; echo '#endif') | \
		(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
		grep IS_64BIT_ARCH >/dev/null
	    then
		SUN_ARCH=sparcv9
	    fi
	fi
	echo "$SUN_ARCH"-sun-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
	exit ;;
    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
	echo i386-pc-auroraux"$UNAME_RELEASE"
	exit ;;
    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
	set_cc_for_build
	SUN_ARCH=i386
	# If there is a compiler, see if it is configured for 64-bit objects.
	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
	# This test works for both compilers.
	if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
		(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
    VAX*:ULTRIX*:*:*)
	echo vax-dec-ultrix"$UNAME_RELEASE"
	exit ;;
    2020:CLIX:*:* | 2430:CLIX:*:*)
	echo clipper-intergraph-clix"$UNAME_RELEASE"
	exit ;;
    mips:*:*:UMIPS | mips:*:*:RISCos)
	eval "$set_cc_for_build"
	sed 's/^	//' << EOF > "$dummy.c"
#ifdef __cplusplus
#include <stdio.h>  /* for printf() prototype */
	int main (int argc, char *argv[]) {
#else
	int main (argc, argv) int argc; char *argv[]; {
#endif







|







496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
    VAX*:ULTRIX*:*:*)
	echo vax-dec-ultrix"$UNAME_RELEASE"
	exit ;;
    2020:CLIX:*:* | 2430:CLIX:*:*)
	echo clipper-intergraph-clix"$UNAME_RELEASE"
	exit ;;
    mips:*:*:UMIPS | mips:*:*:RISCos)
	set_cc_for_build
	sed 's/^	//' << EOF > "$dummy.c"
#ifdef __cplusplus
#include <stdio.h>  /* for printf() prototype */
	int main (int argc, char *argv[]) {
#else
	int main (argc, argv) int argc; char *argv[]; {
#endif
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
	else
		IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
	fi
	echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
	exit ;;
    *:AIX:2:3)
	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
		eval "$set_cc_for_build"
		sed 's/^		//' << EOF > "$dummy.c"
		#include <sys/systemcfg.h>

		main()
			{
			if (!__power_pc())
				exit(1);







|







593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
	else
		IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
	fi
	echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
	exit ;;
    *:AIX:2:3)
	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
		set_cc_for_build
		sed 's/^		//' << EOF > "$dummy.c"
		#include <sys/systemcfg.h>

		main()
			{
			if (!__power_pc())
				exit(1);
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
			  32) HP_ARCH=hppa2.0n ;;
			  64) HP_ARCH=hppa2.0w ;;
			  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
			esac ;;
		    esac
		fi
		if [ "$HP_ARCH" = "" ]; then
		    eval "$set_cc_for_build"
		    sed 's/^		//' << EOF > "$dummy.c"

		#define _HPUX_SOURCE
		#include <stdlib.h>
		#include <unistd.h>

		int main ()







|







674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
			  32) HP_ARCH=hppa2.0n ;;
			  64) HP_ARCH=hppa2.0w ;;
			  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
			esac ;;
		    esac
		fi
		if [ "$HP_ARCH" = "" ]; then
		    set_cc_for_build
		    sed 's/^		//' << EOF > "$dummy.c"

		#define _HPUX_SOURCE
		#include <stdlib.h>
		#include <unistd.h>

		int main ()
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
EOF
		    (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
		    test -z "$HP_ARCH" && HP_ARCH=hppa
		fi ;;
	esac
	if [ "$HP_ARCH" = hppa2.0w ]
	then
	    eval "$set_cc_for_build"

	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
	    # generating 64-bit code.  GNU and HP use different nomenclature:
	    #
	    # $ CC_FOR_BUILD=cc ./config.guess
	    # => hppa2.0w-hp-hpux11.23







|







714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
EOF
		    (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
		    test -z "$HP_ARCH" && HP_ARCH=hppa
		fi ;;
	esac
	if [ "$HP_ARCH" = hppa2.0w ]
	then
	    set_cc_for_build

	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
	    # generating 64-bit code.  GNU and HP use different nomenclature:
	    #
	    # $ CC_FOR_BUILD=cc ./config.guess
	    # => hppa2.0w-hp-hpux11.23
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
	echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
	exit ;;
    ia64:HP-UX:*:*)
	HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
	echo ia64-hp-hpux"$HPUX_REV"
	exit ;;
    3050*:HI-UX:*:*)
	eval "$set_cc_for_build"
	sed 's/^	//' << EOF > "$dummy.c"
	#include <unistd.h>
	int
	main ()
	{
	  long cpu = sysconf (_SC_CPU_VERSION);
	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns







|







740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
	echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
	exit ;;
    ia64:HP-UX:*:*)
	HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
	echo ia64-hp-hpux"$HPUX_REV"
	exit ;;
    3050*:HI-UX:*:*)
	set_cc_for_build
	sed 's/^	//' << EOF > "$dummy.c"
	#include <unistd.h>
	int
	main ()
	{
	  long cpu = sysconf (_SC_CPU_VERSION);
	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
835
836
837
838
839
840
841











842
843
844
845
846
847
848
	echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
	exit ;;
    sparc*:BSD/OS:*:*)
	echo sparc-unknown-bsdi"$UNAME_RELEASE"
	exit ;;
    *:BSD/OS:*:*)
	echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"











	exit ;;
    *:FreeBSD:*:*)
	UNAME_PROCESSOR=`/usr/bin/uname -p`
	case "$UNAME_PROCESSOR" in
	    amd64)
		UNAME_PROCESSOR=x86_64 ;;
	    i386)







>
>
>
>
>
>
>
>
>
>
>







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
	echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
	exit ;;
    sparc*:BSD/OS:*:*)
	echo sparc-unknown-bsdi"$UNAME_RELEASE"
	exit ;;
    *:BSD/OS:*:*)
	echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
	exit ;;
    arm:FreeBSD:*:*)
	UNAME_PROCESSOR=`uname -p`
	set_cc_for_build
	if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
	    | grep -q __ARM_PCS_VFP
	then
	    echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi
	else
	    echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf
	fi
	exit ;;
    *:FreeBSD:*:*)
	UNAME_PROCESSOR=`/usr/bin/uname -p`
	case "$UNAME_PROCESSOR" in
	    amd64)
		UNAME_PROCESSOR=x86_64 ;;
	    i386)
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
		echo ia64-unknown-interix"$UNAME_RELEASE"
		exit ;;
	esac ;;
    i*:UWIN*:*)
	echo "$UNAME_MACHINE"-pc-uwin
	exit ;;
    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
	echo x86_64-unknown-cygwin
	exit ;;
    prep*:SunOS:5.*:*)
	echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
	exit ;;
    *:GNU:*:*)
	# the GNU system
	echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
	exit ;;
    *:GNU/*:*:*)
	# other systems with GNU libc and userland
	echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
	exit ;;
    i*86:Minix:*:*)
	echo "$UNAME_MACHINE"-pc-minix
	exit ;;
    aarch64:Linux:*:*)
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    aarch64_be:Linux:*:*)
	UNAME_MACHINE=aarch64_be
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"







|












|
|







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
		echo ia64-unknown-interix"$UNAME_RELEASE"
		exit ;;
	esac ;;
    i*:UWIN*:*)
	echo "$UNAME_MACHINE"-pc-uwin
	exit ;;
    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
	echo x86_64-pc-cygwin
	exit ;;
    prep*:SunOS:5.*:*)
	echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
	exit ;;
    *:GNU:*:*)
	# the GNU system
	echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
	exit ;;
    *:GNU/*:*:*)
	# other systems with GNU libc and userland
	echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
	exit ;;
    *:Minix:*:*)
	echo "$UNAME_MACHINE"-unknown-minix
	exit ;;
    aarch64:Linux:*:*)
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    aarch64_be:Linux:*:*)
	UNAME_MACHINE=aarch64_be
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
	if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    arc:Linux:*:* | arceb:Linux:*:*)
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    arm*:Linux:*:*)
	eval "$set_cc_for_build"
	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
	    | grep -q __ARM_EABI__
	then
	    echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	else
	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
		| grep -q __ARM_PCS_VFP







|







947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
	if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    arc:Linux:*:* | arceb:Linux:*:*)
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    arm*:Linux:*:*)
	set_cc_for_build
	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
	    | grep -q __ARM_EABI__
	then
	    echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	else
	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
		| grep -q __ARM_PCS_VFP
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
    m32r*:Linux:*:*)
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    m68*:Linux:*:*)
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    mips:Linux:*:* | mips64:Linux:*:*)
	eval "$set_cc_for_build"
	sed 's/^	//' << EOF > "$dummy.c"
	#undef CPU
	#undef ${UNAME_MACHINE}
	#undef ${UNAME_MACHINE}el
	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
	CPU=${UNAME_MACHINE}el
	#else







|







996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
    m32r*:Linux:*:*)
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    m68*:Linux:*:*)
	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
	exit ;;
    mips:Linux:*:* | mips64:Linux:*:*)
	set_cc_for_build
	sed 's/^	//' << EOF > "$dummy.c"
	#undef CPU
	#undef ${UNAME_MACHINE}
	#undef ${UNAME_MACHINE}el
	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
	CPU=${UNAME_MACHINE}el
	#else
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
	echo powerpc-apple-rhapsody"$UNAME_RELEASE"
	exit ;;
    *:Rhapsody:*:*)
	echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
	exit ;;
    *:Darwin:*:*)
	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
	eval "$set_cc_for_build"
	if test "$UNAME_PROCESSOR" = unknown ; then
	    UNAME_PROCESSOR=powerpc
	fi
	if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then
	    if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
		       (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \







|







1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
	echo powerpc-apple-rhapsody"$UNAME_RELEASE"
	exit ;;
    *:Rhapsody:*:*)
	echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
	exit ;;
    *:Darwin:*:*)
	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
	set_cc_for_build
	if test "$UNAME_PROCESSOR" = unknown ; then
	    UNAME_PROCESSOR=powerpc
	fi
	if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then
	    if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
		       (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
1354
1355
1356
1357
1358
1359
1360

1361
1362
1363
1364
1365
1366
1367
    DS/*:UNIX_System_V:*:*)
	echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
	exit ;;
    *:Plan9:*:*)
	# "uname -m" is not consistent, so use $cputype instead. 386
	# is converted to i386 for consistency with other x86
	# operating systems.

	if test "$cputype" = 386; then
	    UNAME_MACHINE=i386
	else
	    UNAME_MACHINE="$cputype"
	fi
	echo "$UNAME_MACHINE"-unknown-plan9
	exit ;;







>







1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
    DS/*:UNIX_System_V:*:*)
	echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
	exit ;;
    *:Plan9:*:*)
	# "uname -m" is not consistent, so use $cputype instead. 386
	# is converted to i386 for consistency with other x86
	# operating systems.
	# shellcheck disable=SC2154
	if test "$cputype" = 386; then
	    UNAME_MACHINE=i386
	else
	    UNAME_MACHINE="$cputype"
	fi
	echo "$UNAME_MACHINE"-unknown-plan9
	exit ;;
1410
1411
1412
1413
1414
1415
1416



1417
1418
1419
1420
1421
1422
1423
	exit ;;
    x86_64:VMkernel:*:*)
	echo "$UNAME_MACHINE"-unknown-esx
	exit ;;
    amd64:Isilon\ OneFS:*:*)
	echo x86_64-unknown-onefs
	exit ;;



esac

echo "$0: unable to guess system type" >&2

case "$UNAME_MACHINE:$UNAME_SYSTEM" in
    mips:Linux | mips64:Linux)
	# If we got here on MIPS GNU/Linux, output extra information.







>
>
>







1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
	exit ;;
    x86_64:VMkernel:*:*)
	echo "$UNAME_MACHINE"-unknown-esx
	exit ;;
    amd64:Isilon\ OneFS:*:*)
	echo x86_64-unknown-onefs
	exit ;;
    *:Unleashed:*:*)
	echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE"
	exit ;;
esac

echo "$0: unable to guess system type" >&2

case "$UNAME_MACHINE:$UNAME_SYSTEM" in
    mips:Linux | mips64:Linux)
	# If we got here on MIPS GNU/Linux, output extra information.

Modified build-aux/config.sub from [0f298c8265] to [f6ee861ee1].

1
2
3
4
5
6
7
8
9
10
11
12
#! /bin/sh
# Configuration validation subroutine script.
#   Copyright 1992-2018 Free Software Foundation, Inc.

timestamp='2018-03-08'

# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but


|

|







1
2
3
4
5
6
7
8
9
10
11
12
#! /bin/sh
# Configuration validation subroutine script.
#   Copyright 1992-2019 Free Software Foundation, Inc.

timestamp='2019-01-05'

# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  -v, --version      print version number, then exit

Report bugs and patches to <config-patches@gnu.org>."

version="\
GNU config.sub ($timestamp)

Copyright 1992-2018 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="
Try \`$me --help' for more information."








|







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  -v, --version      print version number, then exit

Report bugs and patches to <config-patches@gnu.org>."

version="\
GNU config.sub ($timestamp)

Copyright 1992-2019 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="
Try \`$me --help' for more information."

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
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
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530


1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
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
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716



1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
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
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
    --help | --h* | -h )
       echo "$usage"; exit ;;
    -- )     # Stop option processing
       shift; break ;;
    - )	# Use stdin as input.
       break ;;
    -* )
       echo "$me: invalid option $1$help"
       exit 1 ;;

    *local*)
       # First pass through any local machine types.
       echo "$1"
       exit ;;

    * )
       break ;;
  esac
done

case $# in
 0) echo "$me: missing argument$help" >&2
    exit 1;;
 1) ;;
 *) echo "$me: too many arguments$help" >&2
    exit 1;;
esac






# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).











# Here we must recognize all the valid KERNEL-OS combinations.

maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
case $maybe_os in
  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
  linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
  kopensolaris*-gnu* | cloudabi*-eabi* | \
  storm-chaos* | os2-emx* | rtmk-nova*)

    os=-$maybe_os
    basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
    ;;
  android-linux)

    os=-linux-android
    basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
    ;;
  *)









    basic_machine=`echo "$1" | sed 's/-[^-]*$//'`







    if [ "$basic_machine" != "$1" ]
    then os=`echo "$1" | sed 's/.*-/-/'`












    else os=; fi
    ;;




esac




























### Let's recognize common machines as not being operating systems so
### that things like config.sub decstation-3100 work.  We also
### recognize some manufacturers as not being operating systems, so we



### can provide default operating systems below.



case $os in







	-sun*os*)

		# Prevent following clause from handling this invalid input.


		;;
	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
	-apple | -axis | -knuth | -cray | -microblaze*)

		os=






		basic_machine=$1

		;;








	-bluegene*)





		os=-cnk
		;;




















	-sim | -cisco | -oki | -wec | -winbond)

		os=










		basic_machine=$1

		;;
	-scout)










		;;












	-wrs)

		os=-vxworks


		basic_machine=$1

		;;


	-chorusos*)










		os=-chorusos






		basic_machine=$1

		;;






	-chorusrdb)



		os=-chorusrdb


		basic_machine=$1

		;;


	-hiux*)



		os=-hiuxwe2
		;;


















	-sco6)



		os=-sco5v6






		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;
	-sco5)

		os=-sco3.2v5


		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;
	-sco4)

		os=-sco3.2v4


		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;









	-sco3.2.[4-9]*)
		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`






		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;




	-sco3.2v[4-9]*)
		# Don't forget version if it is 3.2v4 or newer.




		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;








	-sco5v6*)
		# Don't forget version if it is 3.2v4 or newer.








		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;




	-sco*)





		os=-sco3.2v2






		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;






	-udk*)


		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;


	-isc)







		os=-isc2.2






		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;
	-clix*)

		basic_machine=clipper-intergraph

		;;






	-isc*)


		basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`

		;;




	-lynx*178)

		os=-lynxos178
		;;
	-lynx*5)













		os=-lynxos5
		;;


	-lynx*)



		os=-lynxos
		;;
	-ptx*)




		basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'`

		;;






	-psos*)



		os=-psos
		;;






























	-mint | -mint[0-9]*)


		basic_machine=m68k-atari




		os=-mint
		;;
esac



# Decode aliases for certain CPU-COMPANY combinations.
case $basic_machine in
	# Recognize the basic CPU types without company name.
	# Some are omitted here because they have special meanings below.
	1750a | 580 \
	| a29k \

	| aarch64 | aarch64_be | arm64 \
	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \

	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
	| am33_2.0 \


	| arc | arceb \
	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
	| avr | avr32 \
	| ba \
	| be32 | be64 \

	| bfin \




	| c4x | c8051 | clipper \


	| d10v | d30v | dlx | dsp16xx \



	| e2k | epiphany \



	| fido | fr30 | frv | ft32 \


	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
	| hexagon \
	| i370 | i860 | i960 | ia16 | ia64 \
	| ip2k | iq2000 \
	| k1om \

	| le32 | le64 \
	| lm32 \
	| m32c | m32r | m32rle | m68000 | m68k | m88k \


	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
	| mips | mipsbe | mipseb | mipsel | mipsle \
	| mips16 \
	| mips64 | mips64el \

	| mips64octeon | mips64octeonel \

	| mips64orion | mips64orionel \
	| mips64r5900 | mips64r5900el \

	| mips64vr | mips64vrel \


	| mips64vr4100 | mips64vr4100el \

	| mips64vr4300 | mips64vr4300el \









	| mips64vr5000 | mips64vr5000el \
	| mips64vr5900 | mips64vr5900el \
	| mipsisa32 | mipsisa32el \
	| mipsisa32r2 | mipsisa32r2el \

	| mipsisa32r6 | mipsisa32r6el \
	| mipsisa64 | mipsisa64el \
	| mipsisa64r2 | mipsisa64r2el \
	| mipsisa64r6 | mipsisa64r6el \
	| mipsisa64sb1 | mipsisa64sb1el \
	| mipsisa64sr71k | mipsisa64sr71kel \
	| mipsr5900 | mipsr5900el \
	| mipstx39 | mipstx39el \

	| mn10200 | mn10300 \
	| moxie \
	| mt \
	| msp430 \
	| nds32 | nds32le | nds32be \

	| nios | nios2 | nios2eb | nios2el \
	| ns16k | ns32k \
	| open8 | or1k | or1knd | or32 \

	| pdp10 | pj | pjl \


	| powerpc | powerpc64 | powerpc64le | powerpcle \














	| pru \
	| pyramid \
	| riscv32 | riscv64 \

	| rl78 | rx \

	| score \
	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \

	| sh64 | sh64le \
	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
	| spu \
	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
	| ubicom32 \

	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \


	| visium \









	| wasm32 \
	| x86 | xc16x | xstormy16 | xtensa \




	| z8k | z80)
		basic_machine=$basic_machine-unknown



		;;



















	c54x)











































		basic_machine=tic54x-unknown



		;;








	c55x)







		basic_machine=tic55x-unknown


		;;


































	c6x)








		basic_machine=tic6x-unknown


		;;
	leon|leon[3-9])
		basic_machine=sparc-$basic_machine
		;;
	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
		basic_machine=$basic_machine-unknown
		os=-none
		;;
	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65)
		;;

	ms1)
		basic_machine=mt-unknown
		;;

	strongarm | thumb | xscale)
		basic_machine=arm-unknown
		;;
	xgate)
		basic_machine=$basic_machine-unknown
		os=-none
		;;
	xscaleeb)
		basic_machine=armeb-unknown
		;;

	xscaleel)
		basic_machine=armel-unknown

		;;

	# We use `pc' rather than `unknown'
	# because (1) that's what they normally are, and
	# (2) the word "unknown" tends to confuse beginning users.
	i*86 | x86_64)
	  basic_machine=$basic_machine-pc

	  ;;
	# Object if more than one company name word.
	*-*-*)

		echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2

		exit 1

		;;




	# Recognize the basic CPU types with company name.
	580-* \
	| a29k-* \
	| aarch64-* | aarch64_be-* | arm64-* \
	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
	| avr-* | avr32-* \
	| ba-* \
	| be32-* | be64-* \
	| bfin-* | bs2000-* \
	| c[123]* | c30-* | [cjt]90-* | c4x-* \
	| c8051-* | clipper-* | craynv-* | cydra-* \
	| d10v-* | d30v-* | dlx-* \
	| e2k-* | elxsi-* \
	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
	| h8300-* | h8500-* \
	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \


	| hexagon-* \
	| i*86-* | i860-* | i960-* | ia16-* | ia64-* \
	| ip2k-* | iq2000-* \
	| k1om-* \
	| le32-* | le64-* \
	| lm32-* \
	| m32c-* | m32r-* | m32rle-* \
	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
	| microblaze-* | microblazeel-* \
	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
	| mips16-* \
	| mips64-* | mips64el-* \
	| mips64octeon-* | mips64octeonel-* \
	| mips64orion-* | mips64orionel-* \
	| mips64r5900-* | mips64r5900el-* \
	| mips64vr-* | mips64vrel-* \

	| mips64vr4100-* | mips64vr4100el-* \
	| mips64vr4300-* | mips64vr4300el-* \
	| mips64vr5000-* | mips64vr5000el-* \
	| mips64vr5900-* | mips64vr5900el-* \
	| mipsisa32-* | mipsisa32el-* \
	| mipsisa32r2-* | mipsisa32r2el-* \
	| mipsisa32r6-* | mipsisa32r6el-* \
	| mipsisa64-* | mipsisa64el-* \
	| mipsisa64r2-* | mipsisa64r2el-* \
	| mipsisa64r6-* | mipsisa64r6el-* \
	| mipsisa64sb1-* | mipsisa64sb1el-* \
	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
	| mipsr5900-* | mipsr5900el-* \
	| mipstx39-* | mipstx39el-* \
	| mmix-* \
	| mt-* \

	| msp430-* \
	| nds32-* | nds32le-* | nds32be-* \
	| nios-* | nios2-* | nios2eb-* | nios2el-* \
	| none-* | np1-* | ns16k-* | ns32k-* \
	| open8-* \


	| or1k*-* \
	| orion-* \

	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
	| pru-* \

	| pyramid-* \
	| riscv32-* | riscv64-* \

	| rl78-* | romp-* | rs6000-* | rx-* \
	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
	| sparclite-* \
	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
	| tahoe-* \
	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
	| tile*-* \
	| tron-* \
	| ubicom32-* \
	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \

	| vax-* \
	| visium-* \
	| wasm32-* \





	| we32k-* \
	| x86-* | x86_64-* | xc16x-* | xps100-* \
	| xstormy16-* | xtensa*-* \
	| ymp-* \
	| z8k-* | z80-*)
		;;
	# Recognize the basic CPU types without company name, with glob match.
	xtensa*)
		basic_machine=$basic_machine-unknown

		;;
	# Recognize the various machine names and aliases which stand
	# for a CPU type and a company and sometimes even an OS.
	386bsd)
		basic_machine=i386-pc
		os=-bsd
		;;
	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
		basic_machine=m68000-att
		;;
	3b*)


		basic_machine=we32k-att
		;;
	a29khif)
		basic_machine=a29k-amd
		os=-udi
		;;
	abacus)
		basic_machine=abacus-unknown
		;;
	adobe68k)
		basic_machine=m68010-adobe
		os=-scout
		;;
	alliant | fx80)
		basic_machine=fx80-alliant
		;;
	altos | altos3068)
		basic_machine=m68k-altos
		;;
	am29k)
		basic_machine=a29k-none
		os=-bsd
		;;
	amd64)
		basic_machine=x86_64-pc
		;;

	amd64-*)
		basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	amdahl)
		basic_machine=580-amdahl
		os=-sysv
		;;
	amiga | amiga-*)
		basic_machine=m68k-unknown
		;;
	amigaos | amigados)
		basic_machine=m68k-unknown
		os=-amigaos
		;;
	amigaunix | amix)
		basic_machine=m68k-unknown
		os=-sysv4
		;;
	apollo68)
		basic_machine=m68k-apollo
		os=-sysv
		;;
	apollo68bsd)
		basic_machine=m68k-apollo
		os=-bsd
		;;
	aros)
		basic_machine=i386-pc
		os=-aros
		;;
	asmjs)
		basic_machine=asmjs-unknown
		;;
	aux)
		basic_machine=m68k-apple
		os=-aux
		;;
	balance)
		basic_machine=ns32k-sequent
		os=-dynix
		;;
	blackfin)
		basic_machine=bfin-unknown
		os=-linux
		;;
	blackfin-*)
		basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		os=-linux
		;;
	bluegene*)
		basic_machine=powerpc-ibm
		os=-cnk
		;;
	c54x-*)
		basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	c55x-*)
		basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	c6x-*)
		basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	c90)
		basic_machine=c90-cray
		os=-unicos
		;;
	cegcc)
		basic_machine=arm-unknown
		os=-cegcc
		;;
	convex-c1)
		basic_machine=c1-convex
		os=-bsd
		;;
	convex-c2)
		basic_machine=c2-convex
		os=-bsd
		;;
	convex-c32)
		basic_machine=c32-convex
		os=-bsd
		;;
	convex-c34)
		basic_machine=c34-convex
		os=-bsd
		;;
	convex-c38)
		basic_machine=c38-convex
		os=-bsd
		;;
	cray | j90)
		basic_machine=j90-cray
		os=-unicos
		;;
	craynv)
		basic_machine=craynv-cray
		os=-unicosmp
		;;
	cr16 | cr16-*)
		basic_machine=cr16-unknown
		os=-elf
		;;
	crds | unos)
		basic_machine=m68k-crds
		;;
	crisv32 | crisv32-* | etraxfs*)
		basic_machine=crisv32-axis
		;;
	cris | cris-* | etrax*)
		basic_machine=cris-axis
		;;
	crx)
		basic_machine=crx-unknown
		os=-elf
		;;
	da30 | da30-*)
		basic_machine=m68k-da30
		;;
	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
		basic_machine=mips-dec
		;;
	decsystem10* | dec10*)
		basic_machine=pdp10-dec
		os=-tops10
		;;
	decsystem20* | dec20*)
		basic_machine=pdp10-dec
		os=-tops20
		;;
	delta | 3300 | motorola-3300 | motorola-delta \
	      | 3300-motorola | delta-motorola)
		basic_machine=m68k-motorola
		;;
	delta88)
		basic_machine=m88k-motorola
		os=-sysv3
		;;
	dicos)
		basic_machine=i686-pc
		os=-dicos
		;;
	djgpp)
		basic_machine=i586-pc
		os=-msdosdjgpp
		;;
	dpx20 | dpx20-*)
		basic_machine=rs6000-bull
		os=-bosx
		;;
	dpx2*)
		basic_machine=m68k-bull
		os=-sysv3
		;;
	e500v[12])
		basic_machine=powerpc-unknown
		os=$os"spe"
		;;
	e500v[12]-*)
		basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		os=$os"spe"
		;;
	ebmon29k)
		basic_machine=a29k-amd
		os=-ebmon
		;;
	elxsi)
		basic_machine=elxsi-elxsi
		os=-bsd
		;;
	encore | umax | mmax)
		basic_machine=ns32k-encore
		;;
	es1800 | OSE68k | ose68k | ose | OSE)
		basic_machine=m68k-ericsson
		os=-ose
		;;
	fx2800)
		basic_machine=i860-alliant
		;;
	genix)
		basic_machine=ns32k-ns
		;;
	gmicro)
		basic_machine=tron-gmicro
		os=-sysv
		;;
	go32)
		basic_machine=i386-pc
		os=-go32
		;;
	h3050r* | hiux*)
		basic_machine=hppa1.1-hitachi
		os=-hiuxwe2
		;;
	h8300hms)
		basic_machine=h8300-hitachi
		os=-hms
		;;
	h8300xray)
		basic_machine=h8300-hitachi
		os=-xray
		;;
	h8500hms)
		basic_machine=h8500-hitachi
		os=-hms
		;;
	harris)
		basic_machine=m88k-harris
		os=-sysv3
		;;
	hp300-*)
		basic_machine=m68k-hp
		;;
	hp300bsd)
		basic_machine=m68k-hp
		os=-bsd
		;;
	hp300hpux)
		basic_machine=m68k-hp
		os=-hpux
		;;
	hp3k9[0-9][0-9] | hp9[0-9][0-9])
		basic_machine=hppa1.0-hp
		;;
	hp9k2[0-9][0-9] | hp9k31[0-9])
		basic_machine=m68000-hp
		;;
	hp9k3[2-9][0-9])
		basic_machine=m68k-hp
		;;
	hp9k6[0-9][0-9] | hp6[0-9][0-9])
		basic_machine=hppa1.0-hp
		;;
	hp9k7[0-79][0-9] | hp7[0-79][0-9])
		basic_machine=hppa1.1-hp
		;;
	hp9k78[0-9] | hp78[0-9])
		# FIXME: really hppa2.0-hp
		basic_machine=hppa1.1-hp
		;;
	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
		# FIXME: really hppa2.0-hp
		basic_machine=hppa1.1-hp
		;;
	hp9k8[0-9][13679] | hp8[0-9][13679])
		basic_machine=hppa1.1-hp
		;;
	hp9k8[0-9][0-9] | hp8[0-9][0-9])
		basic_machine=hppa1.0-hp
		;;
	hppaosf)
		basic_machine=hppa1.1-hp
		os=-osf
		;;
	hppro)
		basic_machine=hppa1.1-hp
		os=-proelf
		;;
	i370-ibm* | ibm*)
		basic_machine=i370-ibm
		;;
	i*86v32)
		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
		os=-sysv32
		;;
	i*86v4*)
		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
		os=-sysv4
		;;
	i*86v)
		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
		os=-sysv
		;;
	i*86sol2)
		basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
		os=-solaris2
		;;
	i386mach)
		basic_machine=i386-mach
		os=-mach
		;;
	vsta)
		basic_machine=i386-unknown
		os=-vsta
		;;
	iris | iris4d)
		basic_machine=mips-sgi
		case $os in
		    -irix*)
			;;
		    *)
			os=-irix4
			;;
		esac
		;;
	isi68 | isi)
		basic_machine=m68k-isi
		os=-sysv
		;;
	leon-*|leon[3-9]-*)
		basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'`
		;;
	m68knommu)
		basic_machine=m68k-unknown
		os=-linux
		;;
	m68knommu-*)
		basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		os=-linux
		;;
	magnum | m3230)
		basic_machine=mips-mips
		os=-sysv
		;;
	merlin)
		basic_machine=ns32k-utek
		os=-sysv
		;;
	microblaze*)

		basic_machine=microblaze-xilinx
		;;

	mingw64)
		basic_machine=x86_64-pc
		os=-mingw64
		;;
	mingw32)

		basic_machine=i686-pc
		os=-mingw32
		;;

	mingw32ce)
		basic_machine=arm-unknown
		os=-mingw32ce
		;;
	miniframe)
		basic_machine=m68000-convergent
		;;
	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
		basic_machine=m68k-atari
		os=-mint
		;;
	mips3*-*)
		basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`
		;;
	mips3*)
		basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown
		;;
	monitor)
		basic_machine=m68k-rom68k
		os=-coff
		;;
	morphos)
		basic_machine=powerpc-unknown
		os=-morphos
		;;
	moxiebox)
		basic_machine=moxie-unknown
		os=-moxiebox
		;;
	msdos)

		basic_machine=i386-pc
		os=-msdos
		;;
	ms1-*)
		basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'`
		;;
	msys)
		basic_machine=i686-pc
		os=-msys
		;;
	mvs)
		basic_machine=i370-ibm
		os=-mvs
		;;
	nacl)
		basic_machine=le32-unknown
		os=-nacl
		;;
	ncr3000)
		basic_machine=i486-ncr
		os=-sysv4

		;;
	netbsd386)
		basic_machine=i386-unknown
		os=-netbsd
		;;
	netwinder)

		basic_machine=armv4l-rebel
		os=-linux
		;;
	news | news700 | news800 | news900)
		basic_machine=m68k-sony
		os=-newsos

		;;
	news1000)
		basic_machine=m68030-sony
		os=-newsos
		;;
	news-3600 | risc-news)
		basic_machine=mips-sony
		os=-newsos

		;;
	necv70)
		basic_machine=v70-nec
		os=-sysv


		;;
	next | m*-next)
		basic_machine=m68k-next
		case $os in
		    -nextstep* )
			;;
		    -ns2*)
		      os=-nextstep2
			;;
		    *)
		      os=-nextstep3
			;;
		esac
		;;
	nh3000)

		basic_machine=m68k-harris
		os=-cxux
		;;
	nh[45]000)
		basic_machine=m88k-harris
		os=-cxux
		;;
	nindy960)
		basic_machine=i960-intel
		os=-nindy
		;;
	mon960)
		basic_machine=i960-intel
		os=-mon960
		;;
	nonstopux)
		basic_machine=mips-compaq
		os=-nonstopux
		;;
	np1)
		basic_machine=np1-gould
		;;
	neo-tandem)

		basic_machine=neo-tandem
		;;
	nse-tandem)

		basic_machine=nse-tandem
		;;
	nsr-tandem)

		basic_machine=nsr-tandem
		;;
	nsv-tandem)

		basic_machine=nsv-tandem
		;;
	nsx-tandem)
		basic_machine=nsx-tandem
		;;
	op50n-* | op60c-*)
		basic_machine=hppa1.1-oki
		os=-proelf
		;;
	openrisc | openrisc-*)
		basic_machine=or32-unknown
		;;
	os400)
		basic_machine=powerpc-ibm
		os=-os400
		;;
	OSE68000 | ose68000)
		basic_machine=m68000-ericsson
		os=-ose
		;;
	os68k)
		basic_machine=m68k-none
		os=-os68k
		;;
	pa-hitachi)
		basic_machine=hppa1.1-hitachi
		os=-hiuxwe2
		;;
	paragon)
		basic_machine=i860-intel
		os=-osf
		;;
	parisc)
		basic_machine=hppa-unknown
		os=-linux
		;;
	parisc-*)
		basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		os=-linux
		;;
	pbd)
		basic_machine=sparc-tti
		;;
	pbb)
		basic_machine=m68k-tti
		;;
	pc532 | pc532-*)
		basic_machine=ns32k-pc532
		;;
	pc98)
		basic_machine=i386-pc
		;;
	pc98-*)
		basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	pentium | p5 | k5 | k6 | nexgen | viac3)
		basic_machine=i586-pc
		;;
	pentiumpro | p6 | 6x86 | athlon | athlon_*)
		basic_machine=i686-pc
		;;
	pentiumii | pentium2 | pentiumiii | pentium3)
		basic_machine=i686-pc
		;;
	pentium4)
		basic_machine=i786-pc
		;;
	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
		basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	pentiumpro-* | p6-* | 6x86-* | athlon-*)
		basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
		basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	pentium4-*)
		basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	pn)
		basic_machine=pn-gould
		;;
	power)	basic_machine=power-ibm
		;;
	ppc | ppcbe)	basic_machine=powerpc-unknown
		;;
	ppc-* | ppcbe-*)
		basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	ppcle | powerpclittle)
		basic_machine=powerpcle-unknown
		;;
	ppcle-* | powerpclittle-*)
		basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	ppc64)	basic_machine=powerpc64-unknown
		;;
	ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	ppc64le | powerpc64little)
		basic_machine=powerpc64le-unknown
		;;
	ppc64le-* | powerpc64little-*)
		basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	ps2)
		basic_machine=i386-ibm
		;;
	psp)
		basic_machine=mipsallegrexel-psp
		os=-elf
		;;
	pw32)
		basic_machine=i586-unknown
		os=-pw32
		;;
	rdos | rdos64)
		basic_machine=x86_64-pc
		os=-rdos
		;;
	rdos32)
		basic_machine=i386-pc
		os=-rdos
		;;
	rom68k)
		basic_machine=m68k-rom68k
		os=-coff
		;;
	rm[46]00)
		basic_machine=mips-siemens
		;;
	rtpc | rtpc-*)
		basic_machine=romp-ibm
		;;
	s390 | s390-*)
		basic_machine=s390-ibm
		;;
	s390x | s390x-*)
		basic_machine=s390x-ibm
		;;
	sa29200)
		basic_machine=a29k-amd
		os=-udi
		;;
	sb1)
		basic_machine=mipsisa64sb1-unknown
		;;
	sb1el)
		basic_machine=mipsisa64sb1el-unknown
		;;
	sde)
		basic_machine=mipsisa32-sde
		os=-elf
		;;
	sei)
		basic_machine=mips-sei
		os=-seiux
		;;
	sequent)
		basic_machine=i386-sequent
		;;
	sh5el)
		basic_machine=sh5le-unknown
		;;
	simso-wrs)
		basic_machine=sparclite-wrs
		os=-vxworks
		;;
	sps7)
		basic_machine=m68k-bull
		os=-sysv2
		;;
	spur)
		basic_machine=spur-unknown
		;;
	st2000)
		basic_machine=m68k-tandem
		;;
	stratus)
		basic_machine=i860-stratus
		os=-sysv4
		;;
	strongarm-* | thumb-*)
		basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'`
		;;
	sun2)
		basic_machine=m68000-sun
		;;
	sun2os3)
		basic_machine=m68000-sun
		os=-sunos3
		;;
	sun2os4)
		basic_machine=m68000-sun
		os=-sunos4
		;;
	sun3os3)
		basic_machine=m68k-sun
		os=-sunos3
		;;
	sun3os4)
		basic_machine=m68k-sun
		os=-sunos4
		;;
	sun4os3)
		basic_machine=sparc-sun
		os=-sunos3
		;;
	sun4os4)
		basic_machine=sparc-sun
		os=-sunos4
		;;
	sun4sol2)
		basic_machine=sparc-sun
		os=-solaris2
		;;
	sun3 | sun3-*)
		basic_machine=m68k-sun
		;;
	sun4)
		basic_machine=sparc-sun
		;;
	sun386 | sun386i | roadrunner)
		basic_machine=i386-sun
		;;
	sv1)
		basic_machine=sv1-cray
		os=-unicos
		;;
	symmetry)
		basic_machine=i386-sequent
		os=-dynix
		;;
	t3e)
		basic_machine=alphaev5-cray
		os=-unicos
		;;
	t90)
		basic_machine=t90-cray
		os=-unicos
		;;
	tile*)
		basic_machine=$basic_machine-unknown
		os=-linux-gnu
		;;
	tx39)
		basic_machine=mipstx39-unknown
		;;
	tx39el)
		basic_machine=mipstx39el-unknown
		;;
	toad1)
		basic_machine=pdp10-xkl
		os=-tops20
		;;
	tower | tower-32)
		basic_machine=m68k-ncr
		;;
	tpf)
		basic_machine=s390x-ibm
		os=-tpf
		;;
	udi29k)
		basic_machine=a29k-amd
		os=-udi
		;;
	ultra3)
		basic_machine=a29k-nyu
		os=-sym1
		;;
	v810 | necv810)
		basic_machine=v810-nec
		os=-none
		;;
	vaxv)
		basic_machine=vax-dec
		os=-sysv
		;;
	vms)
		basic_machine=vax-dec
		os=-vms
		;;
	vpp*|vx|vx-*)
		basic_machine=f301-fujitsu
		;;
	vxworks960)
		basic_machine=i960-wrs
		os=-vxworks
		;;
	vxworks68)
		basic_machine=m68k-wrs
		os=-vxworks
		;;
	vxworks29k)
		basic_machine=a29k-wrs
		os=-vxworks
		;;
	w65*)
		basic_machine=w65-wdc
		os=-none
		;;
	w89k-*)
		basic_machine=hppa1.1-winbond
		os=-proelf
		;;
	x64)
		basic_machine=x86_64-pc
		;;
	xbox)
		basic_machine=i686-pc
		os=-mingw32
		;;
	xps | xps100)
		basic_machine=xps100-honeywell
		;;
	xscale-* | xscalee[bl]-*)
		basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'`
		;;
	ymp)
		basic_machine=ymp-cray
		os=-unicos
		;;
	none)
		basic_machine=none-none
		os=-none
		;;

# Here we handle the default manufacturer of certain CPU types.  It is in
# some cases the only manufacturer, in others, it is the most popular.
	w89k)
		basic_machine=hppa1.1-winbond
		;;
	op50n)
		basic_machine=hppa1.1-oki
		;;
	op60c)
		basic_machine=hppa1.1-oki
		;;
	romp)
		basic_machine=romp-ibm
		;;
	mmix)
		basic_machine=mmix-knuth
		;;
	rs6000)
		basic_machine=rs6000-ibm
		;;
	vax)
		basic_machine=vax-dec
		;;
	pdp11)
		basic_machine=pdp11-dec
		;;
	we32k)
		basic_machine=we32k-att
		;;
	sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
		basic_machine=sh-unknown
		;;
	cydra)
		basic_machine=cydra-cydrome
		;;
	orion)
		basic_machine=orion-highlevel
		;;
	orion105)
		basic_machine=clipper-highlevel
		;;
	mac | mpw | mac-mpw)
		basic_machine=m68k-apple
		;;
	pmac | pmac-mpw)
		basic_machine=powerpc-apple
		;;
	*-unknown)
		# Make sure to match an already-canonicalized machine name.
		;;
	*)






































































































		echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
		exit 1
		;;
esac



# Here we canonicalize certain aliases for manufacturers.
case $basic_machine in
	*-digital*)
		basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'`

		;;
	*-commodore*)
		basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'`
		;;
	*)
		;;
esac

# Decode manufacturer-specific aliases for certain operating systems.

if [ x"$os" != x"" ]
then
case $os in
	# First match some system type aliases that might get confused
	# with valid system types.
	# -solaris* is a basic system type, with this one exception.
	-auroraux)
		os=-auroraux
		;;



	-solaris1 | -solaris1.*)
		os=`echo $os | sed -e 's|solaris1|sunos4|'`
		;;
	-solaris)
		os=-solaris2
		;;
	-unixware*)
		os=-sysv4.2uw
		;;
	-gnu/linux*)
		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
		;;
	# es1800 is here to avoid being matched by es* (a different OS)
	-es1800*)
		os=-ose































		;;
	# Now accept the basic system types.
	# The portable systems comes first.
	# Each alternative MUST end in a * to match a version number.
	# -sysv* is not here because it comes later, after sysvr4.
	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
	      | -sym* | -kopensolaris* | -plan9* \
	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
	      | -aos* | -aros* | -cloudabi* | -sortix* \
	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
	      | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \
	      | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* | -hcos* \
	      | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \
	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
	      | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \
	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
	      | -morphos* | -superux* | -rtmk* | -windiss* \
	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
	      | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \
	      | -midnightbsd*)
	# Remember, each alternative MUST END IN *, to match a version number.
		;;
	-qnx*)
		case $basic_machine in
		    x86-* | i*86-*)
			;;
		    *)
			os=-nto$os
			;;
		esac
		;;



	-nto-qnx*)
		;;
	-nto*)
		os=`echo $os | sed -e 's|nto|nto-qnx|'`
		;;
	-sim | -xray | -os68k* | -v88r* \
	      | -windows* | -osx | -abug | -netware* | -os9* \
	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
		;;
	-mac*)
		os=`echo "$os" | sed -e 's|mac|macos|'`
		;;
	-linux-dietlibc)
		os=-linux-dietlibc
		;;
	-linux*)
		os=`echo $os | sed -e 's|linux|linux-gnu|'`
		;;


















	-sunos5*)
		os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
		;;
	-sunos6*)
		os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
		;;
	-opened*)
		os=-openedition
		;;
	-os400*)
		os=-os400
		;;
	-wince*)
		os=-wince
		;;
	-utek*)
		os=-bsd
		;;
	-dynix*)
		os=-bsd
		;;
	-acis*)
		os=-aos
		;;
	-atheos*)
		os=-atheos
		;;
	-syllable*)
		os=-syllable
		;;
	-386bsd)
		os=-bsd
		;;
	-ctix* | -uts*)
		os=-sysv
		;;
	-nova*)
		os=-rtmk-nova
		;;
	-ns2)
		os=-nextstep2
		;;
	-nsk*)
		os=-nsk
		;;
	# Preserve the version number of sinix5.
	-sinix5.*)
		os=`echo $os | sed -e 's|sinix|sysv|'`
		;;
	-sinix*)
		os=-sysv4
		;;
	-tpf*)
		os=-tpf
		;;
	-triton*)
		os=-sysv3
		;;
	-oss*)
		os=-sysv3
		;;
	-svr4*)
		os=-sysv4
		;;
	-svr3)
		os=-sysv3
		;;
	-sysvr4)
		os=-sysv4
		;;
	# This must come after -sysvr4.
	-sysv*)
		;;
	-ose*)
		os=-ose
		;;
	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
		os=-mint
		;;
	-zvmoe)
		os=-zvmoe
		;;
	-dicos*)
		os=-dicos
		;;
	-pikeos*)
		# Until real need of OS specific support for
		# particular features comes up, bare metal
		# configurations are quite functional.
		case $basic_machine in
		    arm*)
			os=-eabi
			;;
		    *)
			os=-elf
			;;
		esac
		;;
	-nacl*)
		;;
	-ios)
		;;
	-none)
		;;


	*)
		# Get rid of the `-' at the beginning of $os.
		os=`echo $os | sed 's/[^-]*-//'`
		echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
		exit 1
		;;
esac
else

# Here we handle the default operating systems that come with various machines.
# The value should be what the vendor currently ships out the door with their
# machine or put another way, the most popular os provided with the machine.

# Note that if you're going to try to match "-MANUFACTURER" here (say,
# "-sun"), then you have to tell the case statement up towards the top
# that MANUFACTURER isn't an operating system.  Otherwise, code above
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.

case $basic_machine in
	score-*)
		os=-elf
		;;
	spu-*)
		os=-elf
		;;
	*-acorn)
		os=-riscix1.2
		;;
	arm*-rebel)
		os=-linux
		;;
	arm*-semi)
		os=-aout
		;;
	c4x-* | tic4x-*)
		os=-coff
		;;
	c8051-*)
		os=-elf
		;;



	hexagon-*)
		os=-elf
		;;
	tic54x-*)
		os=-coff
		;;
	tic55x-*)
		os=-coff
		;;
	tic6x-*)
		os=-coff
		;;
	# This must come before the *-dec entry.
	pdp10-*)
		os=-tops20
		;;
	pdp11-*)
		os=-none
		;;
	*-dec | vax-*)
		os=-ultrix4.2
		;;
	m68*-apollo)
		os=-domain
		;;
	i386-sun)
		os=-sunos4.0.2
		;;
	m68000-sun)
		os=-sunos3
		;;
	m68*-cisco)
		os=-aout
		;;
	mep-*)
		os=-elf
		;;
	mips*-cisco)
		os=-elf
		;;
	mips*-*)
		os=-elf
		;;
	or32-*)
		os=-coff
		;;
	*-tti)	# must be before sparc entry or we get the wrong os.
		os=-sysv3
		;;
	sparc-* | *-sun)
		os=-sunos4.1.1
		;;
	pru-*)
		os=-elf
		;;
	*-be)
		os=-beos
		;;
	*-ibm)
		os=-aix
		;;
	*-knuth)
		os=-mmixware
		;;
	*-wec)
		os=-proelf
		;;
	*-winbond)
		os=-proelf
		;;
	*-oki)
		os=-proelf
		;;
	*-hp)
		os=-hpux
		;;
	*-hitachi)
		os=-hiux
		;;
	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
		os=-sysv
		;;
	*-cbm)
		os=-amigaos
		;;
	*-dg)
		os=-dgux
		;;
	*-dolphin)
		os=-sysv3
		;;
	m68k-ccur)
		os=-rtu
		;;
	m88k-omron*)
		os=-luna
		;;
	*-next)
		os=-nextstep
		;;
	*-sequent)
		os=-ptx
		;;
	*-crds)
		os=-unos
		;;
	*-ns)
		os=-genix
		;;
	i370-*)
		os=-mvs
		;;
	*-gould)
		os=-sysv
		;;
	*-highlevel)
		os=-bsd
		;;
	*-encore)
		os=-bsd
		;;
	*-sgi)
		os=-irix
		;;
	*-siemens)
		os=-sysv4
		;;
	*-masscomp)
		os=-rtu
		;;
	f30[01]-fujitsu | f700-fujitsu)
		os=-uxpv
		;;
	*-rom68k)
		os=-coff
		;;
	*-*bug)
		os=-coff
		;;
	*-apple)
		os=-macos
		;;
	*-atari*)
		os=-mint
		;;



	*)
		os=-none
		;;
esac
fi

# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer.  We pick the logical manufacturer.
vendor=unknown
case $basic_machine in
	*-unknown)
		case $os in
			-riscix*)
				vendor=acorn
				;;
			-sunos*)
				vendor=sun
				;;
			-cnk*|-aix*)
				vendor=ibm
				;;
			-beos*)
				vendor=be
				;;
			-hpux*)
				vendor=hp
				;;
			-mpeix*)
				vendor=hp
				;;
			-hiux*)
				vendor=hitachi
				;;
			-unos*)
				vendor=crds
				;;
			-dgux*)
				vendor=dg
				;;
			-luna*)
				vendor=omron
				;;
			-genix*)
				vendor=ns
				;;



			-mvs* | -opened*)
				vendor=ibm
				;;
			-os400*)
				vendor=ibm
				;;
			-ptx*)
				vendor=sequent
				;;
			-tpf*)
				vendor=ibm
				;;
			-vxsim* | -vxworks* | -windiss*)
				vendor=wrs
				;;
			-aux*)
				vendor=apple
				;;
			-hms*)
				vendor=hitachi
				;;
			-mpw* | -macos*)
				vendor=apple
				;;
			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
				vendor=atari
				;;
			-vos*)
				vendor=stratus
				;;
		esac
		basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"`
		;;
esac

echo "$basic_machine$os"
exit

# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:







|




















>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
|
>
|
|
|
|
|
|
|
>
|
<
|
|
>
|
<
|
|
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
<
>
>
>
|
>
>
>
|
>
>
>
>
>
>
>
|
>
|
>
>
|
<
<
|
<
<
<
<
>
|
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
|
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
>
>
|
>
|
|
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
>
>
|
>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
|
>
>
>
|
>
>
|
>
|
>
>
|
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
|
>
>
>
>
>
>
|
>
|
|
>
|
>
>
|
>
|
|
>
|
>
>
|
>
|
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
|
>
|
>
>
>
>
|
<
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
|
<
>
>
>
>
>
>
>
>
|
>
|
>
>
>
>
|
>
>
>
>
>
|
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
|
>
>
|
>
|
>
>
|
>
>
>
>
>
>
>
|
>
>
>
>
>
>
|
>
|
<
>
|
>
|
>
>
>
>
>
>
|
>
>
|
>
|
>
>
>
>
|
>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
|
>
>
>
|
|
|
>
>
>
>
|
>
|
>
>
>
>
>
>
|
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
|
>
>
>
>
|
|
|
>
>

|

|
|
<
|
>
|
<
>
|
|
>
>
|
<
<
|
|
>
|
>
>
>
>
|
>
>
|
>
>
>
|
>
>
>
|
>
>
|
<
<
|
|
>
|
|
<
>
>
|
|
|
|
>
|
>
|
|
>
|
>
>
|
>
|
>
>
>
>
>
>
>
>
>
|
<
|
|
>
|
|
|
<
|
<
<
<
>
|
|
<
<
|
>
|
|
|
>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
<
>
|
>
|
<
>
|
<
|
|
<
|
>
|
>
>
|
>
>
>
>
>
>
>
>
>
|
<
>
>
>
>
|
<
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>

>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
|
>
>


|
<
<
|
<

<
<
>
|
|


<
<
<
<
<
|
<
|
<
<
|
<
|
>

<




|
>
|
<
<
>
|
>
|
>

>
>
>
>
|
|
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
>
>
|
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
|
>
|
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
>
|
<
<
<
|
>
>
|
<
>
|
<
|
>
|
|
>
|
<
<
<
<
<
|
<
<
<
<
<
>
|
<
<
>
>
>
>
>
|
<
|
<
<

<
<
|
>

<
<
<
|
|

|
|

<
>
>
|

|
|
<
<
<
|
<
|
<
<
<
|
<
<
<
<
<
<
<
|

|
<
<
>

|

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

|
|

<
<
<
<

|


|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<

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

|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
|
|
<
<
|
<
<
<
<
<
<
<
<
<
<
<


|
|

|
<
|

|
<
|

|
>
|

>
|
<
<

<
>
|
<

>
|
<
<

<
|
<
<
<
|

|
|

|
|

|
<
|

|
|
<

<
<
<
<
<
>
|
<

|
<
<
<
|
<

<
<
<
<
|
|
<

<
<
|
>

|
<
|

<
>
|
<

<
<
|
>

|
|
<

<
<
|
>

<
<
<
>
>

<
|
|
|
<
<
<
<
|
|
|
<
<
<
>
|
|

<
|
|
<
|
<
<

<
<
|
<
<
<
|
<
<
<


>
|


>
|


>
|


>
|


|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|

|
<
|

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

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

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
>
>


|
|
<
>

|
|







|




|
|
|

>
>
>
|


|
|

|
|

|



|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




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


|
|
|


|



>
>
>
|

|


|
|
|

<
<
<
|
|

|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|


|


<
<
<
<
<
<
|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|


|


|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|

|
|

|



|

|


|



|

|

|

>
>

<
<
















|

|


|


|


|


|


|


|

>
>
>

|


|


|


|



|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|

>
>
>

|






|
<
|

|


|


|


|


|


|


|


|


|


|


|


>
>
>
|


|


|


|


|


|


|


|


|


|



<



|








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
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
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520


1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
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
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718

1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
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
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787

1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
    --help | --h* | -h )
       echo "$usage"; exit ;;
    -- )     # Stop option processing
       shift; break ;;
    - )	# Use stdin as input.
       break ;;
    -* )
       echo "$me: invalid option $1$help" >&2
       exit 1 ;;

    *local*)
       # First pass through any local machine types.
       echo "$1"
       exit ;;

    * )
       break ;;
  esac
done

case $# in
 0) echo "$me: missing argument$help" >&2
    exit 1;;
 1) ;;
 *) echo "$me: too many arguments$help" >&2
    exit 1;;
esac

# Split fields of configuration type
# shellcheck disable=SC2162
IFS="-" read field1 field2 field3 field4 <<EOF
$1
EOF

# Separate into logical components for further validation
case $1 in
	*-*-*-*-*)
		echo Invalid configuration \`"$1"\': more than four components >&2
		exit 1
		;;
	*-*-*-*)
		basic_machine=$field1-$field2
		os=$field3-$field4
		;;
	*-*-*)
		# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
		# parts
		maybe_os=$field2-$field3
		case $maybe_os in
			nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
			| linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
			| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
			| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
			| storm-chaos* | os2-emx* | rtmk-nova*)
				basic_machine=$field1
				os=$maybe_os

				;;
			android-linux)
				basic_machine=$field1-unknown
				os=linux-android

				;;
			*)
				basic_machine=$field1-$field2
				os=$field3
				;;
		esac
		;;
	*-*)
		# A lone config we happen to match not fitting any pattern
		case $field1-$field2 in
			decstation-3100)
				basic_machine=mips-dec
				os=
				;;
			*-*)
				# Second component is usually, but not always the OS
				case $field2 in
					# Prevent following clause from handling this valid os
					sun*os*)
						basic_machine=$field1
						os=$field2
						;;
					# Manufacturers
					dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
					| att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
					| unicom* | ibm* | next | hp | isi* | apollo | altos* \
					| convergent* | ncr* | news | 32* | 3600* | 3100* \
					| hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
					| ultra | tti* | harris | dolphin | highlevel | gould \
					| cbm | ns | masscomp | apple | axis | knuth | cray \
					| microblaze* | sim | cisco \
					| oki | wec | wrs | winbond)
						basic_machine=$field1-$field2
						os=
						;;
					*)
						basic_machine=$field1
						os=$field2
						;;
				esac
			;;
		esac
		;;
	*)
		# Convert single-component short-hands not valid as part of
		# multi-component configurations.
		case $field1 in
			386bsd)
				basic_machine=i386-pc
				os=bsd
				;;
			a29khif)
				basic_machine=a29k-amd
				os=udi
				;;
			adobe68k)
				basic_machine=m68010-adobe
				os=scout
				;;
			alliant)
				basic_machine=fx80-alliant
				os=
				;;
			altos | altos3068)
				basic_machine=m68k-altos
				os=
				;;
			am29k)
				basic_machine=a29k-none
				os=bsd

				;;
			amdahl)
				basic_machine=580-amdahl
				os=sysv
				;;
			amiga)
				basic_machine=m68k-unknown
				os=
				;;
			amigaos | amigados)
				basic_machine=m68k-unknown
				os=amigaos
				;;
			amigaunix | amix)
				basic_machine=m68k-unknown
				os=sysv4
				;;
			apollo68)
				basic_machine=m68k-apollo
				os=sysv
				;;


			apollo68bsd)




				basic_machine=m68k-apollo
				os=bsd
				;;
			aros)
				basic_machine=i386-pc
				os=aros
				;;
			aux)
				basic_machine=m68k-apple
				os=aux
				;;
			balance)
				basic_machine=ns32k-sequent
				os=dynix
				;;
			blackfin)
				basic_machine=bfin-unknown
				os=linux
				;;
			cegcc)
				basic_machine=arm-unknown
				os=cegcc
				;;
			convex-c1)
				basic_machine=c1-convex
				os=bsd
				;;
			convex-c2)
				basic_machine=c2-convex
				os=bsd
				;;
			convex-c32)
				basic_machine=c32-convex
				os=bsd
				;;
			convex-c34)
				basic_machine=c34-convex
				os=bsd
				;;
			convex-c38)
				basic_machine=c38-convex
				os=bsd
				;;
			cray)
				basic_machine=j90-cray
				os=unicos
				;;
			crds | unos)
				basic_machine=m68k-crds
				os=
				;;
			da30)
				basic_machine=m68k-da30
				os=
				;;
			decstation | pmax | pmin | dec3100 | decstatn)
				basic_machine=mips-dec
				os=
				;;
			delta88)
				basic_machine=m88k-motorola
				os=sysv3
				;;
			dicos)
				basic_machine=i686-pc
				os=dicos
				;;
			djgpp)
				basic_machine=i586-pc
				os=msdosdjgpp
				;;
			ebmon29k)
				basic_machine=a29k-amd
				os=ebmon
				;;
			es1800 | OSE68k | ose68k | ose | OSE)
				basic_machine=m68k-ericsson
				os=ose
				;;
			gmicro)
				basic_machine=tron-gmicro
				os=sysv
				;;
			go32)
				basic_machine=i386-pc
				os=go32
				;;
			h8300hms)
				basic_machine=h8300-hitachi
				os=hms
				;;
			h8300xray)
				basic_machine=h8300-hitachi
				os=xray
				;;
			h8500hms)
				basic_machine=h8500-hitachi
				os=hms
				;;
			harris)
				basic_machine=m88k-harris
				os=sysv3
				;;
			hp300)
				basic_machine=m68k-hp
				;;
			hp300bsd)
				basic_machine=m68k-hp
				os=bsd
				;;
			hp300hpux)
				basic_machine=m68k-hp
				os=hpux
				;;
			hppaosf)
				basic_machine=hppa1.1-hp
				os=osf
				;;
			hppro)
				basic_machine=hppa1.1-hp
				os=proelf
				;;
			i386mach)
				basic_machine=i386-mach
				os=mach
				;;
			vsta)
				basic_machine=i386-pc
				os=vsta
				;;
			isi68 | isi)
				basic_machine=m68k-isi
				os=sysv
				;;
			m68knommu)
				basic_machine=m68k-unknown
				os=linux
				;;
			magnum | m3230)
				basic_machine=mips-mips
				os=sysv
				;;
			merlin)
				basic_machine=ns32k-utek
				os=sysv
				;;
			mingw64)
				basic_machine=x86_64-pc
				os=mingw64
				;;
			mingw32)
				basic_machine=i686-pc
				os=mingw32
				;;
			mingw32ce)
				basic_machine=arm-unknown
				os=mingw32ce
				;;
			monitor)
				basic_machine=m68k-rom68k
				os=coff
				;;
			morphos)
				basic_machine=powerpc-unknown
				os=morphos
				;;
			moxiebox)
				basic_machine=moxie-unknown
				os=moxiebox
				;;
			msdos)
				basic_machine=i386-pc
				os=msdos
				;;
			msys)
				basic_machine=i686-pc
				os=msys
				;;
			mvs)
				basic_machine=i370-ibm
				os=mvs
				;;
			nacl)
				basic_machine=le32-unknown
				os=nacl
				;;
			ncr3000)
				basic_machine=i486-ncr
				os=sysv4
				;;
			netbsd386)
				basic_machine=i386-pc
				os=netbsd
				;;
			netwinder)
				basic_machine=armv4l-rebel
				os=linux
				;;
			news | news700 | news800 | news900)
				basic_machine=m68k-sony
				os=newsos
				;;
			news1000)
				basic_machine=m68030-sony
				os=newsos
				;;
			necv70)
				basic_machine=v70-nec
				os=sysv
				;;
			nh3000)
				basic_machine=m68k-harris
				os=cxux
				;;
			nh[45]000)

				basic_machine=m88k-harris
				os=cxux
				;;
			nindy960)
				basic_machine=i960-intel
				os=nindy
				;;
			mon960)
				basic_machine=i960-intel
				os=mon960
				;;
			nonstopux)
				basic_machine=mips-compaq
				os=nonstopux
				;;
			os400)

				basic_machine=powerpc-ibm
				os=os400
				;;
			OSE68000 | ose68000)
				basic_machine=m68000-ericsson
				os=ose
				;;
			os68k)
				basic_machine=m68k-none
				os=os68k
				;;
			paragon)
				basic_machine=i860-intel
				os=osf
				;;
			parisc)
				basic_machine=hppa-unknown
				os=linux
				;;
			pw32)
				basic_machine=i586-unknown
				os=pw32
				;;
			rdos | rdos64)
				basic_machine=x86_64-pc
				os=rdos
				;;
			rdos32)
				basic_machine=i386-pc
				os=rdos
				;;
			rom68k)
				basic_machine=m68k-rom68k
				os=coff
				;;
			sa29200)
				basic_machine=a29k-amd
				os=udi
				;;
			sei)
				basic_machine=mips-sei
				os=seiux
				;;
			sequent)
				basic_machine=i386-sequent
				os=
				;;
			sps7)
				basic_machine=m68k-bull
				os=sysv2
				;;
			st2000)
				basic_machine=m68k-tandem
				os=
				;;
			stratus)
				basic_machine=i860-stratus
				os=sysv4
				;;
			sun2)
				basic_machine=m68000-sun
				os=
				;;

			sun2os3)
				basic_machine=m68000-sun
				os=sunos3
				;;
			sun2os4)
				basic_machine=m68000-sun
				os=sunos4
				;;
			sun3)
				basic_machine=m68k-sun
				os=
				;;
			sun3os3)
				basic_machine=m68k-sun
				os=sunos3
				;;
			sun3os4)
				basic_machine=m68k-sun
				os=sunos4
				;;
			sun4)
				basic_machine=sparc-sun
				os=
				;;
			sun4os3)
				basic_machine=sparc-sun
				os=sunos3
				;;
			sun4os4)
				basic_machine=sparc-sun
				os=sunos4
				;;
			sun4sol2)
				basic_machine=sparc-sun
				os=solaris2
				;;
			sun386 | sun386i | roadrunner)
				basic_machine=i386-sun
				os=
				;;
			sv1)
				basic_machine=sv1-cray
				os=unicos
				;;
			symmetry)
				basic_machine=i386-sequent
				os=dynix
				;;
			t3e)
				basic_machine=alphaev5-cray
				os=unicos
				;;
			t90)
				basic_machine=t90-cray
				os=unicos
				;;
			toad1)
				basic_machine=pdp10-xkl
				os=tops20
				;;
			tpf)
				basic_machine=s390x-ibm
				os=tpf
				;;
			udi29k)
				basic_machine=a29k-amd
				os=udi
				;;
			ultra3)
				basic_machine=a29k-nyu
				os=sym1
				;;
			v810 | necv810)
				basic_machine=v810-nec
				os=none
				;;
			vaxv)
				basic_machine=vax-dec
				os=sysv
				;;
			vms)
				basic_machine=vax-dec
				os=vms
				;;
			vxworks960)
				basic_machine=i960-wrs
				os=vxworks
				;;
			vxworks68)
				basic_machine=m68k-wrs
				os=vxworks
				;;
			vxworks29k)
				basic_machine=a29k-wrs
				os=vxworks
				;;
			xbox)
				basic_machine=i686-pc
				os=mingw32
				;;
			ymp)
				basic_machine=ymp-cray
				os=unicos
				;;
			*)
				basic_machine=$1
				os=
				;;
		esac
		;;
esac

# Decode 1-component or ad-hoc basic machines
case $basic_machine in
	# Here we handle the default manufacturer of certain CPU types.  It is in
	# some cases the only manufacturer, in others, it is the most popular.

	w89k)
		cpu=hppa1.1
		vendor=winbond

		;;
	op50n)
		cpu=hppa1.1
		vendor=oki
		;;
	op60c)


		cpu=hppa1.1
		vendor=oki
		;;
	ibm*)
		cpu=i370
		vendor=ibm
		;;
	orion105)
		cpu=clipper
		vendor=highlevel
		;;
	mac | mpw | mac-mpw)
		cpu=m68k
		vendor=apple
		;;
	pmac | pmac-mpw)
		cpu=powerpc
		vendor=apple
		;;

	# Recognize the various machine names and aliases which stand
	# for a CPU type and a company and sometimes even an OS.
	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)


		cpu=m68000
		vendor=att
		;;
	3b*)
		cpu=we32k

		vendor=att
		;;
	bluegene*)
		cpu=powerpc
		vendor=ibm
		os=cnk
		;;
	decsystem10* | dec10*)
		cpu=pdp10
		vendor=dec
		os=tops10
		;;
	decsystem20* | dec20*)
		cpu=pdp10
		vendor=dec
		os=tops20
		;;
	delta | 3300 | motorola-3300 | motorola-delta \
	      | 3300-motorola | delta-motorola)
		cpu=m68k
		vendor=motorola
		;;
	dpx2*)
		cpu=m68k
		vendor=bull
		os=sysv3
		;;
	encore | umax | mmax)

		cpu=ns32k
		vendor=encore
		;;
	elxsi)
		cpu=elxsi
		vendor=elxsi

		os=${os:-bsd}



		;;
	fx2800)
		cpu=i860


		vendor=alliant
		;;
	genix)
		cpu=ns32k
		vendor=ns
		;;
	h3050r* | hiux*)
		cpu=hppa1.1
		vendor=hitachi
		os=hiuxwe2
		;;
	hp3k9[0-9][0-9] | hp9[0-9][0-9])
		cpu=hppa1.0
		vendor=hp
		;;
	hp9k2[0-9][0-9] | hp9k31[0-9])
		cpu=m68000
		vendor=hp
		;;
	hp9k3[2-9][0-9])
		cpu=m68k
		vendor=hp
		;;
	hp9k6[0-9][0-9] | hp6[0-9][0-9])
		cpu=hppa1.0
		vendor=hp

		;;
	hp9k7[0-79][0-9] | hp7[0-79][0-9])
		cpu=hppa1.1
		vendor=hp

		;;
	hp9k78[0-9] | hp78[0-9])

		# FIXME: really hppa2.0-hp
		cpu=hppa1.1

		vendor=hp
		;;
	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
		# FIXME: really hppa2.0-hp
		cpu=hppa1.1
		vendor=hp
		;;
	hp9k8[0-9][13679] | hp8[0-9][13679])
		cpu=hppa1.1
		vendor=hp
		;;
	hp9k8[0-9][0-9] | hp8[0-9][0-9])
		cpu=hppa1.0
		vendor=hp
		;;
	i*86v32)

		cpu=`echo "$1" | sed -e 's/86.*/86/'`
		vendor=pc
		os=sysv32
		;;
	i*86v4*)

		cpu=`echo "$1" | sed -e 's/86.*/86/'`
		vendor=pc
		os=sysv4
		;;
	i*86v)
		cpu=`echo "$1" | sed -e 's/86.*/86/'`
		vendor=pc
		os=sysv
		;;
	i*86sol2)
		cpu=`echo "$1" | sed -e 's/86.*/86/'`
		vendor=pc
		os=solaris2
		;;
	j90 | j90-cray)
		cpu=j90
		vendor=cray
		os=${os:-unicos}
		;;
	iris | iris4d)
		cpu=mips
		vendor=sgi
		case $os in
		    irix*)
			;;
		    *)
			os=irix4
			;;
		esac
		;;
	miniframe)
		cpu=m68000
		vendor=convergent
		;;
	*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
		cpu=m68k
		vendor=atari
		os=mint
		;;
	news-3600 | risc-news)
		cpu=mips
		vendor=sony
		os=newsos
		;;
	next | m*-next)
		cpu=m68k
		vendor=next
		case $os in
		    nextstep* )
			;;
		    ns2*)
		      os=nextstep2
			;;
		    *)
		      os=nextstep3
			;;
		esac
		;;
	np1)
		cpu=np1
		vendor=gould
		;;
	op50n-* | op60c-*)
		cpu=hppa1.1
		vendor=oki
		os=proelf
		;;
	pa-hitachi)
		cpu=hppa1.1
		vendor=hitachi
		os=hiuxwe2
		;;
	pbd)
		cpu=sparc
		vendor=tti
		;;
	pbb)
		cpu=m68k
		vendor=tti
		;;
	pc532)
		cpu=ns32k
		vendor=pc532
		;;
	pn)
		cpu=pn
		vendor=gould
		;;
	power)
		cpu=power
		vendor=ibm
		;;
	ps2)
		cpu=i386
		vendor=ibm
		;;
	psp)
		cpu=mipsallegrexel
		vendor=psp
		;;
	rm[46]00)
		cpu=mips
		vendor=siemens
		;;
	rtpc | rtpc-*)
		cpu=romp
		vendor=ibm
		;;
	sde)
		cpu=mipsisa32
		vendor=sde
		os=${os:-elf}
		;;
	simso-wrs)
		cpu=sparclite
		vendor=wrs
		os=vxworks
		;;
	tower | tower-32)
		cpu=m68k
		vendor=ncr
		;;
	vpp*|vx|vx-*)
		cpu=f301
		vendor=fujitsu
		;;
	w65)
		cpu=w65
		vendor=wdc
		;;
	w89k-*)
		cpu=hppa1.1
		vendor=winbond
		os=proelf
		;;
	none)
		cpu=none
		vendor=none
		;;
	leon|leon[3-9])
		cpu=sparc


		vendor=$basic_machine

		;;


	leon-*|leon[3-9]-*)
		cpu=sparc
		vendor=`echo "$basic_machine" | sed 's/-.*//'`
		;;






	*-*)

		# shellcheck disable=SC2162


		IFS="-" read cpu vendor <<EOF

$basic_machine
EOF
		;;

	# We use `pc' rather than `unknown'
	# because (1) that's what they normally are, and
	# (2) the word "unknown" tends to confuse beginning users.
	i*86 | x86_64)
		cpu=$basic_machine
		vendor=pc
		;;


	# These rules are duplicated from below for sake of the special case above;
	# i.e. things that normalized to x86 arches should also default to "pc"
	pc98)
		cpu=i386
		vendor=pc
		;;
	x64 | amd64)
		cpu=x86_64
		vendor=pc
		;;
	# Recognize the basic CPU types without company name.
	*)







		cpu=$basic_machine









		vendor=unknown
		;;
esac







unset -v basic_machine

# Decode basic machines in the full and proper CPU-Company form.
case $cpu-$vendor in
	# Here we handle the default manufacturer of certain CPU types in canonical form. It is in
	# some cases the only manufacturer, in others, it is the most popular.
	craynv-unknown)
		vendor=cray
		os=${os:-unicosmp}
		;;
	c90-unknown | c90-cray)

		vendor=cray
		os=${os:-unicos}












		;;
	fx80-unknown)



		vendor=alliant
		;;
	romp-unknown)
		vendor=ibm

		;;
	mmix-unknown)

		vendor=knuth
		;;
	microblaze-unknown | microblazeel-unknown)
		vendor=xilinx
		;;
	rs6000-unknown)





		vendor=ibm





		;;
	vax-unknown)


		vendor=dec
		;;
	pdp11-unknown)
		vendor=dec
		;;
	we32k-unknown)

		vendor=att


		;;


	cydra-unknown)
		vendor=cydrome
		;;



	i370-ibm*)
		vendor=ibm
		;;
	orion-unknown)
		vendor=highlevel
		;;

	xps-unknown | xps100-unknown)
		cpu=xps100
		vendor=honeywell
		;;

	# Here we normalize CPU types with a missing or matching vendor



	dpx20-unknown | dpx20-bull)

		cpu=rs6000



		vendor=bull







		os=${os:-bosx}
		;;



	# Here we normalize CPU types irrespective of the vendor
	amd64-*)
		cpu=x86_64
		;;










































	blackfin-*)
		cpu=bfin
		os=linux
		;;




	c54x-*)
		cpu=tic54x
		;;
	c55x-*)
		cpu=tic55x
		;;
	c6x-*)













































































		cpu=tic6x


		;;
















	e500v[12]-*)
		cpu=powerpc
		os=$os"spe"
		;;

















































	mips3*-*)











































































		cpu=mips64


		;;
	ms1-*)


		cpu=mt











		;;
	m68knommu-*)
		cpu=m68k
		os=linux
		;;
	m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)

		cpu=s12z
		;;
	openrisc-*)

		cpu=or32
		;;
	parisc-*)
		cpu=hppa
		os=linux
		;;
	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
		cpu=i586


		;;

	pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
		cpu=i686

		;;
	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
		cpu=i686


		;;

	pentium4-*)



		cpu=i786
		;;
	pc98-*)
		cpu=i386
		;;
	ppc-* | ppcbe-*)
		cpu=powerpc
		;;
	ppcle-* | powerpclittle-*)

		cpu=powerpcle
		;;
	ppc64-*)
		cpu=powerpc64

		;;





	ppc64le-* | powerpc64little-*)
		cpu=powerpc64le

		;;
	sb1-*)



		cpu=mipsisa64sb1

		;;




	sb1el-*)
		cpu=mipsisa64sb1el

		;;


	sh5e[lb]-*)
		cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
		;;
	spur-*)

		cpu=spur
		;;

	strongarm-* | thumb-*)
		cpu=arm

		;;


	tx39-*)
		cpu=mipstx39
		;;
	tx39el-*)
		cpu=mipstx39el

		;;


	x64-*)
		cpu=x86_64
		;;



	xscale-* | xscalee[bl]-*)
		cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
		;;


	# Recognize the canonical CPU Types that limit and/or modify the
	# company names they are paired with.




	cr16-*)
		os=${os:-elf}
		;;



	crisv32-* | etraxfs*-*)
		cpu=crisv32
		vendor=axis
		;;

	cris-* | etrax*-*)
		cpu=cris

		vendor=axis


		;;


	crx-*)



		os=${os:-elf}



		;;
	neo-tandem)
		cpu=neo
		vendor=tandem
		;;
	nse-tandem)
		cpu=nse
		vendor=tandem
		;;
	nsr-tandem)
		cpu=nsr
		vendor=tandem
		;;
	nsv-tandem)
		cpu=nsv
		vendor=tandem
		;;
	nsx-tandem)
		cpu=nsx




























































		vendor=tandem































		;;





































	s390-*)
		cpu=s390






















		vendor=ibm

		;;























































	s390x-*)




















		cpu=s390x

		vendor=ibm
		;;
	tile*-*)

		os=${os:-linux-gnu}
		;;




































































































































	*)
		# Recognize the canonical CPU types that are allowed with any
		# company name.
		case $cpu in
			1750a | 580 \
			| a29k \
			| aarch64 | aarch64_be | arm64 \
			| abacus \
			| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
			| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
			| alphapca5[67] | alpha64pca5[67] \
			| am33_2.0 \
			| amdgcn \
			| arc | arceb \
			| arm  | arm[lb]e | arme[lb] | armv* \
			| avr | avr32 \
			| asmjs \
			| ba \
			| be32 | be64 \
			| bfin | bs2000 \
			| c[123]* | c30 | [cjt]90 | c4x \
			| c8051 | clipper | craynv | csky | cydra \
			| d10v | d30v | dlx | dsp16xx \
			| e2k | elxsi | epiphany \
			| f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
			| h8300 | h8500 \
			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
			| hexagon \
			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
			| ip2k | iq2000 \
			| k1om \
			| le32 | le64 \
			| lm32 \
			| m32c | m32r | m32rle \
			| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
			| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
			| m88110 | m88k | maxq | mb | mcore | mep | metag \
			| microblaze | microblazeel \
			| mips | mipsbe | mipseb | mipsel | mipsle \
			| mips16 \
			| mips64 | mips64eb | mips64el \
			| mips64octeon | mips64octeonel \
			| mips64orion | mips64orionel \
			| mips64r5900 | mips64r5900el \
			| mips64vr | mips64vrel \
			| mips64vr4100 | mips64vr4100el \
			| mips64vr4300 | mips64vr4300el \
			| mips64vr5000 | mips64vr5000el \
			| mips64vr5900 | mips64vr5900el \
			| mipsisa32 | mipsisa32el \
			| mipsisa32r2 | mipsisa32r2el \
			| mipsisa32r6 | mipsisa32r6el \
			| mipsisa64 | mipsisa64el \
			| mipsisa64r2 | mipsisa64r2el \
			| mipsisa64r6 | mipsisa64r6el \
			| mipsisa64sb1 | mipsisa64sb1el \
			| mipsisa64sr71k | mipsisa64sr71kel \
			| mipsr5900 | mipsr5900el \
			| mipstx39 | mipstx39el \
			| mmix \
			| mn10200 | mn10300 \
			| moxie \
			| mt \
			| msp430 \
			| nds32 | nds32le | nds32be \
			| nfp \
			| nios | nios2 | nios2eb | nios2el \
			| none | np1 | ns16k | ns32k | nvptx \
			| open8 \
			| or1k* \
			| or32 \
			| orion \
			| picochip \
			| pdp10 | pdp11 | pj | pjl | pn | power \
			| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
			| pru \
			| pyramid \
			| riscv | riscv32 | riscv64 \
			| rl78 | romp | rs6000 | rx \
			| score \
			| sh | shl \
			| sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
			| sh[1234]e[lb] |  sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
			| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
			| sparclite \
			| sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
			| spu \
			| tahoe \
			| tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
			| tron \
			| ubicom32 \
			| v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
			| vax \
			| visium \
			| w65 | wasm32 \
			| we32k \
			| x86 | x86_64 | xc16x | xgate | xps100 \
			| xstormy16 | xtensa* \
			| ymp \
			| z8k | z80)
				;;

			*)
				echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
				exit 1
				;;
		esac
		;;
esac

# Here we canonicalize certain aliases for manufacturers.
case $vendor in
	digital*)

		vendor=dec
		;;
	commodore*)
		vendor=cbm
		;;
	*)
		;;
esac

# Decode manufacturer-specific aliases for certain operating systems.

if [ x$os != x ]
then
case $os in
	# First match some system type aliases that might get confused
	# with valid system types.
	# solaris* is a basic system type, with this one exception.
	auroraux)
		os=auroraux
		;;
	bluegene*)
		os=cnk
		;;
	solaris1 | solaris1.*)
		os=`echo $os | sed -e 's|solaris1|sunos4|'`
		;;
	solaris)
		os=solaris2
		;;
	unixware*)
		os=sysv4.2uw
		;;
	gnu/linux*)
		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
		;;
	# es1800 is here to avoid being matched by es* (a different OS)
	es1800*)
		os=ose
		;;
	# Some version numbers need modification
	chorusos*)
		os=chorusos
		;;
	isc)
		os=isc2.2
		;;
	sco6)
		os=sco5v6
		;;
	sco5)
		os=sco3.2v5
		;;
	sco4)
		os=sco3.2v4
		;;
	sco3.2.[4-9]*)
		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
		;;
	sco3.2v[4-9]* | sco5v6*)
		# Don't forget version if it is 3.2v4 or newer.
		;;
	scout)
		# Don't match below
		;;
	sco*)
		os=sco3.2v2
		;;
	psos*)
		os=psos
		;;
	# Now accept the basic system types.
	# The portable systems comes first.
	# Each alternative MUST end in a * to match a version number.
	# sysv* is not here because it comes later, after sysvr4.
	gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
	     | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
	     | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
	     | sym* | kopensolaris* | plan9* \
	     | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
	     | aos* | aros* | cloudabi* | sortix* \
	     | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
	     | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
	     | knetbsd* | mirbsd* | netbsd* \
	     | bitrig* | openbsd* | solidbsd* | libertybsd* \
	     | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \
	     | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
	     | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
	     | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \
	     | chorusrdb* | cegcc* | glidix* \
	     | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
	     | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \
	     | linux-newlib* | linux-musl* | linux-uclibc* \
	     | uxpv* | beos* | mpeix* | udk* | moxiebox* \
	     | interix* | uwin* | mks* | rhapsody* | darwin* \
	     | openstep* | oskit* | conix* | pw32* | nonstopux* \
	     | storm-chaos* | tops10* | tenex* | tops20* | its* \
	     | os2* | vos* | palmos* | uclinux* | nucleus* \
	     | morphos* | superux* | rtmk* | windiss* \
	     | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
	     | skyos* | haiku* | rdos* | toppers* | drops* | es* \
	     | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
	     | midnightbsd* | amdhsa* | unleashed* | emscripten*)
	# Remember, each alternative MUST END IN *, to match a version number.
		;;
	qnx*)
		case $cpu in
		    x86 | i*86)
			;;
		    *)
			os=nto-$os
			;;
		esac
		;;
	hiux*)
		os=hiuxwe2
		;;
	nto-qnx*)
		;;
	nto*)
		os=`echo $os | sed -e 's|nto|nto-qnx|'`
		;;
	sim | xray | os68k* | v88r* \
	    | windows* | osx | abug | netware* | os9* \
	    | macos* | mpw* | magic* | mmixware* | mon960* | lnews*)
		;;



	linux-dietlibc)
		os=linux-dietlibc
		;;
	linux*)
		os=`echo $os | sed -e 's|linux|linux-gnu|'`
		;;
	lynx*178)
		os=lynxos178
		;;
	lynx*5)
		os=lynxos5
		;;
	lynx*)
		os=lynxos
		;;
	mac*)
		os=`echo "$os" | sed -e 's|mac|macos|'`
		;;
	opened*)
		os=openedition
		;;
	os400*)
		os=os400
		;;
	sunos5*)
		os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
		;;
	sunos6*)
		os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
		;;






	wince*)
		os=wince
		;;
	utek*)
		os=bsd
		;;
	dynix*)
		os=bsd
		;;
	acis*)
		os=aos
		;;
	atheos*)
		os=atheos
		;;
	syllable*)
		os=syllable
		;;
	386bsd)
		os=bsd
		;;
	ctix* | uts*)
		os=sysv
		;;
	nova*)
		os=rtmk-nova
		;;
	ns2)
		os=nextstep2
		;;
	nsk*)
		os=nsk
		;;
	# Preserve the version number of sinix5.
	sinix5.*)
		os=`echo $os | sed -e 's|sinix|sysv|'`
		;;
	sinix*)
		os=sysv4
		;;
	tpf*)
		os=tpf
		;;
	triton*)
		os=sysv3
		;;
	oss*)
		os=sysv3
		;;
	svr4*)
		os=sysv4
		;;
	svr3)
		os=sysv3
		;;
	sysvr4)
		os=sysv4
		;;
	# This must come after sysvr4.
	sysv*)
		;;
	ose*)
		os=ose
		;;
	*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
		os=mint
		;;
	zvmoe)
		os=zvmoe
		;;
	dicos*)
		os=dicos
		;;
	pikeos*)
		# Until real need of OS specific support for
		# particular features comes up, bare metal
		# configurations are quite functional.
		case $cpu in
		    arm*)
			os=eabi
			;;
		    *)
			os=elf
			;;
		esac
		;;
	nacl*)
		;;
	ios)
		;;
	none)
		;;
	*-eabi)
		;;
	*)


		echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
		exit 1
		;;
esac
else

# Here we handle the default operating systems that come with various machines.
# The value should be what the vendor currently ships out the door with their
# machine or put another way, the most popular os provided with the machine.

# Note that if you're going to try to match "-MANUFACTURER" here (say,
# "-sun"), then you have to tell the case statement up towards the top
# that MANUFACTURER isn't an operating system.  Otherwise, code above
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.

case $cpu-$vendor in
	score-*)
		os=elf
		;;
	spu-*)
		os=elf
		;;
	*-acorn)
		os=riscix1.2
		;;
	arm*-rebel)
		os=linux
		;;
	arm*-semi)
		os=aout
		;;
	c4x-* | tic4x-*)
		os=coff
		;;
	c8051-*)
		os=elf
		;;
	clipper-intergraph)
		os=clix
		;;
	hexagon-*)
		os=elf
		;;
	tic54x-*)
		os=coff
		;;
	tic55x-*)
		os=coff
		;;
	tic6x-*)
		os=coff
		;;
	# This must come before the *-dec entry.
	pdp10-*)
		os=tops20
		;;
	pdp11-*)
		os=none
		;;
	*-dec | vax-*)
		os=ultrix4.2
		;;
	m68*-apollo)
		os=domain
		;;
	i386-sun)
		os=sunos4.0.2
		;;
	m68000-sun)
		os=sunos3
		;;
	m68*-cisco)
		os=aout
		;;
	mep-*)
		os=elf
		;;
	mips*-cisco)
		os=elf
		;;
	mips*-*)
		os=elf
		;;
	or32-*)
		os=coff
		;;
	*-tti)	# must be before sparc entry or we get the wrong os.
		os=sysv3
		;;
	sparc-* | *-sun)
		os=sunos4.1.1
		;;
	pru-*)
		os=elf
		;;
	*-be)
		os=beos
		;;
	*-ibm)
		os=aix
		;;
	*-knuth)
		os=mmixware
		;;
	*-wec)
		os=proelf
		;;
	*-winbond)
		os=proelf
		;;
	*-oki)
		os=proelf
		;;
	*-hp)
		os=hpux
		;;
	*-hitachi)
		os=hiux
		;;
	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
		os=sysv
		;;
	*-cbm)
		os=amigaos
		;;
	*-dg)
		os=dgux
		;;
	*-dolphin)
		os=sysv3
		;;
	m68k-ccur)
		os=rtu
		;;
	m88k-omron*)
		os=luna
		;;
	*-next)
		os=nextstep
		;;
	*-sequent)
		os=ptx
		;;
	*-crds)
		os=unos
		;;
	*-ns)
		os=genix
		;;
	i370-*)
		os=mvs
		;;
	*-gould)
		os=sysv
		;;
	*-highlevel)
		os=bsd
		;;
	*-encore)
		os=bsd
		;;
	*-sgi)
		os=irix
		;;
	*-siemens)
		os=sysv4
		;;
	*-masscomp)
		os=rtu
		;;
	f30[01]-fujitsu | f700-fujitsu)
		os=uxpv
		;;
	*-rom68k)
		os=coff
		;;
	*-*bug)
		os=coff
		;;
	*-apple)
		os=macos
		;;
	*-atari*)
		os=mint
		;;
	*-wrs)
		os=vxworks
		;;
	*)
		os=none
		;;
esac
fi

# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer.  We pick the logical manufacturer.
case $vendor in

	unknown)
		case $os in
			riscix*)
				vendor=acorn
				;;
			sunos*)
				vendor=sun
				;;
			cnk*|-aix*)
				vendor=ibm
				;;
			beos*)
				vendor=be
				;;
			hpux*)
				vendor=hp
				;;
			mpeix*)
				vendor=hp
				;;
			hiux*)
				vendor=hitachi
				;;
			unos*)
				vendor=crds
				;;
			dgux*)
				vendor=dg
				;;
			luna*)
				vendor=omron
				;;
			genix*)
				vendor=ns
				;;
			clix*)
				vendor=intergraph
				;;
			mvs* | opened*)
				vendor=ibm
				;;
			os400*)
				vendor=ibm
				;;
			ptx*)
				vendor=sequent
				;;
			tpf*)
				vendor=ibm
				;;
			vxsim* | vxworks* | windiss*)
				vendor=wrs
				;;
			aux*)
				vendor=apple
				;;
			hms*)
				vendor=hitachi
				;;
			mpw* | macos*)
				vendor=apple
				;;
			*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
				vendor=atari
				;;
			vos*)
				vendor=stratus
				;;
		esac

		;;
esac

echo "$cpu-$vendor-$os"
exit

# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:

Modified build-aux/install-sh from [4fa2988af1] to [0170726bd8].

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
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
#!/bin/sh
# install - install a program, script, or datafile


scriptversion=2013-12-25.23; # UTC



# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.



#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.

tab='	'
nl='
'
IFS=" $tab$nl"

# Set DOITPROG to "echo" to test this script.

doit=${DOITPROG-}
doit_exec=${doit:-exec}

# Put in absolute file names if you don't have them in your path;
# or use environment vars.



chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}

posix_mkdir=

# Desired mode of installed file.

mode=0755

chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog

rmcmd="$rmprog -f"
stripcmd=

src=
dst=
dir_arg=
dst_arg=

copy_on_change=false
is_target_a_directory=possibly

usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
   or: $0 [OPTION]... SRCFILES... DIRECTORY
   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
   or: $0 [OPTION]... -d DIRECTORIES...

In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.

Options:
     --help     display this help and exit.
     --version  display version info and exit.

  -c            (ignored)
  -C            install only if different (preserve the last data modification time)
  -d            create directories instead of installing files.
  -g GROUP      $chgrpprog installed files to GROUP.
  -m MODE       $chmodprog installed files to MODE.
  -o USER       $chownprog installed files to USER.
  -s            $stripprog installed files.
  -t DIRECTORY  install into DIRECTORY.
  -T            report an error if DSTFILE is a directory.

Environment variables override the default commands:
  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
  RMPROG STRIPPROG
"

while test $# -ne 0; do
  case $1 in
    -c) ;;

    -C) copy_on_change=true;;




    -d) dir_arg=true;;

    -g) chgrpcmd="$chgrpprog $2"
        shift;;

    --help) echo "$usage"; exit $?;;

    -m) mode=$2
        case $mode in
          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
            echo "$0: invalid mode: $mode" >&2
            exit 1;;
        esac
        shift;;


    -o) chowncmd="$chownprog $2"
        shift;;

    -s) stripcmd=$stripprog;;


    -t)
        is_target_a_directory=always
        dst_arg=$2
        # Protect names problematic for 'test' and other utilities.
        case $dst_arg in
          -* | [=\(\)!]) dst_arg=./$dst_arg;;
        esac

        shift;;

    -T) is_target_a_directory=never;;

    --version) echo "$0 $scriptversion"; exit $?;;

    --) shift
        break;;


    -*) echo "$0: invalid option: $1" >&2
        exit 1;;

    *)  break;;
  esac
  shift
done



# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.


if test -n "$dir_arg"; then
  if test -n "$dst_arg"; then
    echo "$0: target directory not allowed when installing a directory." >&2
    exit 1
  fi
fi

if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
  # When -d is used, all remaining arguments are directories to create.
  # When -t is used, the destination is already specified.
  # Otherwise, the last argument is the destination.  Remove it from $@.

  for arg

  do

    if test -n "$dst_arg"; then
      # $@ is not empty: it contains at least $arg.
      set fnord "$@" "$dst_arg"
      shift # fnord

    fi


    shift # arg
    dst_arg=$arg
    # Protect names problematic for 'test' and other utilities.
    case $dst_arg in
      -* | [=\(\)!]) dst_arg=./$dst_arg;;
    esac
  done
fi

if test $# -eq 0; then
  if test -z "$dir_arg"; then
    echo "$0: no input file specified." >&2
    exit 1
  fi
  # It's OK to call 'install-sh -d' without argument.
  # This can happen when creating conditional directories.
  exit 0
fi

if test -z "$dir_arg"; then
  if test $# -gt 1 || test "$is_target_a_directory" = always; then
    if test ! -d "$dst_arg"; then
      echo "$0: $dst_arg: Is not a directory." >&2
      exit 1
    fi
  fi
fi

if test -z "$dir_arg"; then
  do_exit='(exit $ret); exit $ret'
  trap "ret=129; $do_exit" 1
  trap "ret=130; $do_exit" 2
  trap "ret=141; $do_exit" 13
  trap "ret=143; $do_exit" 15

  # Set umask so as not to create temps with too-generous modes.
  # However, 'strip' requires both read and write access to temps.
  case $mode in
    # Optimize common cases.
    *644) cp_umask=133;;
    *755) cp_umask=22;;

    *[0-7])
      if test -z "$stripcmd"; then
        u_plus_rw=

      else
        u_plus_rw='% 200'


      fi
      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
    *)
      if test -z "$stripcmd"; then
        u_plus_rw=
      else
        u_plus_rw=,u+rw
      fi
      cp_umask=$mode$u_plus_rw;;
  esac

fi

for src
do
  # Protect names problematic for 'test' and other utilities.



  case $src in
    -* | [=\(\)!]) src=./$src;;
  esac

  if test -n "$dir_arg"; then
    dst=$src
    dstdir=$dst
    test -d "$dstdir"
    dstdir_status=$?
  else


    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
    # might cause directories to be created, which would be especially bad
    # if $src (and thus $dsttmp) contains '*'.
    if test ! -f "$src" && test ! -d "$src"; then
      echo "$0: $src does not exist." >&2
      exit 1
    fi

    if test -z "$dst_arg"; then
      echo "$0: no destination specified." >&2
      exit 1
    fi
    dst=$dst_arg

    # If destination is a directory, append the input filename; won't work
    # if double slashes aren't ignored.
    if test -d "$dst"; then


      if test "$is_target_a_directory" = never; then
        echo "$0: $dst_arg: Is a directory" >&2
        exit 1
      fi
      dstdir=$dst
      dst=$dstdir/`basename "$src"`

      dstdir_status=0
    else
      dstdir=`dirname "$dst"`
      test -d "$dstdir"
      dstdir_status=$?
    fi
  fi

  obsolete_mkdir_used=false

  if test $dstdir_status != 0; then
    case $posix_mkdir in
      '')
        # Create intermediate dirs using mode 755 as modified by the umask.
        # This is like FreeBSD 'install' as of 1997-10-28.
        umask=`umask`
        case $stripcmd.$umask in
          # Optimize common cases.

          *[2367][2367]) mkdir_umask=$umask;;
          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;

          *[0-7])
            mkdir_umask=`expr $umask + 22 \
              - $umask % 100 % 40 + $umask % 20 \
              - $umask % 10 % 4 + $umask % 2
            `;;
          *) mkdir_umask=$umask,go-w;;
        esac

        # With -d, create the new directory with the user-specified mode.
        # Otherwise, rely on $mkdir_umask.
        if test -n "$dir_arg"; then
          mkdir_mode=-m$mode
        else
          mkdir_mode=
        fi



        posix_mkdir=false
        case $umask in


          *[123567][0-7][0-7])
            # POSIX mkdir -p sets u+wx bits regardless of umask, which
            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
            ;;
          *)
            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
            trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0



            if (umask $mkdir_umask &&
                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
            then


              if test -z "$dir_arg" || {
                   # Check for POSIX incompatibilities with -m.
                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
                   # other-writable bit of parent directory when it shouldn't.
                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
                   ls_ld_tmpdir=`ls -ld "$tmpdir"`
                   case $ls_ld_tmpdir in
                     d????-?r-*) different_mode=700;;
                     d????-?--*) different_mode=755;;
                     *) false;;
                   esac &&
                   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
                     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
                   }

                 }
              then posix_mkdir=:


              fi
              rmdir "$tmpdir/d" "$tmpdir"

            else
              # Remove any dirs left behind by ancient mkdir implementations.
              rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
            fi
            trap '' 0;;
        esac;;



    esac

    if
      $posix_mkdir && (
        umask $mkdir_umask &&
        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
      )
    then :
    else

      # The umask is ridiculous, or mkdir does not conform to POSIX,
      # or it failed possibly due to a race condition.  Create the
      # directory the slow way, step by step, checking for races as we go.



      case $dstdir in
        /*) prefix='/';;
        [-=\(\)!]*) prefix='./';;
        *)  prefix='';;
      esac

      oIFS=$IFS
      IFS=/

      set -f
      set fnord $dstdir
      shift
      set +f
      IFS=$oIFS

      prefixes=

      for d
      do
        test X"$d" = X && continue

        prefix=$prefix$d
        if test -d "$prefix"; then
          prefixes=
        else
          if $posix_mkdir; then
            (umask=$mkdir_umask &&
             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
            # Don't fail if two instances are running concurrently.

            test -d "$prefix" || exit 1
          else
            case $prefix in
              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
              *) qprefix=$prefix;;
            esac
            prefixes="$prefixes '$qprefix'"
          fi
        fi
        prefix=$prefix/
      done

      if test -n "$prefixes"; then
        # Don't fail if two instances are running concurrently.
        (umask $mkdir_umask &&
         eval "\$doit_exec \$mkdirprog $prefixes") ||
          test -d "$dstdir" || exit 1
        obsolete_mkdir_used=true
      fi
    fi
  fi

  if test -n "$dir_arg"; then
    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
  else


    # Make a couple of temp file names in the proper directory.
    dsttmp=$dstdir/_inst.$$_
    rmtmp=$dstdir/_rm.$$_

    # Trap to clean up those temp files at exit.


    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0



    # Copy the file name to the temp name.
    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&

    # and set any options; do chmod last to preserve setuid bits.
    #
    # If any of these fail, we abort the whole thing.  If we want to
    # ignore errors from any of these, just make sure not to ignore
    # errors from the above "$doit $cpprog $src $dsttmp" command.





    #
    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&

    # If -C, don't bother to copy if it wouldn't change the file.
    if $copy_on_change &&
       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
       set -f &&
       set X $old && old=:$2:$4:$5:$6 &&
       set X $new && new=:$2:$4:$5:$6 &&
       set +f &&
       test "$old" = "$new" &&
       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
    then
      rm -f "$dsttmp"
    else
      # Rename the file to the real destination.
      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||


      # The rename failed, perhaps because mv can't rename something else
      # to itself, or perhaps because mv is so ancient that it does not
      # support -f.



      {
        # Now remove or move aside any old file at destination location.
        # We try this two ways since rm can't unlink itself on some
        # systems and the destination file might be busy for other




        # reasons.  In this case, the final cleanup might fail but the new
        # file should still install successfully.
        {
          test ! -f "$dst" ||



          $doit $rmcmd -f "$dst" 2>/dev/null ||
          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
          } ||
          { echo "$0: cannot unlink or rename $dst" >&2
            (exit 1); exit 1
          }
        } &&

        # Now rename the file to the real destination.
        $doit $mvcmd "$dsttmp" "$dst"
      }
    fi || exit 1

    trap '' 0
  fi
done

# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

<
|
>
|
>
|
>
|
<
<

|

|
|
<
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<
|
|
<
>
>
>

<
<
<

|





|
<
|
<
|
|

<
<

|
<

>
>
|
|
|
|
|
<
<
|
|

<
|
|
>
|
|
|
<
|
<
>

|
|
|
|
|
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<
<
|
<
|
|
|
|

>
>
>
|

|
|
|
<
<
|
<
<
<
<
|
|
>

|
|
|
<
>

|
<
<
<
<
<
<
>
|
|
<

<
|
|
<
|
>
<
<

|
|
|
|

>
>
|
<

>
|
<
<
|
|
|

<
<
<
<
>
|
>
|
>
|
<
|
|
>
|
>
>
|
<
<
|
<

|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
|
<
<
|
<
<
<
<
<
<
|
<
|
<
>
|
<
>
>
|
<
<
<
<
<
<
|
<
|
>


|
<
|
>
>
>
|
|
<

|
|
|
<
<
|
>

|
|
|
<
<
<
<
<
<
<
<
<
<

<
|
|
>
>
|
|
|
<
<
|
>
|
|
<
<
|
|
|
|
<
|
<
<
<
<
<
<
<
<
>
<
<

<
<
<
<
<
<
<
|
<
<
|
<
|
<
|
>

>
|
|
>
>
|
|
<
<
<
|
<
>
|
>
|
|
|
>
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
|
>
|
<
>
>
|
|
>
|
<
|
<
<
<
>
>
>
<

<
<
<
<
<
<
|
|
<
<
<
>

>
|
<
<
|
<

<
<
>
|
|
|
|
<
|
<

|
<
<
|
<
|
|
|
|
<
|
<
>
|
|
<
<
<
<
<
|
|
<
<

|
<
|
<
<
<
<
<
<

<
<
<
<
<
<
>

<
|
<

|
>
>
|
|
>
>
|
<
|
<
<
<
<
<
>
>
>
>
>
|
|
<
<
<

<
<
<
<
<
<
<
<
<
<
<
|
|
<
|

>
|
<
<
>
>
>
|
<
<
<
>
>
>
>
|
|
|
|
>
>
>
|
<
<
<
<
<
<
|
<
<
|
<
|

<
<
|

|
<
<
<
<
<
|
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
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
#!/bin/sh

#
# $NetBSD: install-sh.in,v 1.6 2012/01/11 13:07:31 hans Exp $
# This script now also installs multiple files, but might choke on installing
# multiple files with spaces in the file names.
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).


#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that


# the above copyright notice appear in all copies and that both that


# copyright notice and this permission notice appear in supporting










# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,

# written prior permission.  M.I.T. makes no representations about the
# suitability of this software for any purpose.  It is provided "as is"
# without express or implied warranty.
#



# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.

# set DOITPROG to echo to test this script



# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"




# put in absolute paths if you don't have them in your path; or use env. vars.


awkprog="${AWKPROG-awk}"
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"


rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"


instcmd="$cpprog"
instflags=""
pathcompchmodcmd="$chmodprog 755"
chmodcmd="$chmodprog 755"
chowncmd=""
chgrpcmd=""

stripcmd=""

stripflags=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
msrc=""
dst=""
dir_arg=""

suffix=""


suffixfmt=""




























while [ x"$1" != x ]; do

    case $1 in
	-b) suffix=".old"
	    shift
	    continue;;

	-B) suffixfmt="$2"
	    shift
	    shift
	    continue;;

	-c) instcmd="$cpprog"
	    shift
	    continue;;







	-d) dir_arg=true
	    shift
	    continue;;

	-m) chmodcmd="$chmodprog $2"
	    shift
	    shift

	    continue;;

	-m*)






	    chmodcmd="$chmodprog ${1#-m}"
	    shift
	    continue;;



	-o) chowncmd="$chownprog $2"
	    shift

	    shift
	    continue;;



	-g) chgrpcmd="$chgrpprog $2"
	    shift
	    shift
	    continue;;

	-s) stripcmd="$stripprog"
	    shift
	    continue;;


	-S) stripcmd="$stripprog"
	    stripflags="-S $2 $stripflags"


	    shift
	    shift
	    continue;;





	-p) instflags="-p"
	    shift
	    continue;;

	*)  if [ x"$msrc" = x ]
	    then

		msrc="$dst"
	    else
		msrc="$msrc $dst"
	    fi
	    src="$dst"
	    dst="$1"
	    shift


	    continue;;

    esac
done





















if [ x"$dir_arg" = x ]
then
	dstisfile=""
	if [ ! -d "$dst" ]


	then






		if [ x"$msrc" = x"$src" ]

		then

			dstisfile=true
		else

			echo "install: destination is not a directory"
			exit 1
		fi






	fi

else
	msrc="$msrc $dst"
fi

if [ x"$msrc" = x ]

then
	echo "install: no destination specified"
	exit 1
fi      

for srcarg in $msrc; do


if [ x"$dir_arg" != x ]; then

	dstarg="$srcarg"


else
	dstarg="$dst"

# Waiting for this to be detected by the "$instcmd $srcarg $dsttmp" command
# might cause directories to be created, which would be especially bad 
# if $src (and thus $dsttmp) contains '*'.












	if [ -f "$srcarg" ]
	then
		doinst="$instcmd $instflags"
	elif [ -d "$srcarg" ]
	then
		echo "install: $srcarg: not a regular file"
		exit 1


	elif [ "$srcarg" = "/dev/null" ]
	then
		doinst="$cpprog"
	else


		echo "install:  $srcarg does not exist"
		exit 1
	fi
	

# If destination is a directory, append the input filename; if your system








# does not like double slashes in filenames, you may need to add some logic










	if [ -d "$dstarg" ]


	then

		dstarg="$dstarg"/`basename "$srcarg"`

	fi
fi

## this sed command emulates the dirname command
dstdir=`echo "$dstarg" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`

# Make sure that the destination directory exists.
#  this part is taken from Noah Friedman's mkinstalldirs script

# Skip lots of stat calls in the usual case.



if [ ! -d "$dstdir" ]; then

defaultIFS='	
'
IFS="${IFS-${defaultIFS}}"

oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"














pathcomp=''


while [ $# -ne 0 ] ; do
	pathcomp="${pathcomp}${1}"
	shift

	if [ ! -d "${pathcomp}" ] ;
        then

		$doit $mkdirprog "${pathcomp}"



        	if [ x"$chowncmd" != x ]; then $doit $chowncmd "${pathcomp}"; else true ; fi &&
        	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "${pathcomp}"; else true ; fi &&
        	if [ x"$pathcompchmodcmd" != x ]; then $doit $pathcompchmodcmd "${pathcomp}"; else true ; fi








	else
		true



	fi

	pathcomp="${pathcomp}/"
done


fi




	if [ x"$dir_arg" != x ]
	then
		if [ -d "$dstarg" ]; then
			true
		else

			$doit $mkdirprog "$dstarg" &&


			if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dstarg"; else true ; fi &&


			if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dstarg"; else true ; fi &&

			if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dstarg"; else true ; fi
		fi
	else


		if [ x"$dstisfile" = x ]

		then
			file=$srcarg
		else





			file=$dst
		fi



		dstfile=`basename "$file"`

		dstfinal="$dstdir/$dstfile"













# Make a temp file name in the proper directory.


		dsttmp=$dstdir/#inst.$$#


# Make a backup file name in the proper directory.
		case x$suffixfmt in
		*%*)	suffix=`echo x |
			$awkprog -v bname="$dstfinal" -v fmt="$suffixfmt" '
			{ cnt = 0;
			  do {
				sfx = sprintf(fmt, cnt++);
				name = bname sfx;

			  } while (system("test -f " name) == 0);





			  print sfx; }' -`;;
		x)	;;
		*)	suffix="$suffixfmt";;
		esac
		dstbackup="$dstfinal$suffix"

# Move or copy the file name to the temp name















		$doit $doinst $srcarg "$dsttmp" &&


		trap "rm -f ${dsttmp}" 0 &&

# and set any options; do chmod last to preserve setuid bits



# If any of these fail, we abort the whole thing.  If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.




		if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else true;fi &&
		if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else true;fi &&
		if [ x"$stripcmd" != x ]; then $doit $stripcmd $stripflags "$dsttmp"; else true;fi &&
		if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else true;fi &&

# Now rename the file to the real destination.

		if [ x"$suffix" != x ] && [ -f "$dstfinal" ]
		then
			$doit $mvcmd "$dstfinal" "$dstbackup"
		else
			$doit $rmcmd -f "$dstfinal"






		fi &&


		$doit $mvcmd "$dsttmp" "$dstfinal"

	fi



done &&







exit 0

Modified build-aux/m4/buildsys.m4 from [74391b1263] to [824bccf73b].

1
2
3
4
5
6
7
8
9
10
dnl
dnl Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017,
dnl               2018, 2020
dnl   Jonathan Schleifer <js@nil.im>
dnl
dnl https://fossil.nil.im/buildsys
dnl
dnl Permission to use, copy, modify, and/or distribute this software for any
dnl purpose with or without fee is hereby granted, provided that the above
dnl copyright notice and this permission notice is present in all copies.


|







1
2
3
4
5
6
7
8
9
10
dnl
dnl Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017,
dnl               2018, 2020, 2021
dnl   Jonathan Schleifer <js@nil.im>
dnl
dnl https://fossil.nil.im/buildsys
dnl
dnl Permission to use, copy, modify, and/or distribute this software for any
dnl purpose with or without fee is hereby granted, provided that the above
dnl copyright notice and this permission notice is present in all copies.
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
dnl POSSIBILITY OF SUCH DAMAGE.
dnl

AC_DEFUN([BUILDSYS_INIT], [
	AC_REQUIRE([AC_CANONICAL_BUILD])
	AC_REQUIRE([AC_CANONICAL_HOST])




	case "$build_os" in
		darwin*)
			case "$host_os" in
				darwin*)
					AC_SUBST(BUILD_AND_HOST_ARE_DARWIN, yes)
					;;
			esac
			;;







	esac

	AC_CONFIG_COMMANDS_PRE([
		AS_IF([test x"$GCC" = x"yes"],
			[AC_SUBST(DEP_CFLAGS, '-MD -MF $${out%.o}.dep')])
		AS_IF([test x"$GXX" = x"yes"],
			[AC_SUBST(DEP_CXXFLAGS, '-MD -MF $${out%.o}.dep')])
		AS_IF([test x"$GOBJC" = x"yes"],
			[AC_SUBST(DEP_OBJCFLAGS, '-MD -MF $${out%.o}.dep')])
		AS_IF([test x"$GOBJCXX" = x"yes"],
			[AC_SUBST(DEP_OBJCXXFLAGS, '-MD -MF $${out%.o}.dep')])

		AC_SUBST(AMIGA_LIB_CFLAGS)
		AC_SUBST(AMIGA_LIB_LDFLAGS)









		AC_PATH_PROG(TPUT, tput)



		AS_IF([test x"$TPUT" != x""], [
			if x=$($TPUT el 2>/dev/null); then
				AC_SUBST(TERM_EL, "$x")
			else
				AC_SUBST(TERM_EL, "$($TPUT ce 2>/dev/null)")
			fi







>
>
>

|
|
|
|
|
|
|
>
>
>
>
>
>
>















>
>
>
>
>
>
>
>
|
>
>







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
dnl POSSIBILITY OF SUCH DAMAGE.
dnl

AC_DEFUN([BUILDSYS_INIT], [
	AC_REQUIRE([AC_CANONICAL_BUILD])
	AC_REQUIRE([AC_CANONICAL_HOST])

	AC_ARG_ENABLE(rpath,
		AS_HELP_STRING([--disable-rpath], [do not use rpath]))

	case "$build_os" in
	darwin*)
		case "$host_os" in
		darwin*)
			AC_SUBST(BUILD_AND_HOST_ARE_DARWIN, yes)
			;;
		esac
		;;
	esac

	AC_PROG_INSTALL
	case "$INSTALL" in
	./build-aux/install-sh*)
		INSTALL="$PWD/$INSTALL"
		;;
	esac

	AC_CONFIG_COMMANDS_PRE([
		AS_IF([test x"$GCC" = x"yes"],
			[AC_SUBST(DEP_CFLAGS, '-MD -MF $${out%.o}.dep')])
		AS_IF([test x"$GXX" = x"yes"],
			[AC_SUBST(DEP_CXXFLAGS, '-MD -MF $${out%.o}.dep')])
		AS_IF([test x"$GOBJC" = x"yes"],
			[AC_SUBST(DEP_OBJCFLAGS, '-MD -MF $${out%.o}.dep')])
		AS_IF([test x"$GOBJCXX" = x"yes"],
			[AC_SUBST(DEP_OBJCXXFLAGS, '-MD -MF $${out%.o}.dep')])

		AC_SUBST(AMIGA_LIB_CFLAGS)
		AC_SUBST(AMIGA_LIB_LDFLAGS)

		case "$build_os" in
		morphos*)
			dnl Don't use tput on MorphOS: The colored output is
			dnl quite unreadable and in some MorphOS versions the
			dnl output from tput is not 8-bit safe, with awk (for
			dnl AC_SUBST) failing as a result.
			;;
		*)
			AC_PATH_PROG(TPUT, tput)
			;;
		esac

		AS_IF([test x"$TPUT" != x""], [
			if x=$($TPUT el 2>/dev/null); then
				AC_SUBST(TERM_EL, "$x")
			else
				AC_SUBST(TERM_EL, "$($TPUT ce 2>/dev/null)")
			fi
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
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
			fi
		])
	])
])

AC_DEFUN([BUILDSYS_CHECK_IOS], [
	case "$host_os" in
		darwin*)
			AC_MSG_CHECKING(whether host is iOS)
			AC_EGREP_CPP(yes, [
				#include <TargetConditionals.h>

				#if (defined(TARGET_OS_IPHONE) && \
				    TARGET_OS_IPHONE) || \
				    (defined(TARGET_OS_SIMULATOR) && \
				    TARGET_OS_SIMULATOR)
				yes
				#endif
			], [
				host_is_ios="yes"
			], [
				host_is_ios="no"
			])
			AC_MSG_RESULT($host_is_ios)
			;;
	esac
])

AC_DEFUN([BUILDSYS_PROG_IMPLIB], [
	AC_REQUIRE([AC_CANONICAL_HOST])
	AC_MSG_CHECKING(whether we need an implib)
	case "$host_os" in
		cygwin* | mingw*)
			AC_MSG_RESULT(yes)
			PROG_IMPLIB_NEEDED='yes'
			PROG_IMPLIB_LDFLAGS='-Wl,--export-all-symbols,--out-implib,lib${PROG}.a'
			;;
		*)
			AC_MSG_RESULT(no)
			PROG_IMPLIB_NEEDED='no'
			PROG_IMPLIB_LDFLAGS=''
			;;
	esac

	AC_SUBST(PROG_IMPLIB_NEEDED)
	AC_SUBST(PROG_IMPLIB_LDFLAGS)
])

AC_DEFUN([BUILDSYS_SHARED_LIB], [
	AC_REQUIRE([AC_CANONICAL_HOST])
	AC_REQUIRE([BUILDSYS_CHECK_IOS])
	AC_MSG_CHECKING(for shared library system)

	case "$host_os" in
		darwin*)
			AC_MSG_RESULT(Darwin)
			LIB_CFLAGS='-fPIC -DPIC'
			LIB_LDFLAGS='-dynamiclib -current_version ${LIB_MAJOR}.${LIB_MINOR} -compatibility_version ${LIB_MAJOR}'
			LIB_LDFLAGS_INSTALL_NAME='-Wl,-install_name,${libdir}/$${out%.dylib}.${LIB_MAJOR}.dylib'
			LIB_PREFIX='lib'
			LIB_SUFFIX='.dylib'

			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'

			PLUGIN_CFLAGS='-fPIC -DPIC'
			PLUGIN_LDFLAGS='-bundle ${PLUGIN_LDFLAGS_BUNDLE_LOADER}'
			PLUGIN_SUFFIX='.bundle'
			AS_IF([test x"$host_is_ios" = x"yes"], [
				LINK_PLUGIN='rm -fr $$out && ${MKDIR_P} $$out && if test -f Info.plist; then ${INSTALL} -m 644 Info.plist $$out/Info.plist; fi && ${LD} -o $$out/$${out%${PLUGIN_SUFFIX}} ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS} && ${CODESIGN} -fs ${CODESIGN_IDENTITY} --timestamp=none $$out'
			], [
				LINK_PLUGIN='rm -fr $$out && ${MKDIR_P} $$out/Contents/MacOS && if test -f Info.plist; then ${INSTALL} -m 644 Info.plist $$out/Contents/Info.plist; fi && ${LD} -o $$out/Contents/MacOS/$${out%${PLUGIN_SUFFIX}} ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS} && ${CODESIGN} -fs ${CODESIGN_IDENTITY} --timestamp=none $$out'
			])
			INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib && ${LN_S} -f $${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.dylib && ${LN_S} -f $${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib ${DESTDIR}${libdir}/$$i'
			UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.dylib ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib'
			INSTALL_PLUGIN='&& rm -fr ${DESTDIR}${plugindir}/$$i && cp -R $$i ${DESTDIR}${plugindir}/'
			UNINSTALL_PLUGIN='&& rm -fr ${DESTDIR}${plugindir}/$$i'
			CLEAN_LIB=''
			;;
		mingw* | cygwin*)
			AC_MSG_RESULT(MinGW / Cygwin)
			LIB_CFLAGS=''
			LIB_LDFLAGS='-shared -Wl,--export-all-symbols,--out-implib,lib${SHARED_LIB}.a'
			LIB_LDFLAGS_INSTALL_NAME=''
			LIB_PREFIX=''
			LIB_SUFFIX='.dll'
			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'
			PLUGIN_CFLAGS=''
			PLUGIN_LDFLAGS='-shared'
			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.a ${DESTDIR}${libdir}/lib$$i.a'
			UNINSTALL_LIB='&& rm -f ${DESTDIR}${bindir}/$$i ${DESTDIR}${libdir}/lib$$i.a'
			INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
			UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
			CLEAN_LIB='${SHARED_LIB}.a'
			;;
		openbsd* | mirbsd*)
			AC_MSG_RESULT(OpenBSD)
			LIB_CFLAGS='-fPIC -DPIC'
			LIB_LDFLAGS='-shared'
			LIB_LDFLAGS_INSTALL_NAME=''
			LIB_PREFIX='lib'
			LIB_SUFFIX='.so.${LIB_MAJOR}.${LIB_MINOR}'

			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'

			PLUGIN_CFLAGS='-fPIC -DPIC'
			PLUGIN_LDFLAGS='-shared'
			PLUGIN_SUFFIX='.so'
			LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
			INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i'
			UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i'
			INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
			UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
			CLEAN_LIB=''
			;;
		solaris*)
			AC_MSG_RESULT(Solaris)
			LIB_CFLAGS='-fPIC -DPIC'
			LIB_LDFLAGS='-shared -Wl,-soname=${SHARED_LIB}.${LIB_MAJOR}.${LIB_MINOR}'
			LIB_LDFLAGS_INSTALL_NAME=''
			LIB_PREFIX='lib'
			LIB_SUFFIX='.so'

			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'

			PLUGIN_CFLAGS='-fPIC -DPIC'
			PLUGIN_LDFLAGS='-shared'
			PLUGIN_SUFFIX='.so'
			LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
			INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR} && rm -f ${DESTDIR}${libdir}/$$i && ${LN_S} $$i.${LIB_MAJOR}.${LIB_MINOR} ${DESTDIR}${libdir}/$$i'
			UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}'
			INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
			UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
			CLEAN_LIB=''
			;;
		*-android*)
			AC_MSG_RESULT(Android)
			LIB_CFLAGS='-fPIC -DPIC'
			LIB_LDFLAGS='-shared -Wl,-soname=${SHARED_LIB}.${LIB_MAJOR}'
			LIB_LDFLAGS_INSTALL_NAME=''
			LIB_PREFIX='lib'
			LIB_SUFFIX='.so'
			LDFLAGS_RPATH=''
			PLUGIN_CFLAGS='-fPIC -DPIC'
			PLUGIN_LDFLAGS='-shared'
			PLUGIN_SUFFIX='.so'
			LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
			INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.0 && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.0 ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.0 ${DESTDIR}${libdir}/$$i'
			UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.0'
			INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
			UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
			CLEAN_LIB=''
			;;










































		*)
			AC_MSG_RESULT(ELF)
			LIB_CFLAGS='-fPIC -DPIC'
			LIB_LDFLAGS='-shared -Wl,-soname=${SHARED_LIB}.${LIB_MAJOR}'
			LIB_LDFLAGS_INSTALL_NAME=''
			LIB_PREFIX='lib'
			LIB_SUFFIX='.so'

			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'

			PLUGIN_CFLAGS='-fPIC -DPIC'
			PLUGIN_LDFLAGS='-shared'
			PLUGIN_SUFFIX='.so'
			LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
			INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.0 && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.0 ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.0 ${DESTDIR}${libdir}/$$i'
			UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.0'
			INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
			UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
			CLEAN_LIB=''
			;;
	esac

	AC_SUBST(LIB_CFLAGS)
	AC_SUBST(LIB_LDFLAGS)
	AC_SUBST(LIB_LDFLAGS_INSTALL_NAME)
	AC_SUBST(LIB_PREFIX)
	AC_SUBST(LIB_SUFFIX)

	AC_SUBST(LDFLAGS_RPATH)
	AC_SUBST(PLUGIN_CFLAGS)
	AC_SUBST(PLUGIN_LDFLAGS)
	AC_SUBST(PLUGIN_SUFFIX)
	AC_SUBST(LINK_PLUGIN)
	AC_SUBST(INSTALL_LIB)
	AC_SUBST(UNINSTALL_LIB)
	AC_SUBST(INSTALL_PLUGIN)
	AC_SUBST(UNINSTALL_PLUGIN)
	AC_SUBST(CLEAN_LIB)
])

AC_DEFUN([BUILDSYS_FRAMEWORK], [
	AC_REQUIRE([AC_CANONICAL_HOST])
	AC_REQUIRE([BUILDSYS_CHECK_IOS])
	AC_REQUIRE([BUILDSYS_SHARED_LIB])

	AC_CHECK_TOOL(CODESIGN, codesign)

	case "$host_os" in
		darwin*)
			AS_IF([test x"$host_is_ios" = x"yes"], [
				FRAMEWORK_LDFLAGS='-dynamiclib -current_version ${LIB_MAJOR}.${LIB_MINOR} -compatibility_version ${LIB_MAJOR}'
				FRAMEWORK_LDFLAGS_INSTALL_NAME='-Wl,-install_name,@executable_path/Frameworks/$$out/$${out%.framework}'
			], [
				FRAMEWORK_LDFLAGS='-dynamiclib -current_version ${LIB_MAJOR}.${LIB_MINOR} -compatibility_version ${LIB_MAJOR}'
				FRAMEWORK_LDFLAGS_INSTALL_NAME='-Wl,-install_name,@executable_path/../Frameworks/$$out/$${out%.framework}'
			])

			AC_SUBST(FRAMEWORK_LDFLAGS)
			AC_SUBST(FRAMEWORK_LDFLAGS_INSTALL_NAME)


			$1
			;;
	esac
])







|
|
|
|

|
<
|
|
|
|
|
|
|
|
|
|
|







|
|
|
|
|
|
|
|
|
|











|
|
|
|
|
|
|
|
>

>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>

>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>

>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
>

>
|
|
|
|
|
|
|
|
|
|







>




















|
|
|
|
|
|
|
|

|
|
>

|
|


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
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
			fi
		])
	])
])

AC_DEFUN([BUILDSYS_CHECK_IOS], [
	case "$host_os" in
	darwin*)
		AC_MSG_CHECKING(whether host is iOS)
		AC_EGREP_CPP(yes, [
			#include <TargetConditionals.h>

			#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \

			    (defined(TARGET_OS_SIMULATOR) && \
			    TARGET_OS_SIMULATOR)
			yes
			#endif
		], [
			host_is_ios="yes"
		], [
			host_is_ios="no"
		])
		AC_MSG_RESULT($host_is_ios)
		;;
	esac
])

AC_DEFUN([BUILDSYS_PROG_IMPLIB], [
	AC_REQUIRE([AC_CANONICAL_HOST])
	AC_MSG_CHECKING(whether we need an implib)
	case "$host_os" in
	cygwin* | mingw*)
		AC_MSG_RESULT(yes)
		PROG_IMPLIB_NEEDED='yes'
		PROG_IMPLIB_LDFLAGS='-Wl,--export-all-symbols,--out-implib,lib${PROG}.a'
		;;
	*)
		AC_MSG_RESULT(no)
		PROG_IMPLIB_NEEDED='no'
		PROG_IMPLIB_LDFLAGS=''
		;;
	esac

	AC_SUBST(PROG_IMPLIB_NEEDED)
	AC_SUBST(PROG_IMPLIB_LDFLAGS)
])

AC_DEFUN([BUILDSYS_SHARED_LIB], [
	AC_REQUIRE([AC_CANONICAL_HOST])
	AC_REQUIRE([BUILDSYS_CHECK_IOS])
	AC_MSG_CHECKING(for shared library system)

	case "$host" in
	*-*-darwin*)
		AC_MSG_RESULT(Darwin)
		LIB_CFLAGS='-fPIC -DPIC'
		LIB_LDFLAGS='-dynamiclib -current_version ${LIB_MAJOR}.${LIB_MINOR} -compatibility_version ${LIB_MAJOR}'
		LIB_LDFLAGS_INSTALL_NAME='-Wl,-install_name,${libdir}/$${out%.dylib}.${LIB_MAJOR}.dylib'
		LIB_PREFIX='lib'
		LIB_SUFFIX='.dylib'
		AS_IF([test x"$enable_rpath" != x"no"], [
			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'
		])
		PLUGIN_CFLAGS='-fPIC -DPIC'
		PLUGIN_LDFLAGS='-bundle ${PLUGIN_LDFLAGS_BUNDLE_LOADER}'
		PLUGIN_SUFFIX='.bundle'
		AS_IF([test x"$host_is_ios" = x"yes"], [
			LINK_PLUGIN='rm -fr $$out && ${MKDIR_P} $$out && if test -f Info.plist; then ${INSTALL} -m 644 Info.plist $$out/Info.plist; fi && ${LD} -o $$out/$${out%${PLUGIN_SUFFIX}} ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS} && ${CODESIGN} -fs ${CODESIGN_IDENTITY} --timestamp=none $$out'
		], [
			LINK_PLUGIN='rm -fr $$out && ${MKDIR_P} $$out/Contents/MacOS && if test -f Info.plist; then ${INSTALL} -m 644 Info.plist $$out/Contents/Info.plist; fi && ${LD} -o $$out/Contents/MacOS/$${out%${PLUGIN_SUFFIX}} ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS} && ${CODESIGN} -fs ${CODESIGN_IDENTITY} --timestamp=none $$out'
		])
		INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib && ${LN_S} -f $${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.dylib && ${LN_S} -f $${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib ${DESTDIR}${libdir}/$$i'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.dylib ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib'
		INSTALL_PLUGIN='&& rm -fr ${DESTDIR}${plugindir}/$$i && cp -R $$i ${DESTDIR}${plugindir}/'
		UNINSTALL_PLUGIN='&& rm -fr ${DESTDIR}${plugindir}/$$i'
		CLEAN_LIB=''
		;;
	*-*-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'
		UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
		CLEAN_LIB='${SHARED_LIB}.a ${SHARED_LIB_NOINST}.a'
		;;
	*-*-openbsd* | *-*-mirbsd*)
		AC_MSG_RESULT(OpenBSD)
		LIB_CFLAGS='-fPIC -DPIC'
		LIB_LDFLAGS='-shared'
		LIB_LDFLAGS_INSTALL_NAME=''
		LIB_PREFIX='lib'
		LIB_SUFFIX='.so.${LIB_MAJOR}.${LIB_MINOR}'
		AS_IF([test x"$enable_rpath" != x"no"], [
			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'
		])
		PLUGIN_CFLAGS='-fPIC -DPIC'
		PLUGIN_LDFLAGS='-shared'
		PLUGIN_SUFFIX='.so'
		LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
		INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i'
		INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
		UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
		CLEAN_LIB=''
		;;
	*-*-solaris*)
		AC_MSG_RESULT(Solaris)
		LIB_CFLAGS='-fPIC -DPIC'
		LIB_LDFLAGS='-shared -Wl,-soname=$$out.${LIB_MAJOR}.${LIB_MINOR}'
		LIB_LDFLAGS_INSTALL_NAME=''
		LIB_PREFIX='lib'
		LIB_SUFFIX='.so'
		AS_IF([test x"$enable_rpath" != x"no"], [
			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'
		])
		PLUGIN_CFLAGS='-fPIC -DPIC'
		PLUGIN_LDFLAGS='-shared'
		PLUGIN_SUFFIX='.so'
		LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
		INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR} && rm -f ${DESTDIR}${libdir}/$$i && ${LN_S} $$i.${LIB_MAJOR}.${LIB_MINOR} ${DESTDIR}${libdir}/$$i'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}'
		INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
		UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
		CLEAN_LIB=''
		;;
	*-*-android*)
		AC_MSG_RESULT(Android)
		LIB_CFLAGS='-fPIC -DPIC'
		LIB_LDFLAGS='-shared -Wl,-soname=$$out.${LIB_MAJOR}'
		LIB_LDFLAGS_INSTALL_NAME=''
		LIB_PREFIX='lib'
		LIB_SUFFIX='.so'

		PLUGIN_CFLAGS='-fPIC -DPIC'
		PLUGIN_LDFLAGS='-shared'
		PLUGIN_SUFFIX='.so'
		LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
		INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.0 && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.0 ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.0 ${DESTDIR}${libdir}/$$i'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.0'
		INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
		UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
		CLEAN_LIB=''
		;;
	hppa*-*-hpux*)
		AC_MSG_RESULT([HP-UX (PA-RISC)])
		LIB_CFLAGS='-fPIC -DPIC'
		LIB_LDFLAGS='-shared -Wl,+h,$$out'
		LIB_LDFLAGS_INSTALL_NAME=''
		LIB_PREFIX='lib'
		LIB_SUFFIX='.${LIB_MAJOR}'
		LINK_LIB='&& rm -f $${out%%.*}.sl && ${LN_S} $$out $${out%%.*}.sl'
		AS_IF([test x"$enable_rpath" != x"no"], [
			LDFLAGS_RPATH='-Wl,+b,${libdir}'
		])
		PLUGIN_CFLAGS='-fPIC -DPIC'
		PLUGIN_LDFLAGS='-shared'
		PLUGIN_SUFFIX='.sl'
		LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
		INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i && ${LN_S} -f $$i ${DESTDIR}${libdir}/$${i%%.*}.sl'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$${i%%.*}.sl'
		INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
		UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
		CLEAN_LIB=''
		;;
	ia64*-*-hpux*)
		AC_MSG_RESULT([HP-UX (Itanium)])
		LIB_CFLAGS='-fPIC -DPIC'
		LIB_LDFLAGS='-shared -Wl,+h,$$out'
		LIB_LDFLAGS_INSTALL_NAME=''
		LIB_PREFIX='lib'
		LIB_SUFFIX='.${LIB_MAJOR}'
		LINK_LIB='&& rm -f $${out%%.*}.so && ${LN_S} $$out $${out%%.*}.so'
		AS_IF([test x"$enable_rpath" != x"no"], [
			LDFLAGS_RPATH='-Wl,+b,${libdir}'
		])
		PLUGIN_CFLAGS='-fPIC -DPIC'
		PLUGIN_LDFLAGS='-shared'
		PLUGIN_SUFFIX='.so'
		LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
		INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i && ${LN_S} -f $$i ${DESTDIR}${libdir}/$${i%%.*}.so'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$${i%%.*}.so'
		INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
		UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
		CLEAN_LIB=''
		;;
	*)
		AC_MSG_RESULT(ELF)
		LIB_CFLAGS='-fPIC -DPIC'
		LIB_LDFLAGS='-shared -Wl,-soname=$$out.${LIB_MAJOR}'
		LIB_LDFLAGS_INSTALL_NAME=''
		LIB_PREFIX='lib'
		LIB_SUFFIX='.so'
		AS_IF([test x"$enable_rpath" != x"no"], [
			LDFLAGS_RPATH='-Wl,-rpath,${libdir}'
		])
		PLUGIN_CFLAGS='-fPIC -DPIC'
		PLUGIN_LDFLAGS='-shared'
		PLUGIN_SUFFIX='.so'
		LINK_PLUGIN='${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}'
		INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.0 && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.0 ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.0 ${DESTDIR}${libdir}/$$i'
		UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.0'
		INSTALL_PLUGIN='&& ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i'
		UNINSTALL_PLUGIN='&& rm -f ${DESTDIR}${plugindir}/$$i'
		CLEAN_LIB=''
		;;
	esac

	AC_SUBST(LIB_CFLAGS)
	AC_SUBST(LIB_LDFLAGS)
	AC_SUBST(LIB_LDFLAGS_INSTALL_NAME)
	AC_SUBST(LIB_PREFIX)
	AC_SUBST(LIB_SUFFIX)
	AC_SUBST(LINK_LIB)
	AC_SUBST(LDFLAGS_RPATH)
	AC_SUBST(PLUGIN_CFLAGS)
	AC_SUBST(PLUGIN_LDFLAGS)
	AC_SUBST(PLUGIN_SUFFIX)
	AC_SUBST(LINK_PLUGIN)
	AC_SUBST(INSTALL_LIB)
	AC_SUBST(UNINSTALL_LIB)
	AC_SUBST(INSTALL_PLUGIN)
	AC_SUBST(UNINSTALL_PLUGIN)
	AC_SUBST(CLEAN_LIB)
])

AC_DEFUN([BUILDSYS_FRAMEWORK], [
	AC_REQUIRE([AC_CANONICAL_HOST])
	AC_REQUIRE([BUILDSYS_CHECK_IOS])
	AC_REQUIRE([BUILDSYS_SHARED_LIB])

	AC_CHECK_TOOL(CODESIGN, codesign)

	case "$host_os" in
	darwin*)
		AS_IF([test x"$host_is_ios" = x"yes"], [
			FRAMEWORK_LDFLAGS='-dynamiclib -current_version ${LIB_MAJOR}.${LIB_MINOR} -compatibility_version ${LIB_MAJOR}'
			FRAMEWORK_LDFLAGS_INSTALL_NAME='-Wl,-install_name,@executable_path/Frameworks/$$out/$${out%.framework}'
		], [
			FRAMEWORK_LDFLAGS='-dynamiclib -current_version ${LIB_MAJOR}.${LIB_MINOR} -compatibility_version ${LIB_MAJOR}'
			FRAMEWORK_LDFLAGS_INSTALL_NAME='-Wl,-install_name,@executable_path/../Frameworks/$$out/$${out%.framework}'
		])

		AC_SUBST(FRAMEWORK_LDFLAGS)
		AC_SUBST(FRAMEWORK_LDFLAGS_INSTALL_NAME)
		AC_SUBST(FRAMEWORK_LIBS)

		$1
		;;
	esac
])

Added build-aux/m4/pkg.m4 version [f7f00f7dea].















































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
# pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
# serial 11 (pkg-config-0.29.1)

dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation; either version 2 of the License, or
dnl (at your option) any later version.
dnl
dnl This program is distributed in the hope that it will be useful, but
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
dnl General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
dnl 02111-1307, USA.
dnl
dnl As a special exception to the GNU General Public License, if you
dnl distribute this file as part of a program that contains a
dnl configuration script generated by Autoconf, you may include it under
dnl the same distribution terms that you use for the rest of that
dnl program.

dnl PKG_PREREQ(MIN-VERSION)
dnl -----------------------
dnl Since: 0.29
dnl
dnl Verify that the version of the pkg-config macros are at least
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
dnl installed version of pkg-config, this checks the developer's version
dnl of pkg.m4 when generating configure.
dnl
dnl To ensure that this macro is defined, also add:
dnl m4_ifndef([PKG_PREREQ],
dnl     [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
dnl
dnl See the "Since" comment for each macro you use to see what version
dnl of the macros you require.
m4_defun([PKG_PREREQ],
[m4_define([PKG_MACROS_VERSION], [0.29.1])
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
    [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
])dnl PKG_PREREQ

dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
dnl ----------------------------------
dnl Since: 0.16
dnl
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
dnl first found in the path. Checks that the version of pkg-config found
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
dnl used since that's the first version where most current features of
dnl pkg-config existed.
AC_DEFUN([PKG_PROG_PKG_CONFIG],
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])

if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
fi
if test -n "$PKG_CONFIG"; then
	_pkg_min_version=m4_default([$1], [0.9.0])
	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
		AC_MSG_RESULT([yes])
	else
		AC_MSG_RESULT([no])
		PKG_CONFIG=""
	fi
fi[]dnl
])dnl PKG_PROG_PKG_CONFIG

dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl -------------------------------------------------------------------
dnl Since: 0.18
dnl
dnl Check to see whether a particular set of modules exists. Similar to
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
dnl
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
dnl only at the first occurence in configure.ac, so if the first place
dnl it's called might be skipped (such as if it is within an "if", you
dnl have to call PKG_CHECK_EXISTS manually
AC_DEFUN([PKG_CHECK_EXISTS],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
if test -n "$PKG_CONFIG" && \
    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
  m4_default([$2], [:])
m4_ifvaln([$3], [else
  $3])dnl
fi])

dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
dnl ---------------------------------------------
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
dnl pkg_failed based on the result.
m4_define([_PKG_CONFIG],
[if test -n "$$1"; then
    pkg_cv_[]$1="$$1"
 elif test -n "$PKG_CONFIG"; then
    PKG_CHECK_EXISTS([$3],
                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
		      test "x$?" != "x0" && pkg_failed=yes ],
		     [pkg_failed=yes])
 else
    pkg_failed=untried
fi[]dnl
])dnl _PKG_CONFIG

dnl _PKG_SHORT_ERRORS_SUPPORTED
dnl ---------------------------
dnl Internal check to see if pkg-config supports short errors.
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
        _pkg_short_errors_supported=yes
else
        _pkg_short_errors_supported=no
fi[]dnl
])dnl _PKG_SHORT_ERRORS_SUPPORTED


dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
dnl   [ACTION-IF-NOT-FOUND])
dnl --------------------------------------------------------------
dnl Since: 0.4.0
dnl
dnl Note that if there is a possibility the first call to
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
AC_DEFUN([PKG_CHECK_MODULES],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl

pkg_failed=no
AC_MSG_CHECKING([for $1])

_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2])

m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])

if test $pkg_failed = yes; then
   	AC_MSG_RESULT([no])
        _PKG_SHORT_ERRORS_SUPPORTED
        if test $_pkg_short_errors_supported = yes; then
	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
        else 
	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
        fi
	# Put the nasty error message in config.log where it belongs
	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD

	m4_default([$4], [AC_MSG_ERROR(
[Package requirements ($2) were not met:

$$1_PKG_ERRORS

Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.

_PKG_TEXT])[]dnl
        ])
elif test $pkg_failed = untried; then
     	AC_MSG_RESULT([no])
	m4_default([$4], [AC_MSG_FAILURE(
[The pkg-config script could not be found or is too old.  Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.

_PKG_TEXT

To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
        ])
else
	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
        AC_MSG_RESULT([yes])
	$3
fi[]dnl
])dnl PKG_CHECK_MODULES


dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
dnl   [ACTION-IF-NOT-FOUND])
dnl ---------------------------------------------------------------------
dnl Since: 0.29
dnl
dnl Checks for existence of MODULES and gathers its build flags with
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
dnl and VARIABLE-PREFIX_LIBS from --libs.
dnl
dnl Note that if there is a possibility the first call to
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
dnl configure.ac.
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
_save_PKG_CONFIG=$PKG_CONFIG
PKG_CONFIG="$PKG_CONFIG --static"
PKG_CHECK_MODULES($@)
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
])dnl PKG_CHECK_MODULES_STATIC


dnl PKG_INSTALLDIR([DIRECTORY])
dnl -------------------------
dnl Since: 0.27
dnl
dnl Substitutes the variable pkgconfigdir as the location where a module
dnl should install pkg-config .pc files. By default the directory is
dnl $libdir/pkgconfig, but the default can be changed by passing
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
dnl parameter.
AC_DEFUN([PKG_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
m4_pushdef([pkg_description],
    [pkg-config installation directory @<:@]pkg_default[@:>@])
AC_ARG_WITH([pkgconfigdir],
    [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
    [with_pkgconfigdir=]pkg_default)
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
])dnl PKG_INSTALLDIR


dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
dnl --------------------------------
dnl Since: 0.27
dnl
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
dnl module should install arch-independent pkg-config .pc files. By
dnl default the directory is $datadir/pkgconfig, but the default can be
dnl changed by passing DIRECTORY. The user can override through the
dnl --with-noarch-pkgconfigdir parameter.
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
m4_pushdef([pkg_description],
    [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
AC_ARG_WITH([noarch-pkgconfigdir],
    [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
    [with_noarch_pkgconfigdir=]pkg_default)
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
])dnl PKG_NOARCH_INSTALLDIR


dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl -------------------------------------------
dnl Since: 0.28
dnl
dnl Retrieves the value of the pkg-config variable for the given module.
AC_DEFUN([PKG_CHECK_VAR],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl

_PKG_CONFIG([$1], [variable="][$3]["], [$2])
AS_VAR_COPY([$1], [pkg_cv_][$1])

AS_VAR_IF([$1], [""], [$5], [$4])dnl
])dnl PKG_CHECK_VAR

dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl   [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],
dnl   [DESCRIPTION], [DEFAULT])
dnl ------------------------------------------
dnl
dnl Prepare a "--with-" configure option using the lowercase
dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and
dnl PKG_CHECK_MODULES in a single macro.
AC_DEFUN([PKG_WITH_MODULES],
[
m4_pushdef([with_arg], m4_tolower([$1]))

m4_pushdef([description],
           [m4_default([$5], [build with ]with_arg[ support])])

m4_pushdef([def_arg], [m4_default([$6], [auto])])
m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes])
m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no])

m4_case(def_arg,
            [yes],[m4_pushdef([with_without], [--without-]with_arg)],
            [m4_pushdef([with_without],[--with-]with_arg)])

AC_ARG_WITH(with_arg,
     AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),,
    [AS_TR_SH([with_]with_arg)=def_arg])

AS_CASE([$AS_TR_SH([with_]with_arg)],
            [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)],
            [auto],[PKG_CHECK_MODULES([$1],[$2],
                                        [m4_n([def_action_if_found]) $3],
                                        [m4_n([def_action_if_not_found]) $4])])

m4_popdef([with_arg])
m4_popdef([description])
m4_popdef([def_arg])

])dnl PKG_WITH_MODULES

dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl   [DESCRIPTION], [DEFAULT])
dnl -----------------------------------------------
dnl
dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES
dnl check._[VARIABLE-PREFIX] is exported as make variable.
AC_DEFUN([PKG_HAVE_WITH_MODULES],
[
PKG_WITH_MODULES([$1],[$2],,,[$3],[$4])

AM_CONDITIONAL([HAVE_][$1],
               [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"])
])dnl PKG_HAVE_WITH_MODULES

dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl   [DESCRIPTION], [DEFAULT])
dnl ------------------------------------------------------
dnl
dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after
dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make
dnl and preprocessor variable.
AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES],
[
PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4])

AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
        [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
])dnl PKG_HAVE_DEFINE_WITH_MODULES

Modified buildsys.mk.in from [0dd86557eb] to [a085f99653].

1
2
3
4
5
6
7
8
9
10
#
#  Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016,
#                2017, 2018, 2020
#    Jonathan Schleifer <js@nil.im>
#
#  https://fossil.nil.im/buildsys
#
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice is present in all copies.


|







1
2
3
4
5
6
7
8
9
10
#
#  Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016,
#                2017, 2018, 2020, 2021
#    Jonathan Schleifer <js@nil.im>
#
#  https://fossil.nil.im/buildsys
#
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice is present in all copies.
52
53
54
55
56
57
58

59
60
61
62
63
64
65

66
67
68
69
70
71
72
PROG_IMPLIB_LDFLAGS = @PROG_IMPLIB_LDFLAGS@
PROG_SUFFIX = @EXEEXT@
LIB_CFLAGS = @LIB_CFLAGS@
LIB_LDFLAGS = @LIB_LDFLAGS@
LIB_LDFLAGS_INSTALL_NAME = @LIB_LDFLAGS_INSTALL_NAME@
LIB_PREFIX = @LIB_PREFIX@
LIB_SUFFIX = @LIB_SUFFIX@

AMIGA_LIB_CFLAGS = @AMIGA_LIB_CFLAGS@
AMIGA_LIB_LDFLAGS = @AMIGA_LIB_LDFLAGS@
PLUGIN_CFLAGS = @PLUGIN_CFLAGS@
PLUGIN_LDFLAGS = @PLUGIN_LDFLAGS@
PLUGIN_SUFFIX = @PLUGIN_SUFFIX@
FRAMEWORK_LDFLAGS = @FRAMEWORK_LDFLAGS@
FRAMEWORK_LDFLAGS_INSTALL_NAME = @FRAMEWORK_LDFLAGS_INSTALL_NAME@

CODESIGN = @CODESIGN@
CODESIGN_IDENTITY ?= -
CLEAN_LIB = @CLEAN_LIB@
DEP_ASFLAGS = @DEP_ASFLAGS@
DEP_CFLAGS = @DEP_CFLAGS@
DEP_CXXFLAGS = @DEP_CXXFLAGS@
DEP_OBJCFLAGS = @DEP_OBJCFLAGS@







>







>







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
PROG_IMPLIB_LDFLAGS = @PROG_IMPLIB_LDFLAGS@
PROG_SUFFIX = @EXEEXT@
LIB_CFLAGS = @LIB_CFLAGS@
LIB_LDFLAGS = @LIB_LDFLAGS@
LIB_LDFLAGS_INSTALL_NAME = @LIB_LDFLAGS_INSTALL_NAME@
LIB_PREFIX = @LIB_PREFIX@
LIB_SUFFIX = @LIB_SUFFIX@
LINK_LIB = @LINK_LIB@
AMIGA_LIB_CFLAGS = @AMIGA_LIB_CFLAGS@
AMIGA_LIB_LDFLAGS = @AMIGA_LIB_LDFLAGS@
PLUGIN_CFLAGS = @PLUGIN_CFLAGS@
PLUGIN_LDFLAGS = @PLUGIN_LDFLAGS@
PLUGIN_SUFFIX = @PLUGIN_SUFFIX@
FRAMEWORK_LDFLAGS = @FRAMEWORK_LDFLAGS@
FRAMEWORK_LDFLAGS_INSTALL_NAME = @FRAMEWORK_LDFLAGS_INSTALL_NAME@
FRAMEWORK_LIBS = @FRAMEWORK_LIBS@
CODESIGN = @CODESIGN@
CODESIGN_IDENTITY ?= -
CLEAN_LIB = @CLEAN_LIB@
DEP_ASFLAGS = @DEP_ASFLAGS@
DEP_CFLAGS = @DEP_CFLAGS@
DEP_CXXFLAGS = @DEP_CXXFLAGS@
DEP_OBJCFLAGS = @DEP_OBJCFLAGS@
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
		${DIR_ENTER}; \
		${MAKE} -s || exit $$?; \
		${DIR_LEAVE}; \
	done

depend: pre-depend
	: >.deps
	for i in ${DEPS}; do \

		echo "-include \$${.CURDIR}/$$i" >>.deps; \
	done

pre-depend:

${PROG} ${PROG_NOINST}: ${EXT_DEPS} ${OBJS} ${OBJS_EXTRA}
	${LINK_STATUS}







|
>







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
		${DIR_ENTER}; \
		${MAKE} -s || exit $$?; \
		${DIR_LEAVE}; \
	done

depend: pre-depend
	: >.deps
	for i in "" ${DEPS}; do \
		test x"$$i" = x"" && continue; \
		echo "-include \$${.CURDIR}/$$i" >>.deps; \
	done

pre-depend:

${PROG} ${PROG_NOINST}: ${EXT_DEPS} ${OBJS} ${OBJS_EXTRA}
	${LINK_STATUS}
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
			${LINK_FAILED}; \
		fi \
	fi

${SHARED_LIB} ${SHARED_LIB_NOINST}: ${EXT_DEPS} ${LIB_OBJS} ${LIB_OBJS_EXTRA}
	${LINK_STATUS}
	out="$@"; \
	if ${LD} -o $@ ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${LIB_LDFLAGS} ${LIB_LDFLAGS_INSTALL_NAME} ${LDFLAGS} ${LIBS}; then \
		${LINK_OK}; \
	else \
		${LINK_FAILED}; \
	fi

${FRAMEWORK} ${FRAMEWORK_NOINST}: ${EXT_DEPS} ${LIB_OBJS} ${LIB_OBJS_EXTRA}
	${LINK_STATUS}







|







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
			${LINK_FAILED}; \
		fi \
	fi

${SHARED_LIB} ${SHARED_LIB_NOINST}: ${EXT_DEPS} ${LIB_OBJS} ${LIB_OBJS_EXTRA}
	${LINK_STATUS}
	out="$@"; \
	if ${LD} -o $@ ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${LIB_LDFLAGS} ${LIB_LDFLAGS_INSTALL_NAME} ${LDFLAGS} ${LIBS} ${LINK_LIB}; then \
		${LINK_OK}; \
	else \
		${LINK_FAILED}; \
	fi

${FRAMEWORK} ${FRAMEWORK_NOINST}: ${EXT_DEPS} ${LIB_OBJS} ${LIB_OBJS_EXTRA}
	${LINK_STATUS}
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
		${MAKE} -s copy-headers-into-framework || exit $$?; \
		cd .. || exit 1; \
	done

	if test x"${includesubdir}" = x"${COPY_HEADERS_IF_SUBDIR}"; then \
		for i in "" ${INCLUDES}; do \
			test x"$$i" = x"" && continue; \
			${MKDIR_P} ${COPY_HEADERS_DESTINATION} || exit $$?; \
			${INSTALL} -m 644 $$i ${COPY_HEADERS_DESTINATION}/$$i || exit $$?; \
		done \
	fi

${AMIGA_LIB} ${AMIGA_LIB_NOINST}: ${EXT_DEPS} ${AMIGA_LIB_OBJS_START} ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA}
	${LINK_STATUS}
	if ${LD} -o $@ ${AMIGA_LIB_OBJS_START} ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA} ${AMIGA_LIB_LDFLAGS} ${AMIGA_LIB_LIBS}; then \







|







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
		${MAKE} -s copy-headers-into-framework || exit $$?; \
		cd .. || exit 1; \
	done

	if test x"${includesubdir}" = x"${COPY_HEADERS_IF_SUBDIR}"; then \
		for i in "" ${INCLUDES}; do \
			test x"$$i" = x"" && continue; \
			${MKDIR_P} $$(dirname ${COPY_HEADERS_DESTINATION}/$$i) || exit $$?; \
			${INSTALL} -m 644 $$i ${COPY_HEADERS_DESTINATION}/$$i || exit $$?; \
		done \
	fi

${AMIGA_LIB} ${AMIGA_LIB_NOINST}: ${EXT_DEPS} ${AMIGA_LIB_OBJS_START} ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA}
	${LINK_STATUS}
	if ${LD} -o $@ ${AMIGA_LIB_OBJS_START} ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA} ${AMIGA_LIB_LDFLAGS} ${AMIGA_LIB_LIBS}; then \
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
		fi; \
	else \
		out="$@"; \
		objs=""; \
		ars=""; \
		for i in ${OBJS} ${OBJS_EXTRA}; do \
			case $$i in \
				*.a) \
					ars="$$ars $$i" \
					;; \
				*.o) \
					objs="$$objs $$i" \
					;; \
			esac \
		done; \
		for i in $$ars; do \
			dir=".$$(echo $$i | sed 's/\//_/g').objs"; \
			rm -fr $$dir; \
			mkdir -p $$dir; \
			cd $$dir; \







|
|
|
|
|
|







247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
		fi; \
	else \
		out="$@"; \
		objs=""; \
		ars=""; \
		for i in ${OBJS} ${OBJS_EXTRA}; do \
			case $$i in \
			*.a) \
				ars="$$ars $$i" \
				;; \
			*.o) \
				objs="$$objs $$i" \
				;; \
			esac \
		done; \
		for i in $$ars; do \
			dir=".$$(echo $$i | sed 's/\//_/g').objs"; \
			rm -fr $$dir; \
			mkdir -p $$dir; \
			cd $$dir; \
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
		fi; \
	else \
		out="$@"; \
		objs=""; \
		ars=""; \
		for i in ${LIB_OBJS} ${LIB_OBJS_EXTRA}; do \
			case $$i in \
				*.a) \
					ars="$$ars $$i" \
					;; \
				*.o) \
					objs="$$objs $$i" \
					;; \
			esac \
		done; \
		for i in $$ars; do \
			dir=".$$(echo $$i | sed 's/\//_/g').objs"; \
			rm -fr $$dir; \
			mkdir -p $$dir; \
			cd $$dir; \







|
|
|
|
|
|







294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
		fi; \
	else \
		out="$@"; \
		objs=""; \
		ars=""; \
		for i in ${LIB_OBJS} ${LIB_OBJS_EXTRA}; do \
			case $$i in \
			*.a) \
				ars="$$ars $$i" \
				;; \
			*.o) \
				objs="$$objs $$i" \
				;; \
			esac \
		done; \
		for i in $$ars; do \
			dir=".$$(echo $$i | sed 's/\//_/g').objs"; \
			rm -fr $$dir; \
			mkdir -p $$dir; \
			cd $$dir; \
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
	${LINK_STATUS}
	rm -f $@
	out="$@"; \
	objs=""; \
	ars=""; \
	for i in ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA}; do \
		case $$i in \
			*.a) \
				ars="$$ars $$i" \
				;; \
			*.o) \
				objs="$$objs $$i" \
				;; \
		esac \
	done; \
	for i in $$ars; do \
		dir=".$$(echo $$i | sed 's/\//_/g').objs"; \
		rm -fr $$dir; \
		mkdir -p $$dir; \
		cd $$dir; \







|
|
|
|
|
|







333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
	${LINK_STATUS}
	rm -f $@
	out="$@"; \
	objs=""; \
	ars=""; \
	for i in ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA}; do \
		case $$i in \
		*.a) \
			ars="$$ars $$i" \
			;; \
		*.o) \
			objs="$$objs $$i" \
			;; \
		esac \
	done; \
	for i in $$ars; do \
		dir=".$$(echo $$i | sed 's/\//_/g').objs"; \
		rm -fr $$dir; \
		mkdir -p $$dir; \
		cd $$dir; \
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
		fi \
	done

	if test x"${INSTALL_INCLUDES}" = x"yes"; then \
		for i in "" ${INCLUDES}; do \
			test x"$$i" = x"" && continue; \
			${INSTALL_STATUS}; \
			if ${MKDIR_P} ${DESTDIR}${includedir}/${includesubdir} && ${INSTALL} -m 644 $$i ${DESTDIR}${includedir}/${includesubdir}/$$i; then \
				${INSTALL_OK}; \
			else \
				${INSTALL_FAILED}; \
			fi \
		done \
	fi








|







729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
		fi \
	done

	if test x"${INSTALL_INCLUDES}" = x"yes"; then \
		for i in "" ${INCLUDES}; do \
			test x"$$i" = x"" && continue; \
			${INSTALL_STATUS}; \
			if ${MKDIR_P} $$(dirname ${DESTDIR}${includedir}/${includesubdir}/$$i) && ${INSTALL} -m 644 $$i ${DESTDIR}${includedir}/${includesubdir}/$$i; then \
				${INSTALL_OK}; \
			else \
				${INSTALL_FAILED}; \
			fi \
		done \
	fi

881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
		${DIR_ENTER}; \
		${MAKE} -s clean || exit $$?; \
		${DIR_LEAVE}; \
	done

	: >.deps

	for i in "" ${DEPS} ${OBJS} ${OBJS_EXTRA} ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA} ${PLUGIN_OBJS} ${PROG} ${PROG_NOINST} ${SHARED_LIB} ${SHARED_LIB_NOINST} ${AMIGA_LIB} ${AMIGA_LIB_NOINST} ${STATIC_LIB} ${STATIC_LIB_NOINST} ${STATIC_PIC_LIB} ${STATIC_PIC_LIB_NOINST} ${STATIC_AMIGA_LIB} ${STATIC_AMIGA_LIB_NOINST} ${FRAMEWORK} ${PLUGIN} ${PLUGIN_NOINST} ${CLEAN_LIB} ${MO_FILES} ${CLEAN}; do \
		test x"$$i" = x"" && continue; \
		if test -f $$i -o -d $$i; then \
			if rm -fr $$i; then \
				${DELETE_OK}; \
			else \
				${DELETE_FAILED}; \
			fi \







|







884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
		${DIR_ENTER}; \
		${MAKE} -s clean || exit $$?; \
		${DIR_LEAVE}; \
	done

	: >.deps

	for i in "" ${DEPS} ${OBJS} ${OBJS_EXTRA} ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_START} ${AMIGA_LIB_OBJS_EXTRA} ${PLUGIN_OBJS} ${PROG} ${PROG_NOINST} ${SHARED_LIB} ${SHARED_LIB_NOINST} ${AMIGA_LIB} ${AMIGA_LIB_NOINST} ${STATIC_LIB} ${STATIC_LIB_NOINST} ${STATIC_PIC_LIB} ${STATIC_PIC_LIB_NOINST} ${STATIC_AMIGA_LIB} ${STATIC_AMIGA_LIB_NOINST} ${FRAMEWORK} ${PLUGIN} ${PLUGIN_NOINST} ${CLEAN_LIB} ${MO_FILES} ${CLEAN}; do \
		test x"$$i" = x"" && continue; \
		if test -f $$i -o -d $$i; then \
			if rm -fr $$i; then \
				${DELETE_OK}; \
			else \
				${DELETE_FAILED}; \
			fi \

Modified configure.ac from [9214c38119] to [5a6b4fb150].

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
AC_INIT(ObjFW, 1.1-dev, js@nil.im)
AC_CONFIG_SRCDIR(src)
AC_CONFIG_AUX_DIR(build-aux)
AC_CONFIG_MACRO_DIR(build-aux/m4)

AC_DEFINE(OBJFW_VERSION_MAJOR, 1, [The major version of ObjFW])
AC_DEFINE(OBJFW_VERSION_MINOR, 1, [The minor version of ObjFW])
dnl This may only be set to 1.1 once 1.1 is released
AC_SUBST(BUNDLE_VERSION, 1.0.0)
AC_SUBST(BUNDLE_SHORT_VERSION, 1.0)

for i in configure.ac build-aux/m4/*; do
	AS_IF([test $i -nt configure], [
		AC_MSG_ERROR([$i is newer than configure! Run ./autogen.sh!])
	])
done

BUILDSYS_INIT

AC_CANONICAL_HOST

AC_ARG_WITH(ixemul,
	AS_HELP_STRING([--with-ixemul], [build with ixemul]))

dnl Used to disable checking for -pedantic on some platforms where it's broken
check_pedantic="yes"

case "$host" in
	arm-*-riscos*)
		AS_IF([test x"$OBJCFLAGS" = x""], [
			OBJCFLAGS="-O2 -g"
		])
		flags="-mfloat-abi=softfp -mfpu=vfp -mlibscl"
		ASFLAGS="$ASFLAGS -mfloat-abi=softfp -mfpu=vfp"
		OBJCFLAGS="$OBJCFLAGS $flags"
		OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flags"
		LDFLAGS="$LDFLAGS $flags"

		enable_shared="no"
		enable_threads="no"
		enable_sockets="no"
		enable_files="no"
		;;
	m68k-*-amigaos*)
		AS_IF([test x"$OBJCFLAGS" = x""], [
			OBJCFLAGS="-O0"
		])
		OBJCFLAGS="$OBJCFLAGS -noixemul"
		OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -noixemul"
		CPPFLAGS="$CPPFLAGS -D__NO_NET_API"
		LDFLAGS="$LDFLAGS -noixemul"


		enable_files="yes"	# Required for reading ENV:
		enable_shared="no"

		supports_amiga_lib="yes"

		AS_IF([test x"$enable_amiga_lib" != x"no"], [
			AC_SUBST(OBJFWRT_AMIGA_LIB, objfwrt68k.library)
			AC_SUBST(SFDC_TARGET, m68k-amigaos)
			AC_SUBST(SFD_FILE, amigaos3.sfd)
			AC_SUBST(SFDC_INLINE_H, inline.h)
			dnl For 68000, GCC emits calls to helper functions that
			dnl do not work properly in a library.
			t="-mcpu=68020 -fbaserel -noixemul"
			AC_SUBST(AMIGA_LIB_CFLAGS, "$t -ffreestanding")

			AC_SUBST(AMIGA_LIB_LDFLAGS,
				"$t -resident -nostartfiles")
		])

		AC_SUBST(LIBBASES_M, libbases.m)
		;;
	powerpc-*-amigaos*)
		CPPFLAGS="$CPPFLAGS -D__USE_INLINE__"

		enable_files="yes"	# Required for reading ENV:
		enable_shared="no"


		AC_SUBST(LIBBASES_M, libbases.m)
		;;
	*-morphos*)
		AS_IF([test x"$with_ixemul" != x"yes"], [
			AS_IF([test x"$OBJCFLAGS" = x""], [
				OBJCFLAGS="-O2 -g"
			])
			OBJCFLAGS="$OBJCFLAGS -noixemul"
			OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -noixemul"
			LDFLAGS="$LDFLAGS -noixemul"


			enable_files="yes"	# Required for reading ENV:

			supports_amiga_lib="yes"
			check_pedantic="no"	# Breaks generated inlines

			AS_IF([test x"$enable_amiga_lib" != x"no"], [
				AC_SUBST(OBJFWRT_AMIGA_LIB, objfwrt.library)
				AC_SUBST(CVINCLUDE_INLINE_H, inline.h)
				t="-mresident32 -ffreestanding -noixemul"
				AC_SUBST(AMIGA_LIB_CFLAGS, $t)
				t="-mresident32 -nostartfiles -nodefaultlibs"
				t="$t -noixemul -lc"
				AC_SUBST(AMIGA_LIB_LDFLAGS, $t)
			])

			AC_SUBST(LIBBASES_M, libbases.m)
		])

		enable_shared="no"
		enable_threads="no"
		;;
	*-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.])
		])

		AS_IF([test x"$OBJCFLAGS" = x""], [
			OBJCFLAGS="-O2"
		])
		OBJCFLAGS="$OBJCFLAGS -G0"
		OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -G0"
		CPPFLAGS="$CPPFLAGS -I$DEVKITPSP/psp/sdk/include"
		LDFLAGS="$LDFLAGS -G0"
		LIBS="$LIBS -L$DEVKITPSP/psp/sdk/lib -lpspdebug -lpspdisplay"
		LIBS="$LIBS -lpspge -lpspctrl -lpspsdk -lc -lpspnet"
		LIBS="$LIBS -lpspnet_inet -lpspnet_apctl -lpspnet_resolver"
		LIBS="$LIBS -lpsputility -lpspuser -lpspkernel -lgcc -lpsplibc"
		enable_shared="no"
		enable_threads="no"	# TODO
		enable_sockets="no"	# TODO
		check_pedantic="no"

		AC_SUBST(MAP_LDFLAGS, ['-Wl,-Map,$@.map'])
		;;



















esac

AS_IF([test x"$host_os" = x"msdosdjgpp" -a x"$build_os" = x"msdosdjgpp"], [
	dnl Hack to make configure find these on DOS.
	: ${AR:=ar.exe}
	: ${GREP:=grep.exe}
	: ${RANLIB:=ranlib.exe}
])

AC_LANG([Objective C])







AC_PROG_OBJC([clang egcc gcc])


AC_PROG_OBJCPP
AC_PROG_LN_S
AC_PROG_INSTALL
AC_PROG_EGREP

BUILDSYS_CHECK_IOS

AC_ARG_WITH(wii,
	AS_HELP_STRING([--with-wii], [build for Wii]))
AS_IF([test x"$with_wii" = x"yes"], [
|




















<
<
<




|
|
<
<
|
|
|
|
|

|
|
|
|
|
|
|
<
<
|
|
|
|
>

|
|
>
|

|
|
|
<
<
|
|
|
|
>
|
<
|

|
|
|
|

|
|
>

|
|
|
<
|
<
<
|
|
|
>
>
|
>
|
<

|
|
|
|
|
|
<
|
|

|
<
<
<
<
|
|
|
|
|
|
|
|
|

|
|
|
|
<
|
|

|
<
<
|
|
|
|
|
|
|
|
|
|
|
|

|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>
>
>
>
>
>
>
|
>
>
|
|
|







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
AC_INIT(ObjFW, 1.1dev, js@nil.im)
AC_CONFIG_SRCDIR(src)
AC_CONFIG_AUX_DIR(build-aux)
AC_CONFIG_MACRO_DIR(build-aux/m4)

AC_DEFINE(OBJFW_VERSION_MAJOR, 1, [The major version of ObjFW])
AC_DEFINE(OBJFW_VERSION_MINOR, 1, [The minor version of ObjFW])
dnl This may only be set to 1.1 once 1.1 is released
AC_SUBST(BUNDLE_VERSION, 1.0.0)
AC_SUBST(BUNDLE_SHORT_VERSION, 1.0)

for i in configure.ac build-aux/m4/*; do
	AS_IF([test $i -nt configure], [
		AC_MSG_ERROR([$i is newer than configure! Run ./autogen.sh!])
	])
done

BUILDSYS_INIT

AC_CANONICAL_HOST




dnl Used to disable checking for -pedantic on some platforms where it's broken
check_pedantic="yes"

case "$host" in
arm-*-riscos*)
	AS_IF([test x"$OBJCFLAGS" = x""], [OBJCFLAGS="-O2 -g"])


	flags="-mfloat-abi=softfp -mfpu=vfp -mlibscl"
	ASFLAGS="$ASFLAGS -mfloat-abi=softfp -mfpu=vfp"
	OBJCFLAGS="$OBJCFLAGS $flags"
	OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flags"
	LDFLAGS="$LDFLAGS $flags"

	enable_shared="no"
	enable_threads="no"
	enable_sockets="no"
	enable_files="no"
	;;
m68k-*-amigaos*)
	AS_IF([test x"$OBJCFLAGS" = x""], [OBJCFLAGS="-O0"])


	OBJCFLAGS="$OBJCFLAGS -noixemul"
	OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -noixemul"
	CPPFLAGS="$CPPFLAGS -D__NO_NET_API"
	LDFLAGS="$LDFLAGS -noixemul"
	LIBS="$LIBS -ldebug"

	enable_files="yes"	# Required for reading ENV:
	enable_shared="no"
	with_tls="no"
	supports_amiga_lib="yes"

	AS_IF([test x"$enable_amiga_lib" != x"no"], [
		AC_SUBST(OBJFWRT_AMIGA_LIB,
			['objfwrt${OBJFWRT_LIB_MAJOR}.library'])


		dnl For 68000, GCC emits calls to helper functions that
		dnl do not work properly in a library.
		t="-mcpu=68020 -fbaserel -noixemul -ffreestanding"
		AC_SUBST(AMIGA_LIB_CFLAGS, $t)
		t="$t -resident -nostartfiles -nodefaultlibs -ldebug -lc"
		AC_SUBST(AMIGA_LIB_LDFLAGS, $t)

	])

	AC_SUBST(LIBBASES_M, libbases.m)
	;;
powerpc-*-amigaos*)
	CPPFLAGS="$CPPFLAGS -D__USE_INLINE__"

	enable_files="yes"	# Required for reading ENV:
	enable_shared="no"
	with_tls="no"

	AC_SUBST(LIBBASES_M, libbases.m)
	;;
*-morphos*)

	AS_IF([test x"$OBJCFLAGS" = x""], [OBJCFLAGS="-O2 -g"])


	OBJCFLAGS="$OBJCFLAGS -noixemul"
	OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -noixemul"
	LDFLAGS="$LDFLAGS -noixemul"
	LIBS="$LIBS -ldebug"

	enable_files="yes"	# Required for reading ENV:
	enable_shared="no"
	supports_amiga_lib="yes"


	AS_IF([test x"$enable_amiga_lib" != x"no"], [
		AC_SUBST(OBJFWRT_AMIGA_LIB,
			['objfwrt${OBJFW_LIB_MAJOR}ppc.library'])
		t="-mresident32 -ffreestanding -noixemul"
		AC_SUBST(AMIGA_LIB_CFLAGS, $t)
		t="-mresident32 -nostartfiles -nodefaultlibs -noixemul -ldebug"

		AC_SUBST(AMIGA_LIB_LDFLAGS, "$t -lc")
	])

	AC_SUBST(LIBBASES_M, libbases.m)




	;;
*-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.])
	])

	AS_IF([test x"$OBJCFLAGS" = x""], [OBJCFLAGS="-O2"])


	OBJCFLAGS="$OBJCFLAGS -G0"
	OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -G0"
	CPPFLAGS="$CPPFLAGS -I$DEVKITPSP/psp/sdk/include"
	LDFLAGS="$LDFLAGS -G0"
	LIBS="$LIBS -L$DEVKITPSP/psp/sdk/lib -lpspdebug -lpspdisplay"
	LIBS="$LIBS -lpspge -lpspctrl -lpspsdk -lc -lpspnet"
	LIBS="$LIBS -lpspnet_inet -lpspnet_apctl -lpspnet_resolver"
	LIBS="$LIBS -lpsputility -lpspuser -lpspkernel -lgcc -lpsplibc"
	enable_shared="no"
	enable_threads="no"	# TODO
	enable_sockets="no"	# TODO
	check_pedantic="no"

	AC_SUBST(MAP_LDFLAGS, ['-Wl,-Map,$@.map'])
	;;
hppa*-*-hpux*)
	dnl Don't default to -g: It creates errors from the assembler and breaks
	dnl exceptions.
	AS_IF([test x"$OBJCFLAGS" = x""], [OBJCFLAGS="-O2"])
	dnl HP-UX 11.11's inttypes.h defines UINTPTR_MAX etc. to nothing. GCC's
	dnl stdint.h defines those correctly, but if inttypes.h gets included
	dnl after something included stdint.h, it gets broken again. Therefore,
	dnl always include inttypes.h as the very first thing.
	dnl We need to put this into OBJCFLAGS and not CPPFLAGS as CPPFLAGS are
	dnl also used for .S files.
	OBJCFLAGS="$OBJCFLAGS -include inttypes.h"
	dnl We need -latomic for GCC's atomics to work.
	LIBS="$LIBS -latomic"
	;;
*-*-mint*)
	enable_shared="no"
	enable_threads="no"	# TODO
	with_tls="no"
	;;
esac

AS_IF([test x"$host_os" = x"msdosdjgpp" -a x"$build_os" = x"msdosdjgpp"], [
	dnl Hack to make configure find these on DOS.
	: ${AR:=ar.exe}
	: ${GREP:=grep.exe}
	: ${RANLIB:=ranlib.exe}
])

AC_LANG([Objective C])
case "$host_os" in
morphos*)
	dnl Don't use clang on MorphOS - it does not support baserel, which is
	dnl required for the .library.
	potential_compilers="gcc"
	;;
*)
	potential_compilers="clang egcc gcc"
	;;
esac
AC_PROG_OBJC($potential_compilers)
AC_PROG_OBJCPP
AC_PROG_LN_S
AC_PROG_EGREP

BUILDSYS_CHECK_IOS

AC_ARG_WITH(wii,
	AS_HELP_STRING([--with-wii], [build for Wii]))
AS_IF([test x"$with_wii" = x"yes"], [
171
172
173
174
175
176
177

178
179
180
181
182
183
184
	CPPFLAGS="$CPPFLAGS -DGEKKO -I$DEVKITPRO/libogc/include"
	OBJFW_CPPFLAGS="$OBJFW_CPPFLAGS -DGEKKO -I\$DEVKITPRO/libogc/include"
	LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -meabi -mhard-float"
	LIBS="$LIBS -L$DEVKITPRO/libogc/lib/wii -lfat -logc"
	TESTS_LIBS="$TESTS_LIBS -lwiiuse -lbte"
	enable_shared="no"
	enable_threads="no"	# TODO


	AC_DEFINE(OF_WII, 1, [Whether we are compiling for Wii])
	AC_SUBST(MAP_LDFLAGS, ['-Wl,-Map,$@.map'])
])

AC_ARG_WITH(wii-u,
	AS_HELP_STRING([--with-wii-u], [build for Wii U]))







>







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
	CPPFLAGS="$CPPFLAGS -DGEKKO -I$DEVKITPRO/libogc/include"
	OBJFW_CPPFLAGS="$OBJFW_CPPFLAGS -DGEKKO -I\$DEVKITPRO/libogc/include"
	LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -meabi -mhard-float"
	LIBS="$LIBS -L$DEVKITPRO/libogc/lib/wii -lfat -logc"
	TESTS_LIBS="$TESTS_LIBS -lwiiuse -lbte"
	enable_shared="no"
	enable_threads="no"	# TODO
	with_tls="no"

	AC_DEFINE(OF_WII, 1, [Whether we are compiling for Wii])
	AC_SUBST(MAP_LDFLAGS, ['-Wl,-Map,$@.map'])
])

AC_ARG_WITH(wii-u,
	AS_HELP_STRING([--with-wii-u], [build for Wii U]))
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
	OBJFW_CPPFLAGS="$OBJFW_CPPFLAGS -DARM11 -I\$DEVKITPRO/libctru/include"
	ASFLAGS="$ASFLAGS -march=armv6k"
	LDFLAGS="$LDFLAGS -specs=3dsx.specs -march=armv6k -mtune=mpcore"
	LDFLAGS="$LDFLAGS -mfloat-abi=hard -mtp=soft -mword-relocations"
	LIBS="$LIBS -L$DEVKITPRO/libctru/lib -lctru"
	enable_shared="no"
	enable_threads="no"	# TODO

	check_pedantic="no"

	AC_DEFINE(OF_NINTENDO_3DS, 1,
		[Whether we are compiling for Nintendo 3DS])
	AC_SUBST(MAP_LDFLAGS, ['-Wl,-Map,$@.map'])
])

CPP="$OBJCPP"
CPPFLAGS="$CPPFLAGS $OBJCPPFLAGS -DOF_COMPILING_OBJFW"
flags="-fexceptions -fobjc-exceptions -funwind-tables"
flags="$flags -fconstant-string-class=OFConstantString"
OBJCFLAGS="$OBJCFLAGS -Wall $flags"
OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flags"
dnl amiga-gcc requires -fexceptions in LDFLAGS in order to link in the glue code
dnl for registering the frames.
LDFLAGS="$LDFLAGS -fexceptions"

case "$OBJC" in
	*clang*)
		case "$host" in

			dnl Clang generates MIPS assembly not accepted by GNU
			dnl as, however, Clang's integrated assembler doesn't
			dnl accept everything used in ObjFW's assembly files.
			dnl Therefore, use the integrated assembler for ObjC
			dnl files, but not for assembly files.
			mips*-*-*)
				flag="-integrated-as"
				OBJCFLAGS="$OBJCFLAGS $flag"
				OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flag"
				;;

			dnl Don't use -no-integrated-as on Darwin. It breaks
			dnl building for the iOS simulator.
			i?86-*-darwin* | x86_64-*-darwin*)
				;;
			dnl Many older Clang versions don't support jmp short.
			i?86-*-* | x86_64-*-*)
				ASFLAGS="$ASFLAGS -no-integrated-as"
				;;
			dnl Clang's assembler on Windows is not complete yet
			dnl and cannot compile all .S files.
			*-*-mingw*)
				ASFLAGS="$ASFLAGS -no-integrated-as"
				;;
			dnl Clang generates assembly output on SPARC64 that
			dnl OpenBSD's assembler does not accept.
			sparc64-*-*openbsd*)
				flag="-integrated-as"
				OBJCFLAGS="$OBJCFLAGS $flag"
				OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flag"
				;;
		esac
		;;
esac

AX_CHECK_COMPILER_FLAGS(-std=gnu11, [
	OBJCFLAGS="$OBJCFLAGS -std=gnu11"
], [
	AX_CHECK_COMPILER_FLAGS(-std=gnu1x, [
		OBJCFLAGS="$OBJCFLAGS -std=gnu1x"
	], [
		AX_CHECK_COMPILER_FLAGS(-std=gnu99,
			[OBJCFLAGS="$OBJCFLAGS -std=gnu99"])
	])
])

case "$build_os" in
	morphos*)
		# MorphOS 3.10 has a buggy ixemul that does not work with -pipe.
		;;
	*)
		AX_CHECK_COMPILER_FLAGS(-pipe, [OBJCFLAGS="$OBJCFLAGS -pipe"])
		;;
esac
AX_CHECK_COMPILER_FLAGS(-fno-common, [OBJCFLAGS="$OBJCFLAGS -fno-common"])
AX_CHECK_COMPILER_FLAGS(-Xclang -fno-constant-cfstrings, [
	flag="-Xclang -fno-constant-cfstrings"
	OBJCFLAGS="$OBJCFLAGS $flag"
	OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flag"
])
AX_CHECK_COMPILER_FLAGS([-Wsign-compare -Werror],







>


















|
|
>
|
|
|
|
|
<
<
|
|
|
>
|
|
<
|
<
|
<
<
<
<
<
<
<
|
|
<
|
|
|
|
|
|













<
<
<
<
<
|
<
<







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
	OBJFW_CPPFLAGS="$OBJFW_CPPFLAGS -DARM11 -I\$DEVKITPRO/libctru/include"
	ASFLAGS="$ASFLAGS -march=armv6k"
	LDFLAGS="$LDFLAGS -specs=3dsx.specs -march=armv6k -mtune=mpcore"
	LDFLAGS="$LDFLAGS -mfloat-abi=hard -mtp=soft -mword-relocations"
	LIBS="$LIBS -L$DEVKITPRO/libctru/lib -lctru"
	enable_shared="no"
	enable_threads="no"	# TODO
	with_tls="no"
	check_pedantic="no"

	AC_DEFINE(OF_NINTENDO_3DS, 1,
		[Whether we are compiling for Nintendo 3DS])
	AC_SUBST(MAP_LDFLAGS, ['-Wl,-Map,$@.map'])
])

CPP="$OBJCPP"
CPPFLAGS="$CPPFLAGS $OBJCPPFLAGS -DOF_COMPILING_OBJFW"
flags="-fexceptions -fobjc-exceptions -funwind-tables"
flags="$flags -fconstant-string-class=OFConstantString"
OBJCFLAGS="$OBJCFLAGS -Wall $flags"
OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flags"
dnl amiga-gcc requires -fexceptions in LDFLAGS in order to link in the glue code
dnl for registering the frames.
LDFLAGS="$LDFLAGS -fexceptions"

case "$OBJC" in
*clang*)
	case "$host" in
	mips*-*-*)
		dnl Clang generates MIPS assembly not accepted by GNU as,
		dnl however, Clang's integrated assembler doesn't accept
		dnl everything used in ObjFW's assembly files. Therefore, use
		dnl the integrated assembler for ObjC files, but not for
		dnl assembly files.


		OBJCFLAGS="$OBJCFLAGS -integrated-as"
		OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -integrated-as"
		;;
	i?86-*-darwin* | x86_64-*-darwin*)
		dnl Don't use -no-integrated-as on Darwin. It breaks building
		dnl for the iOS simulator.

		;;

	sparc64-*-*openbsd*)







		dnl Clang generates assembly output on SPARC64 that OpenBSD's
		dnl assembler does not accept.

		flag="-integrated-as"
		OBJCFLAGS="$OBJCFLAGS $flag"
		OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flag"
		;;
	esac
	;;
esac

AX_CHECK_COMPILER_FLAGS(-std=gnu11, [
	OBJCFLAGS="$OBJCFLAGS -std=gnu11"
], [
	AX_CHECK_COMPILER_FLAGS(-std=gnu1x, [
		OBJCFLAGS="$OBJCFLAGS -std=gnu1x"
	], [
		AX_CHECK_COMPILER_FLAGS(-std=gnu99,
			[OBJCFLAGS="$OBJCFLAGS -std=gnu99"])
	])
])






AX_CHECK_COMPILER_FLAGS(-pipe, [OBJCFLAGS="$OBJCFLAGS -pipe"])


AX_CHECK_COMPILER_FLAGS(-fno-common, [OBJCFLAGS="$OBJCFLAGS -fno-common"])
AX_CHECK_COMPILER_FLAGS(-Xclang -fno-constant-cfstrings, [
	flag="-Xclang -fno-constant-cfstrings"
	OBJCFLAGS="$OBJCFLAGS $flag"
	OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flag"
])
AX_CHECK_COMPILER_FLAGS([-Wsign-compare -Werror],
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
	[OBJCFLAGS="$OBJCFLAGS -Wsemicolon-before-method-body"])
AX_CHECK_COMPILER_FLAGS([-Wobjc-missing-property-synthesis -Werror],
	[OBJCFLAGS="$OBJCFLAGS -Wobjc-missing-property-synthesis"])
AX_CHECK_COMPILER_FLAGS([-Wmissing-method-return-type -Werror],
	[OBJCFLAGS="$OBJCFLAGS -Wmissing-method-return-type"])

case "$host" in
	m68k-*-amigaos*)
		dnl The inline headers generate code that triggers
		dnl -Wpointer-sign.
		OBJCFLAGS="$OBJCFLAGS -Wno-pointer-sign"
		;;
esac

AC_MSG_CHECKING(whether Objective C compiler supports properties)
AC_TRY_COMPILE([

	#ifdef __has_attribute
	# if __has_attribute(objc_root_class)
	__attribute__((__objc_root_class__))
	# endif
	#endif
	@interface Foo
	{
		id bar;
	}

	@property (nonatomic, retain) id bar;
	@end
], [
	Foo *foo = (id)0;
	[foo setBar: (id)0];
	foo = [foo bar];

], [
	AC_MSG_RESULT(yes)
], [
	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")
	AC_SUBST(EXCEPTIONS_EXCEPTIONS_LIB_A, "exceptions/exceptions.lib.a")
	AC_SUBST(FORWARDING_LIB_A, "forwarding.lib.a")
	AC_SUBST(FORWARDING_FORWARDING_LIB_A, "forwarding/forwarding.lib.a")
	AC_SUBST(INVOCATION_LIB_A, "invocation.lib.a")
	AC_SUBST(INVOCATION_INVOCATION_LIB_A, "invocation/invocation.lib.a")
	AC_SUBST(LOOKUP_ASM_LIB_A, "lookup-asm.lib.a")
	AC_SUBST(LOOKUP_ASM_LOOKUP_ASM_LIB_A, "lookup-asm/lookup-asm.lib.a")

	BUILDSYS_FRAMEWORK([
		AC_SUBST(OBJFW_FRAMEWORK, "ObjFW.framework")
		build_framework="yes"
	])
], [
	AC_DEFINE(OF_NO_SHARED, 1, [Whether no shared library was built])







|
|
<
|
|



|
>
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
>
















<

<
<
<

<







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
	[OBJCFLAGS="$OBJCFLAGS -Wsemicolon-before-method-body"])
AX_CHECK_COMPILER_FLAGS([-Wobjc-missing-property-synthesis -Werror],
	[OBJCFLAGS="$OBJCFLAGS -Wobjc-missing-property-synthesis"])
AX_CHECK_COMPILER_FLAGS([-Wmissing-method-return-type -Werror],
	[OBJCFLAGS="$OBJCFLAGS -Wmissing-method-return-type"])

case "$host" in
m68k-*-amigaos*)
	dnl The inline headers generate code that triggers -Wpointer-sign.

	OBJCFLAGS="$OBJCFLAGS -Wno-pointer-sign"
	;;
esac

AC_MSG_CHECKING(whether Objective C compiler supports properties)
AC_COMPILE_IFELSE([
	AC_LANG_PROGRAM([
		#ifdef __has_attribute
		# if __has_attribute(objc_root_class)
		__attribute__((__objc_root_class__))
		# endif
		#endif
		@interface Foo
		{
			id bar;
		}

		@property (nonatomic, retain) id bar;
		@end
	], [[
		Foo *foo = (id)0;
		[foo setBar: (id)0];
		foo = [foo bar];
	]])
], [
	AC_MSG_RESULT(yes)
], [
	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")

	AC_SUBST(FORWARDING_LIB_A, "forwarding.lib.a")



	AC_SUBST(LOOKUP_ASM_LIB_A, "lookup-asm.lib.a")


	BUILDSYS_FRAMEWORK([
		AC_SUBST(OBJFW_FRAMEWORK, "ObjFW.framework")
		build_framework="yes"
	])
], [
	AC_DEFINE(OF_NO_SHARED, 1, [Whether no shared library was built])
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
	TESTS_LIBS="-L../src/runtime -L../src/runtime/linklib $TESTS_LIBS"
	TESTS_LIBS="-L../src -lobjfw $TESTS_LIBS"
])

AC_ARG_ENABLE(amiga-lib,
	AS_HELP_STRING([--disable-amiga-lib], [do not build Amiga library]))
AS_IF([test x"$supports_amiga_lib" != x"yes"], [enable_amiga_lib="no"])

AS_IF([test x"$enable_shared" = x"no" -a x"$enable_amiga_lib" = x"no"], [
	enable_static="yes"


])

AC_ARG_ENABLE(static, AS_HELP_STRING([--enable-static], [build static library]))



AS_IF([test x"$enable_static" = x"yes" -o x"$enable_amiga_lib" != x"no"], [
	AC_SUBST(OBJFW_STATIC_LIB, "libobjfw.a")
	AC_SUBST(EXCEPTIONS_A, "exceptions.a")
	AC_SUBST(EXCEPTIONS_EXCEPTIONS_A, "exceptions/exceptions.a")
	AC_SUBST(FORWARDING_A, "forwarding.a")
	AC_SUBST(FORWARDING_FORWARDING_A, "forwarding/forwarding.a")
	AC_SUBST(INVOCATION_A, "invocation.a")
	AC_SUBST(INVOCATION_INVOCATION_A, "invocation/invocation.a")
	AC_SUBST(LOOKUP_ASM_A, "lookup-asm.a")
	AC_SUBST(LOOKUP_ASM_LOOKUP_ASM_A, "lookup-asm/lookup-asm.a")
])

AC_DEFINE_UNQUOTED(PLUGIN_SUFFIX, "$PLUGIN_SUFFIX", [Suffix for plugins])
AS_IF([test x"$enable_files" != x"no" -a x"$PLUGIN_SUFFIX" != x""], [
	AC_SUBST(USE_SRCS_PLUGINS, '${SRCS_PLUGINS}')
	AC_SUBST(TESTPLUGIN, "plugin")
	AC_DEFINE(OF_HAVE_PLUGINS, 1, [Whether we have plugin support])







|
|
|
>
>



>
>
>
|


<

<
<
<

<







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
	TESTS_LIBS="-L../src/runtime -L../src/runtime/linklib $TESTS_LIBS"
	TESTS_LIBS="-L../src -lobjfw $TESTS_LIBS"
])

AC_ARG_ENABLE(amiga-lib,
	AS_HELP_STRING([--disable-amiga-lib], [do not build Amiga library]))
AS_IF([test x"$supports_amiga_lib" != x"yes"], [enable_amiga_lib="no"])
AS_IF([test x"$enable_amiga_lib" != x"no"], [
	AC_SUBST(OBJFW_STATIC_LIB, "libobjfw.a")
	AC_SUBST(EXCEPTIONS_A, "exceptions.a")
	AC_SUBST(FORWARDING_A, "forwarding.a")
	AC_SUBST(LOOKUP_ASM_AMIGALIB_A, "lookup-asm.amigalib.a")
])

AC_ARG_ENABLE(static, AS_HELP_STRING([--enable-static], [build static library]))
AS_IF([test x"$enable_shared" = x"no" -a x"$enable_amiga_lib" = x"no"], [
	enable_static="yes"
])
AS_IF([test x"$enable_static" = x"yes"], [
	AC_SUBST(OBJFW_STATIC_LIB, "libobjfw.a")
	AC_SUBST(EXCEPTIONS_A, "exceptions.a")

	AC_SUBST(FORWARDING_A, "forwarding.a")



	AC_SUBST(LOOKUP_ASM_A, "lookup-asm.a")

])

AC_DEFINE_UNQUOTED(PLUGIN_SUFFIX, "$PLUGIN_SUFFIX", [Suffix for plugins])
AS_IF([test x"$enable_files" != x"no" -a x"$PLUGIN_SUFFIX" != x""], [
	AC_SUBST(USE_SRCS_PLUGINS, '${SRCS_PLUGINS}')
	AC_SUBST(TESTPLUGIN, "plugin")
	AC_DEFINE(OF_HAVE_PLUGINS, 1, [Whether we have plugin support])
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
])

AC_MSG_CHECKING(whether we need -D_GNU_SOURCE)
AC_EGREP_CPP(egrep_cpp_yes, [
	#include <stdlib.h>

	#if defined(__GLIBC__) || defined(__MINGW32__) || \
	    defined(__NEWLIB__) || defined(__MORPHOS__)
	egrep_cpp_yes
	#endif
], [
	AC_MSG_RESULT(yes)
	CPPFLAGS="-D_GNU_SOURCE $CPPFLAGS"
	gnu_source="yes"
], [
	AC_MSG_RESULT(no)
])

case "$host_os" in
	solaris*)
		CPPFLAGS="-D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS $CPPFLAGS"
		;;
esac

objc_runtime="ObjFW runtime"
AC_CHECK_HEADER(objc/objc.h)
AC_MSG_CHECKING(which Objective C runtime to use)
AC_ARG_ENABLE(runtime,
	AS_HELP_STRING([--enable-runtime], [use the included runtime]))







|











|
|
|







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
])

AC_MSG_CHECKING(whether we need -D_GNU_SOURCE)
AC_EGREP_CPP(egrep_cpp_yes, [
	#include <stdlib.h>

	#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
solaris*)
	CPPFLAGS="-D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS $CPPFLAGS"
	;;
esac

objc_runtime="ObjFW runtime"
AC_CHECK_HEADER(objc/objc.h)
AC_MSG_CHECKING(which Objective C runtime to use)
AC_ARG_ENABLE(runtime,
	AS_HELP_STRING([--enable-runtime], [use the included runtime]))
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510

511
512
513
514
515
516
517
			:
		])
	])
])
AC_MSG_RESULT($objc_runtime)

case "$objc_runtime" in
	"ObjFW runtime")
		AC_DEFINE(OF_OBJFW_RUNTIME, 1,
			[Whether we use the ObjFW runtime])

		AC_MSG_CHECKING([whether -fobjc-runtime=objfw is supported])

		old_OBJCFLAGS="$OBJCFLAGS"
		OBJCFLAGS="$OBJCFLAGS -Xclang -fobjc-runtime=objfw"
		AC_TRY_LINK([

			#ifdef __has_attribute
			# if __has_attribute(objc_root_class)
			__attribute__((__objc_root_class__))
			# endif
			#endif
			@interface Test
			+ (void)test;







|
|
<

|

|
|
|
>







489
490
491
492
493
494
495
496
497

498
499
500
501
502
503
504
505
506
507
508
509
510
511
			:
		])
	])
])
AC_MSG_RESULT($objc_runtime)

case "$objc_runtime" in
"ObjFW runtime")
	AC_DEFINE(OF_OBJFW_RUNTIME, 1, [Whether we use the ObjFW runtime])


	AC_MSG_CHECKING([whether -fobjc-runtime=objfw is supported])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Xclang -fobjc-runtime=objfw"
	AC_LINK_IFELSE([
		AC_LANG_PROGRAM([
			#ifdef __has_attribute
			# if __has_attribute(objc_root_class)
			__attribute__((__objc_root_class__))
			# endif
			#endif
			@interface Test
			+ (void)test;
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
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
				return (void *)0;
			}

			void
			__objc_exec_class(void *module)
			{
			}
		], [
			[Test test];

		], [
			flag="-Xclang -fobjc-runtime=objfw"
			OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flag"
			AC_MSG_RESULT(yes)
		], [
			flag="-fgnu-runtime"
			OBJCFLAGS="$old_OBJCFLAGS $flag"
			OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flag"
			AC_MSG_RESULT(no)
			old_compiler="yes"
		])

		AC_SUBST(RUNTIME, "runtime")
		AC_CONFIG_FILES(src/runtime/Info.plist)

		AS_IF([test x"$enable_shared" != x"no"], [
			AC_SUBST(OBJFWRT_SHARED_LIB,
				"${LIB_PREFIX}objfwrt${LIB_SUFFIX}")
		])

		AS_IF([test x"$enable_static" = x"yes"], [
			AC_SUBST(OBJFWRT_STATIC_LIB, "libobjfwrt.a")
		])

		AS_IF([test x"$build_framework" = x"yes"], [
			AC_SUBST(OBJFWRT_FRAMEWORK, "ObjFWRT.framework")
			AC_SUBST(RUNTIME_FRAMEWORK_LIBS, "-framework ObjFWRT")
		])

		AS_IF([test x"$enable_amiga_lib" != x"no"], [
			AC_SUBST(RUNTIME_LIBS, "-lobjfwrt.library")
			AC_SUBST(LINKLIB, linklib)
			tmp="../src/runtime/linklib/libobjfwrt.library.a"
			AC_SUBST(LIBOBJFWRT_DEP, "$tmp")
			AC_SUBST(LIBOBJFWRT_DEP_LVL2, "../$tmp")
		], [
			AC_SUBST(RUNTIME_LIBS, "-lobjfwrt")
		])

		AS_IF([test x"$enable_shared" = x"no" \
				-a x"$enable_amiga_lib" = x"no"], [
			tmp="../src/runtime/libobjfwrt.a"
			AC_SUBST(LIBOBJFWRT_DEP, "$tmp")
			AC_SUBST(LIBOBJFWRT_DEP_LVL2, "../$tmp")
		])

		AS_IF([test x"$enable_seluid24" = x"yes"], [
			AC_DEFINE(OF_SELUID24, 1,
				[Whether to use 24 bit selector UIDs])
		])

		AC_MSG_CHECKING(for exception type)
		AC_TRY_COMPILE([

			extern void foo();
		], [
			@try {
				foo();
			} @finally {
				foo();
			}

		], [
			AS_IF([$EGREP __gnu_objc_personality_v0 \
					conftest.$ac_objext >/dev/null], [
				exception_type="DWARF"
			])
			AS_IF([$EGREP __gnu_objc_personality_sj0 \
					conftest.$ac_objext >/dev/null], [
				exception_type="SjLj"
			])
			AS_IF([$EGREP __gnu_objc_personality_seh0 \
					conftest.$ac_objext >/dev/null], [
				exception_type="SEH"
			])

			raise_exception="_Unwind_RaiseException"

			case "$exception_type" in
			DWARF)
				AC_DEFINE(HAVE_DWARF_EXCEPTIONS, 1,
					[Whether DWARF exceptions are used])

				;;
			SjLj)
				AC_DEFINE(HAVE_SJLJ_EXCEPTIONS, 1,
					[Whether SjLj exceptions are used])
				raise_exception="_Unwind_SjLj_RaiseException"
				;;
			SEH)
				AC_DEFINE(HAVE_SEH_EXCEPTIONS, 1,
					[Whether SEH exceptions are used])

				;;
			*)
				AC_MSG_RESULT(unknown)
				AC_MSG_ERROR([Exception type not detected!])
				;;
			esac

			AC_MSG_RESULT($exception_type)
		], [
			AC_MSG_RESULT(exceptions unavailable!)
			AC_MSG_ERROR([Exceptions not accepted by compiler!])
		])

		AC_SEARCH_LIBS($raise_exception, [c++abi gcc_s gcc], [
			dnl c++abi requires pthread on OpenBSD
			AS_IF([test x"$ac_lib" = x"c++abi"], [
				LIBS="$LIBS -lpthread"
			])
		], [
			AC_MSG_ERROR([$raise_exception missing!])
		], [-lpthread])

		AC_CHECK_FUNCS(_Unwind_GetDataRelBase _Unwind_GetTextRelBase)
		;;
	"Apple runtime")
		AC_DEFINE(OF_APPLE_RUNTIME, 1,
			[Whether we use the Apple ObjC runtime])

		AC_CHECK_LIB(objc, objc_msgSend, [
			AC_SUBST(RUNTIME_LIBS, "-lobjc")
			AC_SUBST(RUNTIME_FRAMEWORK_LIBS, "-lobjc")
		], [
			AC_MSG_ERROR([libobjc not found!])
		])




		AC_CHECK_FUNC(objc_autoreleasePoolPush, [], [
			AC_SUBST(RUNTIME_AUTORELEASE_M, "runtime/autorelease.m")
		])
		AC_CHECK_FUNC(objc_constructInstance, [], [
			AC_SUBST(RUNTIME_INSTANCE_M, "runtime/instance.m")
		])


		;;
esac









AC_CHECK_FUNCS(_Unwind_Backtrace)



case "$host_os" in
	darwin*)
		AC_SUBST(LDFLAGS_REEXPORT, ["-Wl,-reexport-lobjfw"])
		AS_IF([test x"$objc_runtime" = x"Apple runtime"], [
			AC_SUBST(REEXPORT_RUNTIME, ["-Wl,-reexport-lobjc"])
			AC_SUBST(REEXPORT_RUNTIME_FRAMEWORK,
				["-Wl,-reexport-lobjc"])
			LDFLAGS="$LDFLAGS -Wl,-U,_NSFoundationVersionNumber"
		])

		AS_IF([test x"$objc_runtime" = x"ObjFW runtime"], [
			AS_IF([test x"$exception_type" = x"DWARF"], [
				LDFLAGS="$LDFLAGS -Wl,-U,___gxx_personality_v0"
			])
			AS_IF([test x"$exception_type" = x"SjLj"], [
				LDFLAGS="$LDFLAGS -Wl,-U,___gxx_personality_sj0"
			])
			AC_SUBST(REEXPORT_RUNTIME, ["-Wl,-reexport-lobjfwrt"])
			AC_SUBST(REEXPORT_RUNTIME_FRAMEWORK,
				["-Wl,-reexport_framework,ObjFWRT"])
		])

		AC_CHECK_HEADERS(sysdir.h)
		AC_CHECK_FUNCS(sysdir_start_search_path_enumeration)

		AS_IF([test x"$host_is_ios" = x"yes"], [
			AC_SUBST(TESTS_STATIC_LIB, tests.a)
			TESTS_LIBS="$TESTS_LIBS -framework CoreFoundation"
		])
		;;
esac

AC_MSG_CHECKING(whether Objective C compiler supports ARC)
old_OBJCFLAGS="$OBJCFLAGS"
OBJCFLAGS="$OBJCFLAGS -fobjc-arc -fobjc-arc-exceptions"
AC_TRY_COMPILE([

	#ifdef __has_attribute
	# if __has_attribute(objc_root_class)
	__attribute__((__objc_root_class__))
	# endif
	#endif
	@interface Foo
	{
		struct objc_class *_isa;
	}

	+ (id)alloc;
	@end

	@implementation Foo
	+ (id)alloc
	{
		return (id)0;
	}
	@end
], [
	__weak id foo = [Foo alloc];
	(void)foo;

], [
	AC_MSG_RESULT(yes)
	AC_DEFINE(COMPILER_SUPPORTS_ARC, 1, [Whether the compiler supports ARC])
	AC_SUBST(RUNTIME_ARC_TESTS_M, RuntimeARCTests.m)
], [
	AC_MSG_RESULT(no)
])
OBJCFLAGS="$old_OBJCFLAGS"

AC_C_BIGENDIAN([
	AC_DEFINE(OF_BIG_ENDIAN, 1, [Whether we are big endian])
])
AS_IF([test x"$ac_cv_c_bigendian" = x"universal"], [
	AC_DEFINE(OF_UNIVERSAL, 1, [Whether we are building a universal binary])
])

AC_MSG_CHECKING(for SIZE_MAX)
AC_EGREP_CPP(egrep_cpp_yes, [
	#include <stdint.h>
	#include <limits.h>

	#ifdef SIZE_MAX
	egrep_cpp_yes
	#endif
], [
	AC_MSG_RESULT(yes)
], [
	AC_MSG_RESULT(no)
	AC_MSG_CHECKING(for SIZE_T_MAX)
	AC_EGREP_CPP(egrep_cpp_yes, [
		#include <stdint.h>
		#include <limits.h>

		#ifdef SIZE_T_MAX
		egrep_cpp_yes
		#endif
	], [
		AC_MSG_RESULT(yes)
		size_max="SIZE_T_MAX"
	], [
		AC_MSG_RESULT(no)
		size_max="(~(size_t)0)"
	])
	AC_DEFINE_UNQUOTED(SIZE_MAX, $size_max, [Maximum value for size_t])
])
AC_MSG_CHECKING(for SSIZE_MAX)
AC_EGREP_CPP(egrep_cpp_yes, [
	#include <stdint.h>
	#include <limits.h>

	#ifdef SSIZE_MAX
	egrep_cpp_yes
	#endif
], [
	AC_MSG_RESULT(yes)
], [
	AC_MSG_RESULT(no)
	AC_DEFINE(SSIZE_MAX, [((ssize_t)(SIZE_MAX / 2))],
		[Maximum value for ssize_t])
])
AC_MSG_CHECKING(for UINTPTR_MAX)
AC_EGREP_CPP(egrep_cpp_yes, [
	#include <stdint.h>
	#include <limits.h>

	#ifdef UINTPTR_MAX
	egrep_cpp_yes
	#endif
], [
	AC_MSG_RESULT(yes)
], [
	AC_MSG_RESULT(no)

	AC_DEFINE(UINTPTR_MAX, [(~(uintptr_t)0)], [Maximum value for uintptr_t])

])

AC_CHECK_HEADER(inttypes.h,
	[AC_DEFINE(OF_HAVE_INTTYPES_H, 1, [Whether we have inttypes.h])])

AC_CHECK_HEADER(sys/types.h,
	[AC_DEFINE(OF_HAVE_SYS_TYPES_H, 1, [Whether we have sys/types.h])])







|

>
|
<
|
|
|
<
|
|
|
|
|

|
|

|
|
|
|

|
|
|

|
|
|
|

|
|
|
|
|
|
|
|
|

|
|
<
|
|
|

|
|
<
|

|
|
>







>
|
|
|
|
|
|
|
|
|
|
|
|
|

<
<
|
|
|
|
>
|
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|

|
|
|
|
|

|
|
|
<
<
|
|
|

|
|
|
|
<

|
|
|
|
|
|

>
>
>
|
|
|
|
|
|
>
>
|


>
>
>
>
>
>
>
>
|
>
>


|
|
|
|
|
<
|
|

|
|
|
|
|
|
|
|
|
|
|

|
|

|
|
|
|
|





|
>
|
|
|
|
|
|
|
|
|

|
|

|
|
|
|
|
|
|
|
|
>
















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












<
|













>
|
>







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
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
				return (void *)0;
			}

			void
			__objc_exec_class(void *module)
			{
			}
		], [[
			[Test test];
		]])
	], [

		OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -Xclang -fobjc-runtime=objfw"
		AC_MSG_RESULT(yes)
	], [

		OBJCFLAGS="$old_OBJCFLAGS -fgnu-runtime"
		OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -fgnu-runtime"
		AC_MSG_RESULT(no)
		old_compiler="yes"
	])

	AC_SUBST(RUNTIME, "runtime")
	AC_CONFIG_FILES(src/runtime/Info.plist)

	AS_IF([test x"$enable_shared" != x"no"], [
		AC_SUBST(OBJFWRT_SHARED_LIB,
			"${LIB_PREFIX}objfwrt${LIB_SUFFIX}")
	])

	AS_IF([test x"$enable_static" = x"yes"], [
		AC_SUBST(OBJFWRT_STATIC_LIB, "libobjfwrt.a")
	])

	AS_IF([test x"$build_framework" = x"yes"], [
		AC_SUBST(OBJFWRT_FRAMEWORK, "ObjFWRT.framework")
		AC_SUBST(RUNTIME_FRAMEWORK_LIBS, "-framework ObjFWRT")
	])

	AS_IF([test x"$enable_amiga_lib" != x"no"], [
		AC_SUBST(RUNTIME_LIBS, "-lobjfwrt.library")
		AC_SUBST(LINKLIB, linklib)
		tmp="../src/runtime/linklib/libobjfwrt.library.a"
		AC_SUBST(LIBOBJFWRT_DEP, "$tmp")
		AC_SUBST(LIBOBJFWRT_DEP_LVL2, "../$tmp")
	], [
		AC_SUBST(RUNTIME_LIBS, "-lobjfwrt")
	])

	AS_IF([test x"$enable_shared" = x"no" \
	    -a x"$enable_amiga_lib" = x"no"], [

		AC_SUBST(LIBOBJFWRT_DEP, "../src/runtime/libobjfwrt.a")
		AC_SUBST(LIBOBJFWRT_DEP_LVL2, "../../src/runtime/libobjfwrt.a")
	])

	AS_IF([test x"$enable_seluid24" = x"yes"], [
		AC_DEFINE(OF_SELUID24, 1, [Whether to use 24 bit selector UIDs])

	])

	AC_MSG_CHECKING(for exception type)
	AC_COMPILE_IFELSE([
		AC_LANG_PROGRAM([
			extern void foo();
		], [
			@try {
				foo();
			} @finally {
				foo();
			}
		])
	], [
		AS_IF([$EGREP __gnu_objc_personality_v0 conftest.$ac_objext \
		    >/dev/null], [
			exception_type="DWARF"
		])
		AS_IF([$EGREP __gnu_objc_personality_sj0 conftest.$ac_objext \
		    >/dev/null], [
			exception_type="SjLj"
		])
		AS_IF([$EGREP __gnu_objc_personality_seh0 conftest.$ac_objext \
		    >/dev/null], [
			exception_type="SEH"
		])



		case "$exception_type" in
		DWARF)
			AC_DEFINE(HAVE_DWARF_EXCEPTIONS, 1,
				[Whether DWARF exceptions are used])
			raise_exception="_Unwind_RaiseException"
			;;
		SjLj)
			AC_DEFINE(HAVE_SJLJ_EXCEPTIONS, 1,
				[Whether SjLj exceptions are used])
			raise_exception="_Unwind_SjLj_RaiseException"
			;;
		SEH)
			AC_DEFINE(HAVE_SEH_EXCEPTIONS, 1,
				[Whether SEH exceptions are used])
			raise_exception="_Unwind_RaiseException"
			;;
		*)
			AC_MSG_RESULT(unknown)
			AC_MSG_ERROR([Exception type not detected!])
			;;
		esac

		AC_MSG_RESULT($exception_type)
	], [
		AC_MSG_RESULT(exceptions unavailable!)
		AC_MSG_ERROR([Exceptions not accepted by compiler!])
	])

	AC_SEARCH_LIBS($raise_exception, [c++abi gcc_s gcc unwind], [
		dnl c++abi requires pthread on OpenBSD
		AS_IF([test x"$ac_lib" = x"c++abi"], [LIBS="$LIBS -lpthread"])


	], [
		AC_MSG_ERROR([$raise_exception missing!])
	], [-lpthread])

	AC_CHECK_FUNCS(_Unwind_GetDataRelBase _Unwind_GetTextRelBase)
	;;
"Apple runtime")
	AC_DEFINE(OF_APPLE_RUNTIME, 1, [Whether we use the Apple ObjC runtime])


	AC_CHECK_LIB(objc, objc_msgSend, [
		AC_SUBST(RUNTIME_LIBS, "-lobjc")
		AC_SUBST(RUNTIME_FRAMEWORK_LIBS, "-lobjc")
	], [
		AC_MSG_ERROR([libobjc not found!])
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -lobjc"

	AC_CHECK_FUNC(objc_autoreleasePoolPush, [], [
		AC_SUBST(RUNTIME_AUTORELEASE_M, "runtime/autorelease.m")
	])
	AC_CHECK_FUNC(objc_constructInstance, [], [
		AC_SUBST(RUNTIME_INSTANCE_M, "runtime/instance.m")
	])

	OBJCFLAGS="$old_OBJCFLAGS"
	;;
esac

case "$host_os" in
mint*)
	dnl _Unwind_Backtrace crashes on MiNT
	;;
hpux*)
	dnl _Unwind_Backtrace() returns complete garbage on HP-UX.
	;;
*)
	AC_CHECK_FUNCS(_Unwind_Backtrace)
	;;
esac

case "$host_os" in
darwin*)
	AC_SUBST(LDFLAGS_REEXPORT, ["-Wl,-reexport-lobjfw"])
	AS_IF([test x"$objc_runtime" = x"Apple runtime"], [
		AC_SUBST(REEXPORT_RUNTIME, ["-Wl,-reexport-lobjc"])
		AC_SUBST(REEXPORT_RUNTIME_FRAMEWORK, ["-Wl,-reexport-lobjc"])

		LDFLAGS="$LDFLAGS -Wl,-U,_NSFoundationVersionNumber"
	])

	AS_IF([test x"$objc_runtime" = x"ObjFW runtime"], [
		AS_IF([test x"$exception_type" = x"DWARF"], [
			LDFLAGS="$LDFLAGS -Wl,-U,___gxx_personality_v0"
		])
		AS_IF([test x"$exception_type" = x"SjLj"], [
			LDFLAGS="$LDFLAGS -Wl,-U,___gxx_personality_sj0"
		])
		AC_SUBST(REEXPORT_RUNTIME, ["-Wl,-reexport-lobjfwrt"])
		AC_SUBST(REEXPORT_RUNTIME_FRAMEWORK,
			["-Wl,-reexport_framework,ObjFWRT"])
	])

	AC_CHECK_HEADERS(sysdir.h)
	AC_CHECK_FUNCS(sysdir_start_search_path_enumeration)

	AS_IF([test x"$host_is_ios" = x"yes"], [
		AC_SUBST(TESTS_STATIC_LIB, tests.a)
		TESTS_LIBS="$TESTS_LIBS -framework CoreFoundation"
	])
	;;
esac

AC_MSG_CHECKING(whether Objective C compiler supports ARC)
old_OBJCFLAGS="$OBJCFLAGS"
OBJCFLAGS="$OBJCFLAGS -fobjc-arc -fobjc-arc-exceptions"
AC_COMPILE_IFELSE([
	AC_LANG_PROGRAM([
		#ifdef __has_attribute
		# if __has_attribute(objc_root_class)
		__attribute__((__objc_root_class__))
		# endif
		#endif
		@interface Foo
		{
			struct objc_class *_isa;
		}

		+ (id)alloc;
		@end

		@implementation Foo
		+ (id)alloc
		{
			return (id)0;
		}
		@end
	], [[
		__weak id foo = [Foo alloc];
		(void)foo;
	]])
], [
	AC_MSG_RESULT(yes)
	AC_DEFINE(COMPILER_SUPPORTS_ARC, 1, [Whether the compiler supports ARC])
	AC_SUBST(RUNTIME_ARC_TESTS_M, RuntimeARCTests.m)
], [
	AC_MSG_RESULT(no)
])
OBJCFLAGS="$old_OBJCFLAGS"

AC_C_BIGENDIAN([
	AC_DEFINE(OF_BIG_ENDIAN, 1, [Whether we are big endian])
])
AS_IF([test x"$ac_cv_c_bigendian" = x"universal"], [
	AC_DEFINE(OF_UNIVERSAL, 1, [Whether we are building a universal binary])
])






























AC_MSG_CHECKING(for SSIZE_MAX)
AC_EGREP_CPP(egrep_cpp_yes, [
	#include <stdint.h>
	#include <limits.h>

	#ifdef SSIZE_MAX
	egrep_cpp_yes
	#endif
], [
	AC_MSG_RESULT(yes)
], [
	AC_MSG_RESULT(no)

	AC_DEFINE(SSIZE_MAX, [(SIZE_MAX / 2)], [Maximum value for ssize_t])
])
AC_MSG_CHECKING(for UINTPTR_MAX)
AC_EGREP_CPP(egrep_cpp_yes, [
	#include <stdint.h>
	#include <limits.h>

	#ifdef UINTPTR_MAX
	egrep_cpp_yes
	#endif
], [
	AC_MSG_RESULT(yes)
], [
	AC_MSG_RESULT(no)
	AC_CHECK_SIZEOF(uintptr_t)
	AC_DEFINE(UINTPTR_MAX,
		[(SIZEOF_UINTPTR_T * CHAR_BIT)], [Maximum value for uintptr_t])
])

AC_CHECK_HEADER(inttypes.h,
	[AC_DEFINE(OF_HAVE_INTTYPES_H, 1, [Whether we have inttypes.h])])

AC_CHECK_HEADER(sys/types.h,
	[AC_DEFINE(OF_HAVE_SYS_TYPES_H, 1, [Whether we have sys/types.h])])
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
AS_IF([test x"$ac_cv_sizeof_float" != x"4" -o x"$ac_cv_sizeof_double" != x"8"],
	[AC_MSG_ERROR(
		[Floating point implementation does not conform to IEEE 754!])])

AC_MSG_CHECKING(for floating point endianess)
fp_endianess="unknown"
AS_IF([test x"$ac_cv_c_bigendian" != x"universal"], [
	AC_TRY_COMPILE([

		double endianess = 2.993700760838795055656993580068609688772747263874402942272934826871811872228512759832626847251963763755836687759498519784550143745834860002945223766052808125982053455555265216112722718870586961456110693379343178124592311441022662940307099598578775368547768968914916965731708568179631324904813506101190853720749196062963892799499230635163056742330563321122389331703618066046034494287335316842529021563862331183541255013987734473643350285400060357711238514186776429325214739886098119655678483017894951556639821088508565036657794343031121375178126860889964700274558728491825977274341798997758923017217660272136611938897932105874133412726223468780517578125e-259;
	], [
	], [
		AS_IF([$EGREP BigEnd conftest.$ac_objext >/dev/null], [
			AC_DEFINE(OF_FLOAT_BIG_ENDIAN, 1,
				[Whether floats are big endian])
			fp_endianess="big endian"
		], [
			AS_IF([$EGREP dnEgiB conftest.$ac_objext >/dev/null], [
				fp_endianess="little endian"
			])
		])
	])
], [
	fp_endianess="universal"
])
AC_MSG_RESULT($fp_endianess)
AS_IF([test x"$fp_endianess" = x"unknown"], [
	AC_MSG_ERROR(
		[Floating point implementation does not conform to IEEE 754!])])

AC_MSG_CHECKING(for INFINITY)
AC_TRY_COMPILE([
	#include <stdio.h>
	#include <math.h>
], [
	printf("%f", INFINITY);
], [
	AC_MSG_RESULT(yes)
], [
	AC_MSG_RESULT(no)

	AC_MSG_CHECKING(for __builtin_inf)
	AC_TRY_COMPILE([
		#include <stdio.h>
	], [
		printf("%f", __builtin_inf());
	], [
		AC_MSG_RESULT(yes)
		AC_DEFINE(INFINITY, [(__builtin_inf())],
			[Workaround for missing INFINITY])
	], [
		AC_MSG_RESULT(no)

		AC_MSG_ERROR([Neither INFINITY or __builtin_inf was found!])
	])
])

case "$host_cpu" in
	arm* | earm*)
		AC_MSG_CHECKING(for VFP2 or above)
		AC_TRY_COMPILE([], [

			#if !defined(__arm64__) && !defined(__aarch64__) && \
			    !defined(__ARM64_ARCH_8__)
			__asm__ __volatile__ (
			    "vstmdb	sp!, {d0-d7}"
			);
			#endif

		], [
			AC_DEFINE(HAVE_VFP2, 1, [Whether we have VFP2 or above])
			AC_MSG_RESULT(yes)
		], [
			AC_MSG_RESULT(no)
		])
		;;
esac

AC_CHECK_LIB(m, fmod, LIBS="$LIBS -lm")
AC_CHECK_LIB(complex, creal, TESTS_LIBS="$TESTS_LIBS -lcomplex")



AC_CHECK_FUNC(asprintf, [
	case "$host" in




		*-*-mingw*)
			dnl asprintf from MinGW is broken on older Windows
			dnl versions
			have_asprintf="no"
			;;
		*-psp-*)
			dnl asprintf is broken on the PSP
			have_asprintf="no"
			;;
		*)
			have_asprintf="yes"
			AC_DEFINE(HAVE_ASPRINTF, 1,
				[Whether we have asprintf()])
		;;
	esac
], [
	have_asprintf="no"
])

AC_ARG_ENABLE(unicode-tables,
	AS_HELP_STRING([--disable-unicode-tables], [Disable Unicode tables]))
AS_IF([test x"$enable_unicode_tables" != x"no"], [
	AC_DEFINE(OF_HAVE_UNICODE_TABLES, 1,
		[Whether to build with Unicode tables])
	AC_SUBST(UNICODE_M, "unicode.m")
])

ENCODINGS_SRCS=""
AC_DEFUN([ENCODING_FLAG], [
	AC_ARG_ENABLE($1,
		AS_HELP_STRING([--disable-$1],
			[Disables support for $3]))
	AS_IF([test x"$enable_$2" != x"no"], [
		AC_DEFINE($4, 1,
			[Whether we have support for $3])
		ENCODINGS_SRCS="$ENCODINGS_SRCS $2.m"
	])
])
ENCODING_FLAG(codepage-437, codepage_437, [Codepage 437], HAVE_CODEPAGE_437)
ENCODING_FLAG(codepage-850, codepage_850, [Codepage 850], HAVE_CODEPAGE_850)
ENCODING_FLAG(codepage-858, codepage_858, [Codepage 858], HAVE_CODEPAGE_858)
ENCODING_FLAG(iso-8859-2, iso_8859-2, [ISO 8859-2], HAVE_ISO_8859_2)
ENCODING_FLAG(iso-8859-3, iso_8859-3, [ISO 8859-3], HAVE_ISO_8859_3)
ENCODING_FLAG(iso-8859-15, iso_8859-15, [ISO 8859-15], HAVE_ISO_8859_15)
ENCODING_FLAG(koi8-r, koi8-r, [KOI8-R], HAVE_KOI8_R)
ENCODING_FLAG(koi8-u, koi8-u, [KOI8-U], HAVE_KOI8_U)
ENCODING_FLAG(mac-roman, mac_roman, [Mac Roman encoding], HAVE_MAC_ROMAN)
ENCODING_FLAG(windows-1251, windows-1251, [Windows-1251], HAVE_WINDOWS_1251)
ENCODING_FLAG(windows-1252, windows-1252, [Windows-1252], HAVE_WINDOWS_1252)

AC_SUBST(ENCODINGS_SRCS)
AS_IF([test x"$ENCODINGS_SRCS" != x""], [


	AC_SUBST(ENCODINGS, "encodings")

	AS_IF([test x"$enable_shared" != x"no"], [
		AC_SUBST(ENCODINGS_LIB_A, "encodings.lib.a")
		AC_SUBST(ENCODINGS_ENCODINGS_LIB_A, "encodings/encodings.lib.a")
	])
	AS_IF([test x"$enable_static" = x"yes" -o x"$enable_shared" = x"no"], [
		AC_SUBST(ENCODINGS_A, "encodings.a")
		AC_SUBST(ENCODINGS_ENCODINGS_A, "encodings/encodings.a")
	])
])

AC_CHECK_FUNCS(arc4random arc4random_buf getrandom random, break)

AS_IF([test x"$host_os" != x"morphos"], [
	AC_CHECK_LIB(dl, dlopen, LIBS="$LIBS -ldl")
])
AC_CHECK_HEADERS_ONCE(dlfcn.h)
case "$host_os" in
	netbsd*)
		dnl dladdr exists on NetBSD, but it is completely broken.
		dnl When using it with code that uses __thread, it freezes the
		dnl process so that it has to be killed using SIGKILL.
		dnl When disabling __thread, it doesn't freeze, but all symbols
		dnl are wrong.
		;;
	*)
		AC_CHECK_FUNCS(dladdr)
		;;
esac

AC_CHECK_HEADERS(sys/mman.h)
AC_CHECK_FUNCS(mmap mlock)

AC_ARG_ENABLE(threads,
	AS_HELP_STRING([--disable-threads], [disable thread support]))







|
>
|
|



















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

|
|
|
>






>
|
|
|
|
|
|
|





>
>


>
>
>
>
|
|
<
|
|
|
|
|
|
|
|
|
<




















<
|
|





|
|
|
|
|

|
|

<
|
>
>
|
<
|
|
<
|
|
|
<
<









|
|
|
|
|
|
|
|
|
|







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
AS_IF([test x"$ac_cv_sizeof_float" != x"4" -o x"$ac_cv_sizeof_double" != x"8"],
	[AC_MSG_ERROR(
		[Floating point implementation does not conform to IEEE 754!])])

AC_MSG_CHECKING(for floating point endianess)
fp_endianess="unknown"
AS_IF([test x"$ac_cv_c_bigendian" != x"universal"], [
	AC_COMPILE_IFELSE([
		AC_LANG_SOURCE([
			double endianess = 2.993700760838795055656993580068609688772747263874402942272934826871811872228512759832626847251963763755836687759498519784550143745834860002945223766052808125982053455555265216112722718870586961456110693379343178124592311441022662940307099598578775368547768968914916965731708568179631324904813506101190853720749196062963892799499230635163056742330563321122389331703618066046034494287335316842529021563862331183541255013987734473643350285400060357711238514186776429325214739886098119655678483017894951556639821088508565036657794343031121375178126860889964700274558728491825977274341798997758923017217660272136611938897932105874133412726223468780517578125e-259;
		])
	], [
		AS_IF([$EGREP BigEnd conftest.$ac_objext >/dev/null], [
			AC_DEFINE(OF_FLOAT_BIG_ENDIAN, 1,
				[Whether floats are big endian])
			fp_endianess="big endian"
		], [
			AS_IF([$EGREP dnEgiB conftest.$ac_objext >/dev/null], [
				fp_endianess="little endian"
			])
		])
	])
], [
	fp_endianess="universal"
])
AC_MSG_RESULT($fp_endianess)
AS_IF([test x"$fp_endianess" = x"unknown"], [
	AC_MSG_ERROR(
		[Floating point implementation does not conform to IEEE 754!])])




























case "$host_cpu" in
arm* | earm*)
	AC_MSG_CHECKING(for VFP2 or above)
	AC_COMPILE_IFELSE([
		AC_LANG_PROGRAM([], [
			#if !defined(__arm64__) && !defined(__aarch64__) && \
			    !defined(__ARM64_ARCH_8__)
			__asm__ __volatile__ (
			    "vstmdb	sp!, {d0-d7}"
			);
			#endif
		])
	], [
		AC_DEFINE(HAVE_VFP2, 1, [Whether we have VFP2 or above])
		AC_MSG_RESULT(yes)
	], [
		AC_MSG_RESULT(no)
	])
	;;
esac

AC_CHECK_LIB(m, fmod, LIBS="$LIBS -lm")
AC_CHECK_LIB(complex, creal, TESTS_LIBS="$TESTS_LIBS -lcomplex")

AC_CHECK_FUNCS(strtof truncf)

AC_CHECK_FUNC(asprintf, [
	case "$host" in
	*-*-mint*)
		dnl asprintf is not in headers
		have_asprintf="no"
		;;
	*-*-mingw*)
		dnl asprintf from MinGW is broken on older Windows versions

		have_asprintf="no"
		;;
	*-psp-*)
		dnl asprintf is broken on the PSP
		have_asprintf="no"
		;;
	*)
		have_asprintf="yes"
		AC_DEFINE(HAVE_ASPRINTF, 1, [Whether we have asprintf()])

		;;
	esac
], [
	have_asprintf="no"
])

AC_ARG_ENABLE(unicode-tables,
	AS_HELP_STRING([--disable-unicode-tables], [Disable Unicode tables]))
AS_IF([test x"$enable_unicode_tables" != x"no"], [
	AC_DEFINE(OF_HAVE_UNICODE_TABLES, 1,
		[Whether to build with Unicode tables])
	AC_SUBST(UNICODE_M, "unicode.m")
])

ENCODINGS_SRCS=""
AC_DEFUN([ENCODING_FLAG], [
	AC_ARG_ENABLE($1,
		AS_HELP_STRING([--disable-$1],
			[Disables support for $3]))
	AS_IF([test x"$enable_$2" != x"no"], [

		AC_DEFINE($4, 1, [Whether we have support for $3])
		ENCODINGS_SRCS="$ENCODINGS_SRCS $1.m"
	])
])
ENCODING_FLAG(codepage-437, codepage_437, [Codepage 437], HAVE_CODEPAGE_437)
ENCODING_FLAG(codepage-850, codepage_850, [Codepage 850], HAVE_CODEPAGE_850)
ENCODING_FLAG(codepage-858, codepage_858, [Codepage 858], HAVE_CODEPAGE_858)
ENCODING_FLAG(iso-8859-2, iso_8859_2, [ISO 8859-2], HAVE_ISO_8859_2)
ENCODING_FLAG(iso-8859-3, iso_8859_3, [ISO 8859-3], HAVE_ISO_8859_3)
ENCODING_FLAG(iso-8859-15, iso_8859_15, [ISO 8859-15], HAVE_ISO_8859_15)
ENCODING_FLAG(koi8-r, koi8_r, [KOI8-R], HAVE_KOI8_R)
ENCODING_FLAG(koi8-u, koi8_u, [KOI8-U], HAVE_KOI8_U)
ENCODING_FLAG(mac-roman, mac_roman, [Mac Roman encoding], HAVE_MAC_ROMAN)
ENCODING_FLAG(windows-1251, windows_1251, [Windows-1251], HAVE_WINDOWS_1251)
ENCODING_FLAG(windows-1252, windows_1252, [Windows-1252], HAVE_WINDOWS_1252)


AS_IF([test x"$ENCODINGS_SRCS" = x""], [
	ENCODINGS_SRCS="dummy.m"
])
AC_SUBST(ENCODINGS_SRCS)

AS_IF([test x"$enable_shared" != x"no"], [
	AC_SUBST(ENCODINGS_LIB_A, "encodings.lib.a")

])
AS_IF([test x"$enable_static" = x"yes" -o x"$enable_shared" = x"no"], [
	AC_SUBST(ENCODINGS_A, "encodings.a")


])

AC_CHECK_FUNCS(arc4random arc4random_buf getrandom random, break)

AS_IF([test x"$host_os" != x"morphos"], [
	AC_CHECK_LIB(dl, dlopen, LIBS="$LIBS -ldl")
])
AC_CHECK_HEADERS_ONCE(dlfcn.h)
case "$host_os" in
netbsd*)
	dnl dladdr exists on NetBSD, but it is completely broken.
	dnl When using it with code that uses __thread, it freezes the process
	dnl so that it has to be killed using SIGKILL.
	dnl When disabling __thread, it doesn't freeze, but all symbols are
	dnl wrong.
	;;
*)
	AC_CHECK_FUNCS(dladdr)
	;;
esac

AC_CHECK_HEADERS(sys/mman.h)
AC_CHECK_FUNCS(mmap mlock)

AC_ARG_ENABLE(threads,
	AS_HELP_STRING([--disable-threads], [disable thread support]))
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
		dnl Use -Wp, as we only use it for the preprocessor.
		AX_CHECK_COMPILER_FLAGS([-Wp,-pthread], [
			CPPFLAGS="$CPPFLAGS -Wp,-pthread"
		], [
			CPPFLAGS="$CPPFLAGS -D_REENTRANT -D_THREAD_SAFE"
		])

		AC_CHECK_LIB(pthread, pthread_create, LIBS="$LIBS -lpthread")

		AC_TRY_LINK([

			#include <pthread.h>
		], [
			pthread_create(NULL, NULL, NULL, NULL);

		], [], [
			AC_MSG_ERROR(No supported threads found!)
		])

		AC_DEFINE(OF_HAVE_PTHREADS, 1, [Whether we have pthreads])

		AC_TRY_COMPILE([

			#include <pthread.h>
		], [
			pthread_mutexattr_t attr;
			pthread_mutexattr_settype(&attr,
			    PTHREAD_MUTEX_RECURSIVE);

		], [
			AC_DEFINE(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES, 1,
				[If pthread mutexes can be recursive])
		])

		AC_CHECK_FUNC(pthread_spin_lock, [
			have_spinlocks="yes"







|

|
>
|
|
|
>






|
>
|
|
|
|
|
>







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
		dnl Use -Wp, as we only use it for the preprocessor.
		AX_CHECK_COMPILER_FLAGS([-Wp,-pthread], [
			CPPFLAGS="$CPPFLAGS -Wp,-pthread"
		], [
			CPPFLAGS="$CPPFLAGS -D_REENTRANT -D_THREAD_SAFE"
		])

		AC_CHECK_LIB(pthread, main, LIBS="$LIBS -lpthread")

		AC_LINK_IFELSE([
			AC_LANG_PROGRAM([
				#include <pthread.h>
			], [
				pthread_create(NULL, NULL, NULL, NULL);
			])
		], [], [
			AC_MSG_ERROR(No supported threads found!)
		])

		AC_DEFINE(OF_HAVE_PTHREADS, 1, [Whether we have pthreads])

		AC_COMPILE_IFELSE([
			AC_LANG_PROGRAM([
				#include <pthread.h>
			], [
				pthread_mutexattr_t attr;
				pthread_mutexattr_settype(&attr,
				    PTHREAD_MUTEX_RECURSIVE);
			])
		], [
			AC_DEFINE(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES, 1,
				[If pthread mutexes can be recursive])
		])

		AC_CHECK_FUNC(pthread_spin_lock, [
			have_spinlocks="yes"
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
		AC_CHECK_HEADERS(pthread_np.h, [], [], [#include <pthread.h>])
		AC_CHECK_FUNCS(pthread_set_name_np pthread_setname_np, break)
		;;
	esac

	AC_DEFINE(OF_HAVE_THREADS, 1, [Whether we have threads])
	AC_SUBST(USE_SRCS_THREADS, '${SRCS_THREADS}')


	AC_ARG_ENABLE(compiler-tls,
		AS_HELP_STRING([--disable-compiler-tls],
			[disable compiler thread local storage]))

	case "$host" in
		aarch64*-*-android*)
			# Compiler TLS is broken on AArch64 Android with Clang
			enable_compiler_tls="no"
			;;
		m68k-*-amigaos | powerpc-*-amigaos)
			# Compiler TLS is broken on AmigaOS
			enable_compiler_tls="no"
			;;





	esac

	AS_IF([test x"$enable_compiler_tls" != x"no"], [
		AC_CHECK_HEADER(threads.h, [
			AC_DEFINE(OF_HAVE_THREADS_H, 1,
				[Whether we have threads.h])
		])

		AC_MSG_CHECKING(whether _Thread_local works)
		AC_TRY_LINK([

			static _Thread_local int x = 0;
		], [
			x++;

		], [
			AS_IF([test x"$enable_shared" != x"no"], [
				old_OBJCFLAGS="$OBJCFLAGS"
				OBJCFLAGS="$OBJCFLAGS -fPIC"
				AC_TRY_COMPILE([

					static _Thread_local int x = 0;
				], [
					x++;

				], [
					AC_MSG_RESULT(yes)
					AC_DEFINE(OF_HAVE__THREAD_LOCAL, 1,
						[Whether _Thread_local works])
					have_thread_local="yes"
				], [
					AC_MSG_RESULT(no)







>






|
|
|
|
|
|
|
|
>
>
>
>
>









|
>
|
|
|
>




|
>
|
|
|
>







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
		AC_CHECK_HEADERS(pthread_np.h, [], [], [#include <pthread.h>])
		AC_CHECK_FUNCS(pthread_set_name_np pthread_setname_np, break)
		;;
	esac

	AC_DEFINE(OF_HAVE_THREADS, 1, [Whether we have threads])
	AC_SUBST(USE_SRCS_THREADS, '${SRCS_THREADS}')
	AC_SUBST(OBJC_SYNC, objc_sync)

	AC_ARG_ENABLE(compiler-tls,
		AS_HELP_STRING([--disable-compiler-tls],
			[disable compiler thread local storage]))

	case "$host" in
	aarch64*-*-android*)
		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

	AS_IF([test x"$enable_compiler_tls" != x"no"], [
		AC_CHECK_HEADER(threads.h, [
			AC_DEFINE(OF_HAVE_THREADS_H, 1,
				[Whether we have threads.h])
		])

		AC_MSG_CHECKING(whether _Thread_local works)
		AC_LINK_IFELSE([
			AC_LANG_PROGRAM([
				static _Thread_local int x = 0;
			], [
				x++;
			])
		], [
			AS_IF([test x"$enable_shared" != x"no"], [
				old_OBJCFLAGS="$OBJCFLAGS"
				OBJCFLAGS="$OBJCFLAGS -fPIC"
				AC_COMPILE_IFELSE([
					AC_LANG_PROGRAM([
						static _Thread_local int x = 0;
					], [
						x++;
					])
				], [
					AC_MSG_RESULT(yes)
					AC_DEFINE(OF_HAVE__THREAD_LOCAL, 1,
						[Whether _Thread_local works])
					have_thread_local="yes"
				], [
					AC_MSG_RESULT(no)
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
			])
		], [
			AC_MSG_RESULT(no)
		])

		AS_IF([test x"$have_thread_local" != x"yes"], [
			AC_MSG_CHECKING(whether __thread works)
			AC_TRY_LINK([


				/* It seems __thread is buggy with GCC 4.1 */

				#if __GNUC__ == 4 && __GNUC_MINOR__ < 2
				# error buggy
				#endif

				__thread int x = 0;
			], [
				x++;

			], [
				AS_IF([test x"$enable_shared" != x"no"], [
					old_OBJCFLAGS="$OBJCFLAGS"
					OBJCFLAGS="$OBJCFLAGS -fPIC"
					AC_TRY_COMPILE([

						__thread int x = 0;
					], [
						x++;

					], [
						AC_MSG_RESULT(yes)
						AC_DEFINE(OF_HAVE___THREAD, 1,
							[Whether __thread works]
						)
					], [
						AC_MSG_RESULT(no)







|
>
>
|
>
|
|
|

|
|
|
>




|
>
|
|
|
>







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
			])
		], [
			AC_MSG_RESULT(no)
		])

		AS_IF([test x"$have_thread_local" != x"yes"], [
			AC_MSG_CHECKING(whether __thread works)
			AC_LINK_IFELSE([
				AC_LANG_PROGRAM([
					/*
					 * It seems __thread is buggy with
					 * GCC 4.1 */
					#if __GNUC__ == 4 && __GNUC_MINOR__ < 2
					# error buggy
					#endif

					__thread int x = 0;
				], [
					x++;
				])
			], [
				AS_IF([test x"$enable_shared" != x"no"], [
					old_OBJCFLAGS="$OBJCFLAGS"
					OBJCFLAGS="$OBJCFLAGS -fPIC"
					AC_COMPILE_IFELSE([
						AC_LANG_PROGRAM([
							__thread int x = 0;
						], [
							x++;
						])
					], [
						AC_MSG_RESULT(yes)
						AC_DEFINE(OF_HAVE___THREAD, 1,
							[Whether __thread works]
						)
					], [
						AC_MSG_RESULT(no)
1160
1161
1162
1163
1164
1165
1166
1167

1168
1169
1170
1171
1172
1173
1174
1175
1176

1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187


1188

1189
1190
1191
1192

1193
1194
1195
1196
1197
1198
1199
		AC_MSG_RESULT(yes)
		atomic_ops="assembly implementation"
	], [
		AC_MSG_RESULT(no)
	])

	AC_MSG_CHECKING(whether __atomic_* works)
	AC_TRY_LINK([

		#include <stdbool.h>
		#include <stdint.h>
	], [
		int32_t i, j;
		if (__atomic_add_fetch(&i, 1, __ATOMIC_RELAXED))
			j = __atomic_sub_fetch(&i, 1, __ATOMIC_RELAXED);
		while (!__atomic_compare_exchange_n(&i, &j, 1, false,
		    __ATOMIC_RELAXED, __ATOMIC_RELAXED));
		__atomic_thread_fence(__ATOMIC_SEQ_CST);

	], [
		AC_MSG_RESULT(yes)
		test x"$atomic_ops" = x"none" && \
			atomic_ops="__atomic_* builtins"
		AC_DEFINE(OF_HAVE_ATOMIC_BUILTINS, 1,
			[Whether __atomic_* builtins are available])
	], [
		AC_MSG_RESULT(no)
	])

	AC_MSG_CHECKING(whether __sync_* works)


	AC_TRY_LINK([#include <stdint.h>], [

		int32_t i, j;
		if (__sync_add_and_fetch(&i, 1))
			j = __sync_sub_and_fetch(&i, 1);
		while (!__sync_bool_compare_and_swap(&i, 0, 1));

	], [
		AC_MSG_RESULT(yes)
		test x"$atomic_ops" = x"none" && \
			atomic_ops="__sync_* builtins"
		AC_DEFINE(OF_HAVE_SYNC_BUILTINS, 1,
			[Whether __sync_* builtins are available])
	], [







|
>
|
|
|
|
|
|
|
|
|
>











>
>
|
>
|
|
|
|
>







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
		AC_MSG_RESULT(yes)
		atomic_ops="assembly implementation"
	], [
		AC_MSG_RESULT(no)
	])

	AC_MSG_CHECKING(whether __atomic_* works)
	AC_LINK_IFELSE([
		AC_LANG_PROGRAM([
			#include <stdbool.h>
			#include <stdint.h>
		], [
			int32_t i, j;
			if (__atomic_add_fetch(&i, 1, __ATOMIC_RELAXED))
				j = __atomic_sub_fetch(&i, 1, __ATOMIC_RELAXED);
			while (!__atomic_compare_exchange_n(&i, &j, 1, false,
			    __ATOMIC_RELAXED, __ATOMIC_RELAXED));
			__atomic_thread_fence(__ATOMIC_SEQ_CST);
		])
	], [
		AC_MSG_RESULT(yes)
		test x"$atomic_ops" = x"none" && \
			atomic_ops="__atomic_* builtins"
		AC_DEFINE(OF_HAVE_ATOMIC_BUILTINS, 1,
			[Whether __atomic_* builtins are available])
	], [
		AC_MSG_RESULT(no)
	])

	AC_MSG_CHECKING(whether __sync_* works)
	AC_LINK_IFELSE([
		AC_LANG_PROGRAM([
			#include <stdint.h>
		], [
			int32_t i, j;
			if (__sync_add_and_fetch(&i, 1))
				j = __sync_sub_and_fetch(&i, 1);
			while (!__sync_bool_compare_and_swap(&i, 0, 1));
		])
	], [
		AC_MSG_RESULT(yes)
		test x"$atomic_ops" = x"none" && \
			atomic_ops="__sync_* builtins"
		AC_DEFINE(OF_HAVE_SYNC_BUILTINS, 1,
			[Whether __sync_* builtins are available])
	], [
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
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.
			;;
		*)
			AC_CHECK_TYPE(off64_t, [
				AC_DEFINE(OF_HAVE_OFF64_T, 1,
					[Whether we have off64_t])
				AC_CHECK_FUNCS([lseek64 lstat64 open64 stat64])
			])
			;;
	esac

	AC_CHECK_HEADERS([pwd.h grp.h])
	AC_CHECK_FUNC(chmod, [
		AC_DEFINE(OF_HAVE_CHMOD, 1, [Whether we have chmod()])
	])
	AC_CHECK_FUNC(chown, [







|
|
|
|
|
|
<
|
|
|







1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214

1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
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.
		;;
	*)
		AC_CHECK_TYPE(off64_t, [
			AC_DEFINE(OF_HAVE_OFF64_T, 1, [Whether we have off64_t])

			AC_CHECK_FUNCS([lseek64 lstat64 open64 stat64])
		])
		;;
	esac

	AC_CHECK_HEADERS([pwd.h grp.h])
	AC_CHECK_FUNC(chmod, [
		AC_DEFINE(OF_HAVE_CHMOD, 1, [Whether we have chmod()])
	])
	AC_CHECK_FUNC(chown, [
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
	AC_CHECK_MEMBERS([struct stat.st_birthtime], [], [], [
		#include <sys/stat.h>
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Werror"
	AC_MSG_CHECKING(for readdir_r)
	AC_TRY_COMPILE([

		#include <dirent.h>
	], [
		DIR *dir = 0;
		struct dirent entry, *result;

		readdir_r(dir, &entry, &result);

	], [
		AC_MSG_RESULT(yes)
		AC_DEFINE(HAVE_READDIR_R, 1, [Whether we have readdir_r()])
	], [
		AC_MSG_RESULT(no)
	])
	OBJCFLAGS="$old_OBJCFLAGS"
])

AC_CHECK_HEADERS(fcntl.h dirent.h)
AC_CHECK_FUNCS([sysconf gmtime_r localtime_r nanosleep fcntl])














AC_CHECK_HEADERS(xlocale.h)
AC_CHECK_FUNCS([strtod_l strtof_l asprintf_l])
AS_IF([test x"$gnu_source" != x"yes" -a \( \
    x"$ac_cv_func_strtod_l" = x"yes" -o x"$ac_cv_func_strtof_l" = x"yes" -o \
    x"$ac_cv_func_asprintf_l" = x"yes" \)], [
	AC_MSG_CHECKING(whether *_l functions need _GNU_SOURCE)
	AC_TRY_COMPILE([

		#include <stdlib.h>
		#include <stdio.h>

		#include <locale.h>
		#ifdef HAVE_XLOCALE_H
		# include <xlocale.h>
		#endif
	], [
		#ifdef HAVE_STRTOD_L
		(void)strtod_l;
		#endif
		#ifdef HAVE_STRTOF_L
		(void)strtof_l;
		#endif
		#ifdef HAVE_ASPRINTF_L
		(void)asprintf_l;
		#endif

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

dnl This check needs to happen after the above, as _GNU_SOURCE can change the
dnl return type.
AC_CHECK_FUNCS(strerror_r, [
	AC_MSG_CHECKING(for return type of strerror_r)
	AC_TRY_COMPILE([

		#include <stdio.h>
		#include <string.h>
	], [
		switch (strerror_r(0, NULL, 0)) {
		case 0:;
		}

	], [
		AC_MSG_RESULT(int)
	], [
		AC_MSG_RESULT(char *)
		AC_DEFINE(STRERROR_R_RETURNS_CHARP, 1,
			[Whether strerror_r returns char *])
	])
])

AC_CHECK_HEADERS(sys/utsname.h)
AC_CHECK_FUNCS(uname)

case "$host_os" in
	amigaos*)
		;;
	*)
		AC_CHECK_FUNC(pipe, [
			AC_DEFINE(OF_HAVE_PIPE, 1, [Whether we have pipe()])
		])
		;;
esac

AC_ARG_ENABLE(sockets,
	AS_HELP_STRING([--disable-sockets], [disable socket support]))
AS_IF([test x"$enable_sockets" != x"no"], [
	AC_DEFINE(OF_HAVE_SOCKETS, 1, [Whether we have sockets])
	AC_SUBST(USE_SRCS_SOCKETS, '${SRCS_SOCKETS}')

	case "$host_os" in
		amigaos*)
			;;
		haiku*)
			LIBS="$LIBS -lnetwork"
			;;
		mingw*)
			LIBS="$LIBS -lws2_32 -liphlpapi"
			;;
		*)
			AC_CHECK_LIB(socket, socket, LIBS="$LIBS -lsocket")
			;;
	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_HEADER(netinet/sctp.h, [
		AC_DEFINE(OF_HAVE_SCTP, 1, [Whether we have SCTP])
		AC_DEFINE(OF_HAVE_NETINET_SCTP_H, 1,
			[Whether we have netinet/sctp.h])
		AC_SUBST(USE_SRCS_SCTP, '${SRCS_SCTP}')
	])
	AC_CHECK_HEADERS([arpa/inet.h netdb.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








|
>
|
|
|
|

|
>









|
|
>
>
>
>
>
>
>
>
>
>
>
>
>







|
>
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
>












|
>
|
|
|
|
|
|
>












<
<
<
<
|
|
|
<
<








|
|
|
|
|
|
|
|
|
|
|














<
<
<
<
<
<





>
>
>







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
	AC_CHECK_MEMBERS([struct stat.st_birthtime], [], [], [
		#include <sys/stat.h>
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Werror"
	AC_MSG_CHECKING(for readdir_r)
	AC_COMPILE_IFELSE([
		AC_LANG_PROGRAM([
			#include <dirent.h>
		], [
			DIR *dir = 0;
			struct dirent entry, *result;

			readdir_r(dir, &entry, &result);
		])
	], [
		AC_MSG_RESULT(yes)
		AC_DEFINE(HAVE_READDIR_R, 1, [Whether we have readdir_r()])
	], [
		AC_MSG_RESULT(no)
	])
	OBJCFLAGS="$old_OBJCFLAGS"
])

AC_CHECK_HEADERS(dirent.h)
AC_CHECK_FUNCS([sysconf gmtime_r localtime_r])

case "$host_os" in
amigaos* | morphos*)
	dnl We don't want fcntl() or nanosleep() on AmigaOS / MorphOS, despite
	dnl a symbol existing. The reason is that we cannot use fcntl() for
	dnl sockets and that nanosleep() is yet another function that uses
	dnl errno, so would need to be passed from the linklib.
	;;
*)
	AC_CHECK_HEADERS(fcntl.h)
	AC_CHECK_FUNCS([fcntl nanosleep])
	;;
esac

AC_CHECK_HEADERS(xlocale.h)
AC_CHECK_FUNCS([strtod_l strtof_l asprintf_l])
AS_IF([test x"$gnu_source" != x"yes" -a \( \
    x"$ac_cv_func_strtod_l" = x"yes" -o x"$ac_cv_func_strtof_l" = x"yes" -o \
    x"$ac_cv_func_asprintf_l" = x"yes" \)], [
	AC_MSG_CHECKING(whether *_l functions need _GNU_SOURCE)
	AC_COMPILE_IFELSE([
		AC_LANG_PROGRAM([
			#include <stdlib.h>
			#include <stdio.h>

			#include <locale.h>
			#ifdef HAVE_XLOCALE_H
			# include <xlocale.h>
			#endif
		], [
			#ifdef HAVE_STRTOD_L
			(void)strtod_l;
			#endif
			#ifdef HAVE_STRTOF_L
			(void)strtof_l;
			#endif
			#ifdef HAVE_ASPRINTF_L
			(void)asprintf_l;
			#endif
		])
	], [
		AC_MSG_RESULT(no)
	], [
		AC_MSG_RESULT(yes)
		CPPFLAGS="-D_GNU_SOURCE $CPPFLAGS"
	])
])

dnl This check needs to happen after the above, as _GNU_SOURCE can change the
dnl return type.
AC_CHECK_FUNCS(strerror_r, [
	AC_MSG_CHECKING(for return type of strerror_r)
	AC_COMPILE_IFELSE([
		AC_LANG_PROGRAM([
			#include <stdio.h>
			#include <string.h>
		], [
			switch (strerror_r(0, NULL, 0)) {
			case 0:;
			}
		])
	], [
		AC_MSG_RESULT(int)
	], [
		AC_MSG_RESULT(char *)
		AC_DEFINE(STRERROR_R_RETURNS_CHARP, 1,
			[Whether strerror_r returns char *])
	])
])

AC_CHECK_HEADERS(sys/utsname.h)
AC_CHECK_FUNCS(uname)





AC_CHECK_FUNC(pipe, [
	AC_DEFINE(OF_HAVE_PIPE, 1, [Whether we have pipe()])
])



AC_ARG_ENABLE(sockets,
	AS_HELP_STRING([--disable-sockets], [disable socket support]))
AS_IF([test x"$enable_sockets" != x"no"], [
	AC_DEFINE(OF_HAVE_SOCKETS, 1, [Whether we have sockets])
	AC_SUBST(USE_SRCS_SOCKETS, '${SRCS_SOCKETS}')

	case "$host_os" in
	amigaos* | morphos*)
		;;
	haiku*)
		LIBS="$LIBS -lnetwork"
		;;
	mingw*)
		LIBS="$LIBS -lws2_32 -liphlpapi"
		;;
	*)
		AC_CHECK_LIB(socket, socket, LIBS="$LIBS -lsocket")
		;;
	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(netipx/ipx.h, [
		AC_DEFINE(OF_HAVE_NETIPX_IPX_H, 1,
			[Whether we have netipx/ipx.h])
	])
	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

1411
1412
1413
1414
1415
1416
1417



1418
1419
1420
1421
1422
1423
1424
			#ifdef AF_INET6
			egrep_cpp_yes
			#endif
		], [
			AC_DEFINE(OF_HAVE_IPV6, 1, [Whether we have IPv6])
		])
	], [



	], [
		#ifdef _WIN32
		typedef int BOOL;
		#endif

		#ifdef OF_HAVE_NETINET_IN_H
		# include <netinet/in.h>







>
>
>







1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
			#ifdef AF_INET6
			egrep_cpp_yes
			#endif
		], [
			AC_DEFINE(OF_HAVE_IPV6, 1, [Whether we have IPv6])
		])
	], [
		dnl Work around a bug in autoconf 2.61 that creates a broken
		dnl configure if this branch is empty.
		:
	], [
		#ifdef _WIN32
		typedef int BOOL;
		#endif

		#ifdef OF_HAVE_NETINET_IN_H
		# include <netinet/in.h>
1503
1504
1505
1506
1507
1508
1509


































1510
1511
1512
1513
1514
1515
1516
			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")







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







1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
			egrep_cpp_yes
			#endif
		], [
			AC_DEFINE(OF_HAVE_IPX, 1, [Whether we have IPX/SPX])
			AC_SUBST(USE_SRCS_IPX, '${SRCS_IPX}')
		])
	])

	AC_CHECK_HEADERS(afunix.h, [
		AC_DEFINE(OF_HAVE_AFUNIX_H, 1, [Whether we have afunix.h])
	], [], [
		#ifdef _WIN32
		# include <winsock2.h>
		#endif
	])
	AC_CHECK_MEMBER(struct sockaddr_un.sun_path, [
		AC_DEFINE(OF_HAVE_UNIX_SOCKETS, 1,
			[Whether we have UNIX sockets])
		AC_SUBST(USE_SRCS_UNIX_SOCKETS, '${SRCS_UNIX_SOCKETS}')
	], [], [
		#ifdef OF_HAVE_SYS_TYPES_H
		# include <sys/types.h>
		#endif
		#ifdef OF_HAVE_SYS_UN_H
		# include <sys/un.h>
		#endif
		#ifdef _WIN32
		# include <winsock2.h>
		#endif
		#ifdef HAVE_AFUNIX_H
		# include <afunix.h>
		#endif

		#ifdef __morphos__
		# error MorphOS has the struct but does not support it
		#endif

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

	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")
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553












































1554
1555
1556





























































1557
1558
1559
1560
1561
1562
1563

1564
1565
1566
1567
1568
1569
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
			AC_DEFINE(HAVE_POLL, 1, [Whether we have poll()])
			AC_SUBST(OF_POLL_KERNEL_EVENT_OBSERVER_M,
				"OFPollKernelEventObserver.m")
		])
	])

	case "$host_os" in
		amigaos* | mingw* | morphos*)
			AC_DEFINE(HAVE_SELECT, 1,
				[Whether we have select() or similar])
			AC_SUBST(OF_SELECT_KERNEL_EVENT_OBSERVER_M,
				"OFSelectKernelEventObserver.m")
			;;
		*)
			AC_CHECK_HEADERS(sys/select.h)
			AC_CHECK_FUNC(select, [
				AC_DEFINE(HAVE_SELECT, 1,
					[Whether we have select() or similar])
				AC_SUBST(OF_SELECT_KERNEL_EVENT_OBSERVER_M,
					"OFSelectKernelEventObserver.m")
			])












































			;;
	esac






























































	AS_IF([test x"$enable_threads" != x"no"], [
		AC_SUBST(OF_HTTP_CLIENT_TESTS_M, "OFHTTPClientTests.m")
	])

	AC_SUBST(OFDNS, "ofdns")
	AS_IF([test x"$enable_files" != x"no"], [
		AC_SUBST(OFHTTP, "ofhttp")

	])
	AC_SUBST(OFSOCK, "ofsock")
])

AC_DEFUN([CHECK_BUILTIN_BSWAP], [
	AC_MSG_CHECKING(for __builtin_bswap$1)
	AC_TRY_LINK([

		#include <stdint.h>
		#include <stdio.h>
		#include <errno.h>
	], [
		uint$1_t i = errno;
		printf("%d", (int)__builtin_bswap$1(i));

	], [
		AC_MSG_RESULT(yes)
		AC_DEFINE(OF_HAVE_BUILTIN_BSWAP$1, 1,
			[Whether we have __builtin_bswap$1])
	], [
		AC_MSG_RESULT(no)
	])
])
CHECK_BUILTIN_BSWAP(16)
CHECK_BUILTIN_BSWAP(32)
CHECK_BUILTIN_BSWAP(64)

case "$host" in
	arm*-apple-darwin*)
















		have_processes="no"

		;;
	*-*-mingw*)
		have_processes="yes"
		;;
	*-*-msdosdjgpp*)
		have_processes="no"
		;;
	*)
		AC_HEADER_SYS_WAIT
		AC_CHECK_FUNCS(kill)

		AC_CHECK_FUNCS(posix_spawnp, [
			AS_IF([test x"$ac_cv_func_kill" = x"yes"], [
				have_processes="yes"

				AC_CHECK_HEADERS(spawn.h)
			])
		], [
			AC_CHECK_FUNCS([vfork dup2 execvp _exit], [
				AS_IF([test x"$ac_cv_func_vfork" = x"yes" \
				    -a x"$ac_cv_func_pipe" = x"yes" \
				    -a x"$ac_cv_func_dup2" = x"yes" \
				    -a x"$ac_cv_func_execvp" = x"yes" \
				    -a x"$ac_cv_func_kill" = x"yes" \
				    -a x"$ac_cv_func__exit" = x"yes"], [
					have_processes="yes"
				])
			], [
				break
			])
		])
		;;
esac
AS_IF([test x"$have_processes" = x"yes"], [
	AC_SUBST(OF_PROCESS_M, "OFProcess.m")
	AC_DEFINE(OF_HAVE_PROCESSES, 1, [Whether we have processes])
])

AC_CHECK_HEADERS_ONCE([complex.h sys/ioctl.h sys/ttycom.h])
AC_CHECK_FUNCS(isatty)

AC_CHECK_FUNC(pledge, [
	AC_DEFINE(OF_HAVE_PLEDGE, 1, [Whether we have pledge()])







|
|
<
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|

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







>

<




|
>
|
|
|
|
|
|
>












|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
|
|
|
|
|
|
|
|
|

|
|
|

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

|
|
|







1559
1560
1561
1562
1563
1564
1565
1566
1567

1568
1569
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
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695

1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
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
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
			AC_DEFINE(HAVE_POLL, 1, [Whether we have poll()])
			AC_SUBST(OF_POLL_KERNEL_EVENT_OBSERVER_M,
				"OFPollKernelEventObserver.m")
		])
	])

	case "$host_os" in
	amigaos* | mingw* | morphos*)
		AC_DEFINE(HAVE_SELECT, 1, [Whether we have select() or similar])

		AC_SUBST(OF_SELECT_KERNEL_EVENT_OBSERVER_M,
			"OFSelectKernelEventObserver.m")
		;;
	*)
		AC_CHECK_HEADERS(sys/select.h)
		AC_CHECK_FUNC(select, [
			AC_DEFINE(HAVE_SELECT, 1,
				[Whether we have select() or similar])
			AC_SUBST(OF_SELECT_KERNEL_EVENT_OBSERVER_M,
				"OFSelectKernelEventObserver.m")
		])
		;;
	esac

	AC_ARG_WITH(tls,
		AS_HELP_STRING([--with-tls], [
			enable TLS support using the specified library
			(yes, openssl, gnutls, securetransport or no)]))
	AS_IF([test x"$with_tls" = x""], [with_tls="yes"])
	tls_support="no"

	AS_IF([test x"$with_tls" = x"securetransport" \
		-o x"$with_tls" = x"yes"], [
		AC_CHECK_HEADERS(Security/SecureTransport.h, [
			old_LIBS="$LIBS"
			LIBS="-framework Security -framework Foundation $LIBS"

			AC_CHECK_FUNC(SSLHandshake, [
				AC_DEFINE(HAVE_SECURE_TRANSPORT, 1,
					[Whether we have Secure Transport])

				tls_support="Secure Transport"
				TLS_LIBS="-framework Foundation $TLS_LIBS"
				TLS_LIBS="-framework Security $TLS_LIBS"

				AC_SUBST(OF_SECURE_TRANSPORT_TLS_STREAM_M,
					"OFSecureTransportTLSStream.m")

				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"
			;;
		*)
			ssl="ssl"
			crypto="crypto"
			;;
		esac

		AC_CHECK_LIB($ssl, SSL_set1_host, [
			AC_CHECK_HEADER(openssl/ssl.h, [
				AC_DEFINE(HAVE_OPENSSL, 1,
					[Whether we have OpenSSL])

				tls_support="OpenSSL"
				TLS_LIBS="-l$ssl -l$crypto $TLS_LIBS"

				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)

		OFHTTP_LIBS="-lobjfwtls $TLS_LIBS $OFHTTP_LIBS"

		AS_IF([test x"$enable_shared" != x"no"], [
			AC_SUBST(OBJFWTLS_SHARED_LIB,
				"${LIB_PREFIX}objfwtls${LIB_SUFFIX}")
		])
		AS_IF([test x"$enable_static" = x"yes" \
		    -o x"$enable_shared" = x"no"], [
			AC_SUBST(OBJFWTLS_STATIC_LIB, "libobjfwtls.a")
		])
		AS_IF([test x"$build_framework" = x"yes"], [
			AC_SUBST(OBJFWTLS_FRAMEWORK, "ObjFWTLS.framework")
		])
	])

	AS_IF([test x"$with_tls" != x"no" -a x"$tls_support" = x"no"], [
		AC_MSG_ERROR(m4_normalize([
			No TLS implementation was found. Please install OpenSSL,
			GnuTLS or use --without-tls.
		]))
	])

	AS_IF([test x"$enable_threads" != x"no"], [
		AC_SUBST(OF_HTTP_CLIENT_TESTS_M, "OFHTTPClientTests.m")
	])

	AC_SUBST(OFDNS, "ofdns")
	AS_IF([test x"$enable_files" != x"no"], [
		AC_SUBST(OFHTTP, "ofhttp")
		AC_SUBST(OFHTTP_LIBS)
	])

])

AC_DEFUN([CHECK_BUILTIN_BSWAP], [
	AC_MSG_CHECKING(for __builtin_bswap$1)
	AC_LINK_IFELSE([
		AC_LANG_PROGRAM([
			#include <stdint.h>
			#include <stdio.h>
			#include <errno.h>
		], [
			uint$1_t i = errno;
			printf("%d", (int)__builtin_bswap$1(i));
		])
	], [
		AC_MSG_RESULT(yes)
		AC_DEFINE(OF_HAVE_BUILTIN_BSWAP$1, 1,
			[Whether we have __builtin_bswap$1])
	], [
		AC_MSG_RESULT(no)
	])
])
CHECK_BUILTIN_BSWAP(16)
CHECK_BUILTIN_BSWAP(32)
CHECK_BUILTIN_BSWAP(64)

case "$host_os" in
darwin*)
	AC_MSG_CHECKING(whether we are compiling for macOS)
	AC_EGREP_CPP(egrep_cpp_yes, [
		#include <TargetConditionals.h>

		#if (!defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE) && \
		    (!defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR)
		egrep_cpp_yes
		#endif
	], [
		AC_MSG_RESULT(yes)
		have_subprocesses="yes"

		AC_CHECK_FUNCS(posix_spawnp)
		AC_CHECK_HEADERS(spawn.h)
	], [
		AC_MSG_RESULT(no)
		have_subprocesses="no"
	])
	;;
mingw*)
	have_subprocesses="yes"
	;;
msdosdjgpp*)
	have_subprocesses="no"
	;;
*)
	AC_HEADER_SYS_WAIT
	AC_CHECK_FUNCS(kill)

	AC_CHECK_FUNCS(posix_spawnp, [
		AS_IF([test x"$ac_cv_func_kill" = x"yes"], [
			have_subprocesses="yes"

			AC_CHECK_HEADERS(spawn.h)
		])
	], [
		AC_CHECK_FUNCS([vfork dup2 execvp _exit], [
			AS_IF([test x"$ac_cv_func_vfork" = x"yes" \
			    -a x"$ac_cv_func_pipe" = x"yes" \
			    -a x"$ac_cv_func_dup2" = x"yes" \
			    -a x"$ac_cv_func_execvp" = x"yes" \
			    -a x"$ac_cv_func_kill" = x"yes" \
			    -a x"$ac_cv_func__exit" = x"yes"], [
				have_subprocesses="yes"
			])
		], [
			break
		])
	])
	;;
esac
AS_IF([test x"$have_subprocesses" = x"yes"], [
	AC_SUBST(OF_SUBPROCESS_M, "OFSubprocess.m")
	AC_DEFINE(OF_HAVE_SUBPROCESSES, 1, [Whether we have subprocesses])
])

AC_CHECK_HEADERS_ONCE([complex.h sys/ioctl.h sys/ttycom.h])
AC_CHECK_FUNCS(isatty)

AC_CHECK_FUNC(pledge, [
	AC_DEFINE(OF_HAVE_PLEDGE, 1, [Whether we have pledge()])
1656
1657
1658
1659
1660
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
1692
1693
1694
1695
1696

1697
1698
1699
1700
1701
1702
1703
1704
1705
1706

1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
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
1768
1769
1770
1771
1772

1773
1774

1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815

1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832

1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843

1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854





1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866

1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895

1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906

1907
1908
1909
1910

1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953




1954
1955
1956
1957
1958
1959
1960

dnl This needs to be after all other header checks, as they include unistd.h,
dnl which in old glibc versions uses __block. This is worked around in the code
dnl by providing a wrapper for unistd.h which takes care of this.
AC_MSG_CHECKING(whether Objective C compiler supports blocks)
old_OBJCFLAGS="$OBJCFLAGS"
OBJCFLAGS="$OBJCFLAGS -Xclang -fblocks"
AC_TRY_COMPILE([], [

	int (^foo)(int bar);
	foo = ^ (int bar) { return 0; }

], [
	OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -Xclang -fblocks"
	AC_SUBST(OF_BLOCK_TESTS_M, "OFBlockTests.m")
	AC_MSG_RESULT(yes)
], [
	AC_MSG_RESULT(no)
	OBJCFLAGS="$old_OBJCFLAGS"
])

AS_IF([test x"$GOBJC" = x"yes"], [
	OBJCFLAGS="$OBJCFLAGS -Wwrite-strings -Wpointer-arith -Werror"

	AC_MSG_CHECKING(whether we need -Wno-strict-aliasing due to GCC bugs)
	AC_TRY_COMPILE([

		#ifdef __has_attribute
		# if __has_attribute(objc_root_class)
		__attribute__((__objc_root_class__))
		# endif
		#endif
		@interface Foo
		{
			struct objc_class *_isa;
		}
		@end

		static struct {
			struct objc_class *_isa;
		} object;
	], [
		Foo *test = (Foo *)&object;
		(void)test; /* Get rid of unused variable warning */

	], [
		AC_MSG_RESULT(no)
	], [
		AC_MSG_RESULT(yes)
		OBJCFLAGS="$OBJCFLAGS -Wno-strict-aliasing"
	])

	AC_MSG_CHECKING(
	    whether we need -Wno-unused-property-ivar due to Clang bugs)
	AC_TRY_COMPILE([

		#ifdef __has_attribute
		# if __has_attribute(objc_root_class)
		__attribute__((__objc_root_class__))
		# endif
		#endif
		@interface Foo
		{
			struct objc_class *_isa;
			Foo *_foo;
		}

		@property (readonly, nonatomic) Foo *foo;

		+ (Foo *)foo;
		@end

		@implementation Foo
		@synthesize foo = _foo;

		+ (Foo *)foo
		{
			return (Foo *)0;
		}
		@end
	], [
	], [
		AC_MSG_RESULT(no)
	], [
		AC_MSG_RESULT(yes)
		OBJCFLAGS="$OBJCFLAGS -Wno-unused-property-ivar"
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Wcast-align"
	AC_MSG_CHECKING(whether -Wcast-align is buggy)
	AC_TRY_COMPILE([

		#ifdef __has_attribute
		# if __has_attribute(objc_root_class)
		__attribute__((__objc_root_class__))
		# endif
		#endif
		@interface Foo
		{
			struct objc_class *_isa;
		}
		@end

		@implementation Foo
		- (void)foo
		{
			struct objc_class *c = _isa;
			(void)c;
		}
		@end
	], [
	], [
		AC_MSG_RESULT(no)
	], [
		AC_MSG_RESULT(yes)
		OBJCFLAGS="$old_OBJCFLAGS"
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Wunreachable-code"
	AC_MSG_CHECKING(whether -Wunreachable-code can be used)
	AC_TRY_COMPILE([

		#include <stdlib.h>


		typedef void *SEL;

		#ifdef __has_attribute
		# if __has_attribute(objc_root_class)
		__attribute__((__objc_root_class__))
		# endif
		#endif
		@interface Object
		- (void)doesNotRecognizeSelector: (SEL)selector
		#ifdef __clang__
		    __attribute__((__noreturn__))
		#endif
		    ;
		- (void)dealloc;
		@end

		@interface Foo: Object
		@end

		void
		test(void)
		{
			if (sizeof(int) == 4)
				__asm__ ("");
			else if (sizeof(int) == 8)
				__asm__ ("");
			else
				abort();
		}

		/*
		 * Unfortunately, this cannot be shorter, as it only works when
		 * it is used inside a macro.
		 */
		#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];		\
									\
			abort();					\
									\
			[super dealloc];
		#endif

		@implementation Foo
		- (void)dealloc
		{
			OF_DEALLOC_UNSUPPORTED
		}
		@end

	], [], [
		AC_MSG_RESULT(yes)
	], [
		AC_MSG_RESULT(no)
		OBJCFLAGS="$old_OBJCFLAGS"
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Wdocumentation"
	AC_MSG_CHECKING(whether -Wdocumentation works correctly)
	AC_TRY_COMPILE([

		/**
		 * @class Test conftest.m conftest.m
		 */
		#ifdef __has_attribute
		# if __has_attribute(objc_root_class)
		__attribute__((__objc_root_class__))
		# endif
		#endif
		@interface Test
		@end
	], [





	], [
		AC_MSG_RESULT(yes)
	], [
		AC_MSG_RESULT(no)
		OBJCFLAGS="$old_OBJCFLAGS"
	])

	AS_IF([test x"$check_pedantic" = x"yes"], [
		old_OBJCFLAGS="$OBJCFLAGS"
		OBJCFLAGS="$OBJCFLAGS -pedantic"
		AC_MSG_CHECKING(whether -pedantic is buggy)
		AC_TRY_COMPILE([

			#include <stdlib.h>

			#include <assert.h>

			#ifdef __has_attribute
			# if __has_attribute(objc_root_class)
			__attribute__((__objc_root_class__))
			# endif
			#endif
			@interface Foo
			{
				void *foo;
			}
			@end

			@interface Bar: Foo
			- (void)assert;
			@end

			@implementation Bar
			- (void)assert
			{
				/*
				 * Some versions of glibc break with -pedantic
				 * when using assert.
				 */
				assert(1);
			}
			@end

		], [], [
			AC_MSG_RESULT(no)
		], [
			AC_MSG_RESULT(yes)
			OBJCFLAGS="$old_OBJCFLAGS"
		])
	])

	AS_IF([test x"$ac_cv_header_complex_h" = x"yes"], [
		AC_MSG_CHECKING(whether we need -Wno-gnu-imaginary-constant)
		AC_TRY_COMPILE([

			#include <complex.h>
		], [
			complex float f = 0.5 + 0.5 * I;
			(void)f;

		], [
			AC_MSG_RESULT(no)
		], [
			AC_MSG_RESULT(yes)
			OBJCFLAGS="$OBJCFLAGS -Wno-gnu-imaginary-constant"
		])
	])
])

AS_IF([test x"$cross_compiling" = x"yes"], [
	AC_SUBST(BIN_PREFIX, "${host_alias}-")

	case "$host" in
		i?86-*-mingw*)
			AC_CHECK_PROG(WINE, wine, wine)
			;;
		x86_64-*-mingw*)
			AC_CHECK_PROG(WINE, wine64, wine64)
			;;
	esac

	AS_IF([test x"$WINE" != x""], [
		AC_SUBST(RUN_TESTS, "run")
		AC_SUBST(WRAPPER, "$WINE")
	])

	AS_IF([test x"$with_wii" = x"yes"], [
		dnl Keep this lowercase, as WIILOAD is a variable used by
		dnl wiiload and thus likely already set by the user to something
		dnl that is not the path of the wiiload binary.
		AC_CHECK_PROG(wiiload, wiiload, wiiload)

		AS_IF([test x"$wiiload" != x""], [
			AC_SUBST(WRAPPER, "$wiiload")
		])
	])
], [
	AC_SUBST(RUN_TESTS, "run")
])

AC_ARG_WITH(fish_completions,
	AS_HELP_STRING([--with-fish-completions],
		[install completions for the fish shell]))




AS_IF([test x"$with_fish_completions" = x"yes"], [
	AC_SUBST(FISH_COMPLETIONS, fish)
])

dnl We don't call AC_PROG_CPP, but only AC_PROG_OBJCPP and set CPP to OBJCPP
dnl and add OBJCPPFLAGS to CPPFLAGS, thus we need to AC_SUBST these ourself.
AC_SUBST(CPP)







|
>
|
|
>













|
>
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
>









|
>
|
|
|
|
|
|
|
|
|
|

|

|
|

|
|

|
|
|
|
|
|










|
>
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|










|
>
|

>
|

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

|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|

|

|
>
|
|
|
|
|

|

|
|

|
|
|
|
|
|
>
|









|
>
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>











|
>
|

|

|
|
|
|
|
|
|
|
|
|

|
|
|

|
|
|
|
|
|
|
|
|
|
>
|









|
>
|
|
|
|
>













|
|
|
|
|
|



<













<
<





>
>
>
>







1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101

2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114


2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130

dnl This needs to be after all other header checks, as they include unistd.h,
dnl which in old glibc versions uses __block. This is worked around in the code
dnl by providing a wrapper for unistd.h which takes care of this.
AC_MSG_CHECKING(whether Objective C compiler supports blocks)
old_OBJCFLAGS="$OBJCFLAGS"
OBJCFLAGS="$OBJCFLAGS -Xclang -fblocks"
AC_COMPILE_IFELSE([
	AC_LANG_PROGRAM([], [
		int (^foo)(int bar);
		foo = ^ (int bar) { return 0; }
	])
], [
	OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS -Xclang -fblocks"
	AC_SUBST(OF_BLOCK_TESTS_M, "OFBlockTests.m")
	AC_MSG_RESULT(yes)
], [
	AC_MSG_RESULT(no)
	OBJCFLAGS="$old_OBJCFLAGS"
])

AS_IF([test x"$GOBJC" = x"yes"], [
	OBJCFLAGS="$OBJCFLAGS -Wwrite-strings -Wpointer-arith -Werror"

	AC_MSG_CHECKING(whether we need -Wno-strict-aliasing due to GCC bugs)
	AC_COMPILE_IFELSE([
		AC_LANG_PROGRAM([
			#ifdef __has_attribute
			# if __has_attribute(objc_root_class)
			__attribute__((__objc_root_class__))
			# endif
			#endif
			@interface Foo
			{
				struct objc_class *_isa;
			}
			@end

			static struct {
				struct objc_class *_isa;
			} object;
		], [
			Foo *test = (Foo *)&object;
			(void)test; /* Get rid of unused variable warning */
		])
	], [
		AC_MSG_RESULT(no)
	], [
		AC_MSG_RESULT(yes)
		OBJCFLAGS="$OBJCFLAGS -Wno-strict-aliasing"
	])

	AC_MSG_CHECKING(
	    whether we need -Wno-unused-property-ivar due to Clang bugs)
	AC_COMPILE_IFELSE([
		AC_LANG_SOURCE([
			#ifdef __has_attribute
			# if __has_attribute(objc_root_class)
			__attribute__((__objc_root_class__))
			# endif
			#endif
			@interface Foo
			{
				struct objc_class *_isa;
				Foo *_foo;
			}

			@property (readonly, nonatomic) Foo *foo;

			+ (Foo *)foo;
			@end

			@implementation Foo
			@synthesize foo = _foo;

			+ (Foo *)foo
			{
				return (Foo *)0;
			}
			@end
		])
	], [
		AC_MSG_RESULT(no)
	], [
		AC_MSG_RESULT(yes)
		OBJCFLAGS="$OBJCFLAGS -Wno-unused-property-ivar"
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Wcast-align"
	AC_MSG_CHECKING(whether -Wcast-align is buggy)
	AC_COMPILE_IFELSE([
		AC_LANG_SOURCE([
			#ifdef __has_attribute
			# if __has_attribute(objc_root_class)
			__attribute__((__objc_root_class__))
			# endif
			#endif
			@interface Foo
			{
				struct objc_class *_isa;
			}
			@end

			@implementation Foo
			- (void)foo
			{
				struct objc_class *c = _isa;
				(void)c;
			}
			@end
		])
	], [
		AC_MSG_RESULT(no)
	], [
		AC_MSG_RESULT(yes)
		OBJCFLAGS="$old_OBJCFLAGS"
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Wunreachable-code"
	AC_MSG_CHECKING(whether -Wunreachable-code can be used)
	AC_COMPILE_IFELSE([
		AC_LANG_SOURCE([[
			#include <stdlib.h>

			struct objc_selector;
			typedef const struct objc_selector *SEL;

			#ifdef __has_attribute
			# if __has_attribute(objc_root_class)
			__attribute__((__objc_root_class__))
			# endif
			#endif
			@interface Object
			- (void)doesNotRecognizeSelector: (SEL)selector
			#ifdef __clang__
			    __attribute__((__noreturn__))
			#endif
			    ;
			- (void)dealloc;
			@end

			@interface Foo: Object
			@end

			void
			test(void)
			{
				if (sizeof(int) == 4)
					__asm__ ("");
				else if (sizeof(int) == 8)
					__asm__ ("");
				else
					abort();
			}

			/*
			 * Unfortunately, this cannot be shorter, as it only
			 * works when it is used inside a macro.
			 */
			#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];	\
									\
				abort();				\
									\
				[super dealloc];
			#endif

			@implementation Foo
			- (void)dealloc
			{
				OF_DEALLOC_UNSUPPORTED
			}
			@end
		]])
	], [
		AC_MSG_RESULT(yes)
	], [
		AC_MSG_RESULT(no)
		OBJCFLAGS="$old_OBJCFLAGS"
	])

	old_OBJCFLAGS="$OBJCFLAGS"
	OBJCFLAGS="$OBJCFLAGS -Wdocumentation"
	AC_MSG_CHECKING(whether -Wdocumentation works correctly)
	AC_COMPILE_IFELSE([
		AC_LANG_SOURCE([
			/**
			 * @class Test conftest.m conftest.m
			 */
			#ifdef __has_attribute
			# if __has_attribute(objc_root_class)
			__attribute__((__objc_root_class__))
			# endif
			#endif
			@interface Test
			@end

			/**
			 * @struct Foo conftest.m conftest.m
			 */
			typedef struct {} Foo;
		])
	], [
		AC_MSG_RESULT(yes)
	], [
		AC_MSG_RESULT(no)
		OBJCFLAGS="$old_OBJCFLAGS"
	])

	AS_IF([test x"$check_pedantic" = x"yes"], [
		old_OBJCFLAGS="$OBJCFLAGS"
		OBJCFLAGS="$OBJCFLAGS -pedantic"
		AC_MSG_CHECKING(whether -pedantic is buggy)
		AC_COMPILE_IFELSE([
			AC_LANG_SOURCE([
				#include <stdlib.h>

				#include <assert.h>

				#ifdef __has_attribute
				# if __has_attribute(objc_root_class)
				__attribute__((__objc_root_class__))
				# endif
				#endif
				@interface Foo
				{
					void *foo;
				}
				@end

				@interface Bar: Foo
				- (void)assert;
				@end

				@implementation Bar
				- (void)assert
				{
					/*
					 * Some versions of glibc break with
					 * -pedantic when using assert.
					 */
					assert(1);
				}
				@end
			])
		], [
			AC_MSG_RESULT(no)
		], [
			AC_MSG_RESULT(yes)
			OBJCFLAGS="$old_OBJCFLAGS"
		])
	])

	AS_IF([test x"$ac_cv_header_complex_h" = x"yes"], [
		AC_MSG_CHECKING(whether we need -Wno-gnu-imaginary-constant)
		AC_COMPILE_IFELSE([
			AC_LANG_PROGRAM([
				#include <complex.h>
			], [
				complex float f = 0.5 + 0.5 * I;
				(void)f;
			])
		], [
			AC_MSG_RESULT(no)
		], [
			AC_MSG_RESULT(yes)
			OBJCFLAGS="$OBJCFLAGS -Wno-gnu-imaginary-constant"
		])
	])
])

AS_IF([test x"$cross_compiling" = x"yes"], [
	AC_SUBST(BIN_PREFIX, "${host_alias}-")

	case "$host" in
	i?86-*-mingw*)
		AC_CHECK_PROG(WINE, wine, wine)
		;;
	x86_64-*-mingw*)
		AC_CHECK_PROG(WINE, wine64, wine64)
		;;
	esac

	AS_IF([test x"$WINE" != x""], [

		AC_SUBST(WRAPPER, "$WINE")
	])

	AS_IF([test x"$with_wii" = x"yes"], [
		dnl Keep this lowercase, as WIILOAD is a variable used by
		dnl wiiload and thus likely already set by the user to something
		dnl that is not the path of the wiiload binary.
		AC_CHECK_PROG(wiiload, wiiload, wiiload)

		AS_IF([test x"$wiiload" != x""], [
			AC_SUBST(WRAPPER, "$wiiload")
		])
	])


])

AC_ARG_WITH(fish_completions,
	AS_HELP_STRING([--with-fish-completions],
		[install completions for the fish shell]))
AS_IF([test x"$with_fish_completions" = x""], [
	AC_CHECK_PROG(FISH, fish, fish)
	AS_IF([test x"$FISH" != x""], [with_fish_completions="yes"])
])
AS_IF([test x"$with_fish_completions" = x"yes"], [
	AC_SUBST(FISH_COMPLETIONS, fish)
])

dnl We don't call AC_PROG_CPP, but only AC_PROG_OBJCPP and set CPP to OBJCPP
dnl and add OBJCPPFLAGS to CPPFLAGS, thus we need to AC_SUBST these ourself.
AC_SUBST(CPP)

Modified extra.mk.in from [be5acfe5fa] to [8ae8820dcd].

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
OBJFW_SHARED_LIB = @OBJFW_SHARED_LIB@
OBJFW_STATIC_LIB = @OBJFW_STATIC_LIB@
OBJFW_FRAMEWORK = @OBJFW_FRAMEWORK@
OBJFW_LIB_MAJOR = 9
OBJFW_LIB_MINOR = 1
OBJFW_LIB_MAJOR_MINOR = ${OBJFW_LIB_MAJOR}.${OBJFW_LIB_MINOR}

OBJFWRT_SHARED_LIB = @OBJFWRT_SHARED_LIB@
OBJFWRT_STATIC_LIB = @OBJFWRT_STATIC_LIB@
OBJFWRT_FRAMEWORK = @OBJFWRT_FRAMEWORK@
OBJFWRT_AMIGA_LIB = @OBJFWRT_AMIGA_LIB@
OBJFWRT_LIB_MAJOR = 1
OBJFWRT_LIB_MINOR = 0
OBJFWRT_LIB_MAJOR_MINOR = ${OBJFWRT_LIB_MAJOR}.${OBJFWRT_LIB_MINOR}

OBJFWBRIDGE_SHARED_LIB = @OBJFWBRIDGE_SHARED_LIB@
OBJFWBRIDGE_STATIC_LIB = @OBJFWBRIDGE_STATIC_LIB@
OBJFWBRIDGE_FRAMEWORK = @OBJFWBRIDGE_FRAMEWORK@





BIN_PREFIX = @BIN_PREFIX@
BRIDGE = @BRIDGE@
CVINCLUDE_INLINE_H = @CVINCLUDE_INLINE_H@
ENCODINGS = @ENCODINGS@
ENCODINGS_A = @ENCODINGS_A@
ENCODINGS_ENCODINGS_A = @ENCODINGS_ENCODINGS_A@
ENCODINGS_ENCODINGS_LIB_A = @ENCODINGS_ENCODINGS_LIB_A@
ENCODINGS_LIB_A = @ENCODINGS_LIB_A@
ENCODINGS_SRCS = @ENCODINGS_SRCS@
EXCEPTIONS_A = @EXCEPTIONS_A@
EXCEPTIONS_EXCEPTIONS_A = @EXCEPTIONS_EXCEPTIONS_A@
EXCEPTIONS_EXCEPTIONS_LIB_A = @EXCEPTIONS_EXCEPTIONS_LIB_A@
EXCEPTIONS_LIB_A = @EXCEPTIONS_LIB_A@
FISH_COMPLETIONS = @FISH_COMPLETIONS@
FORWARDING_A = @FORWARDING_A@
FORWARDING_FORWARDING_A = @FORWARDING_FORWARDING_A@
FORWARDING_FORWARDING_LIB_A = @FORWARDING_FORWARDING_LIB_A@
FORWARDING_LIB_A = @FORWARDING_LIB_A@
INVOCATION_A = @INVOCATION_A@
INVOCATION_INVOCATION_A = @INVOCATION_INVOCATION_A@
INVOCATION_INVOCATION_LIB_A = @INVOCATION_INVOCATION_LIB_A@
INVOCATION_LIB_A = @INVOCATION_LIB_A@
LIBBASES_M = @LIBBASES_M@
LIBOBJFWRT_DEP = @LIBOBJFWRT_DEP@
LIBOBJFWRT_DEP_LVL2 = @LIBOBJFWRT_DEP_LVL2@
LIBOBJFW_DEP = @LIBOBJFW_DEP@
LIBOBJFW_DEP_LVL2 = @LIBOBJFW_DEP_LVL2@
LINKLIB = @LINKLIB@
LOOKUP_ASM_A = @LOOKUP_ASM_A@
LOOKUP_ASM_LIB_A = @LOOKUP_ASM_LIB_A@
LOOKUP_ASM_LOOKUP_ASM_A = @LOOKUP_ASM_LOOKUP_ASM_A@
LOOKUP_ASM_LOOKUP_ASM_LIB_A = @LOOKUP_ASM_LOOKUP_ASM_LIB_A@
MAP_LDFLAGS = @MAP_LDFLAGS@

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

OF_HTTP_CLIENT_TESTS_M = @OF_HTTP_CLIENT_TESTS_M@
OF_KQUEUE_KERNEL_EVENT_OBSERVER_M = @OF_KQUEUE_KERNEL_EVENT_OBSERVER_M@

OF_POLL_KERNEL_EVENT_OBSERVER_M = @OF_POLL_KERNEL_EVENT_OBSERVER_M@
OF_PROCESS_M = @OF_PROCESS_M@
OF_SCTP_SOCKET_M = @OF_SCTP_SOCKET_M@
OF_SELECT_KERNEL_EVENT_OBSERVER_M = @OF_SELECT_KERNEL_EVENT_OBSERVER_M@

REEXPORT_RUNTIME = @REEXPORT_RUNTIME@
REEXPORT_RUNTIME_FRAMEWORK = @REEXPORT_RUNTIME_FRAMEWORK@
RUNTIME = @RUNTIME@
RUNTIME_ARC_TESTS_M = @RUNTIME_ARC_TESTS_M@
RUNTIME_AUTORELEASE_M = @RUNTIME_AUTORELEASE_M@
RUNTIME_FRAMEWORK_LIBS = @RUNTIME_FRAMEWORK_LIBS@
RUNTIME_INSTANCE_M = @RUNTIME_INSTANCE_M@
RUNTIME_LIBS = @RUNTIME_LIBS@
RUN_TESTS = @RUN_TESTS@
SFDC_INLINE_H = @SFDC_INLINE_H@
SFDC_TARGET = @SFDC_TARGET@
SFD_FILE = @SFD_FILE@
TESTPLUGIN = @TESTPLUGIN@
TESTPLUGIN_LIBS = @TESTPLUGIN_LIBS@
TESTS_LIBS = @TESTS_LIBS@
TESTS_STATIC_LIB = @TESTS_STATIC_LIB@



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_SCTP = @USE_SRCS_SCTP@
USE_SRCS_SOCKETS = @USE_SRCS_SOCKETS@
USE_SRCS_THREADS = @USE_SRCS_THREADS@

USE_SRCS_WINDOWS = @USE_SRCS_WINDOWS@
WRAPPER = @WRAPPER@



|
|






|







>
>
>
>



<

<
<



<
<



<
<

<
<
<
<







|
<
|

>




|


>


>

<
|

>








<







>
>
>





<


>


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
OBJFW_SHARED_LIB = @OBJFW_SHARED_LIB@
OBJFW_STATIC_LIB = @OBJFW_STATIC_LIB@
OBJFW_FRAMEWORK = @OBJFW_FRAMEWORK@
OBJFW_LIB_MAJOR = 0
OBJFW_LIB_MINOR = 0
OBJFW_LIB_MAJOR_MINOR = ${OBJFW_LIB_MAJOR}.${OBJFW_LIB_MINOR}

OBJFWRT_SHARED_LIB = @OBJFWRT_SHARED_LIB@
OBJFWRT_STATIC_LIB = @OBJFWRT_STATIC_LIB@
OBJFWRT_FRAMEWORK = @OBJFWRT_FRAMEWORK@
OBJFWRT_AMIGA_LIB = @OBJFWRT_AMIGA_LIB@
OBJFWRT_LIB_MAJOR = 0
OBJFWRT_LIB_MINOR = 0
OBJFWRT_LIB_MAJOR_MINOR = ${OBJFWRT_LIB_MAJOR}.${OBJFWRT_LIB_MINOR}

OBJFWBRIDGE_SHARED_LIB = @OBJFWBRIDGE_SHARED_LIB@
OBJFWBRIDGE_STATIC_LIB = @OBJFWBRIDGE_STATIC_LIB@
OBJFWBRIDGE_FRAMEWORK = @OBJFWBRIDGE_FRAMEWORK@

OBJFWTLS_SHARED_LIB = @OBJFWTLS_SHARED_LIB@
OBJFWTLS_STATIC_LIB = @OBJFWTLS_STATIC_LIB@
OBJFWTLS_FRAMEWORK = @OBJFWTLS_FRAMEWORK@

BIN_PREFIX = @BIN_PREFIX@
BRIDGE = @BRIDGE@
CVINCLUDE_INLINE_H = @CVINCLUDE_INLINE_H@

ENCODINGS_A = @ENCODINGS_A@


ENCODINGS_LIB_A = @ENCODINGS_LIB_A@
ENCODINGS_SRCS = @ENCODINGS_SRCS@
EXCEPTIONS_A = @EXCEPTIONS_A@


EXCEPTIONS_LIB_A = @EXCEPTIONS_LIB_A@
FISH_COMPLETIONS = @FISH_COMPLETIONS@
FORWARDING_A = @FORWARDING_A@


FORWARDING_LIB_A = @FORWARDING_LIB_A@




LIBBASES_M = @LIBBASES_M@
LIBOBJFWRT_DEP = @LIBOBJFWRT_DEP@
LIBOBJFWRT_DEP_LVL2 = @LIBOBJFWRT_DEP_LVL2@
LIBOBJFW_DEP = @LIBOBJFW_DEP@
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@
OF_GNUTLS_TLS_STREAM_M = @OF_GNUTLS_TLS_STREAM_M@
OF_HTTP_CLIENT_TESTS_M = @OF_HTTP_CLIENT_TESTS_M@
OF_KQUEUE_KERNEL_EVENT_OBSERVER_M = @OF_KQUEUE_KERNEL_EVENT_OBSERVER_M@
OF_OPENSSL_TLS_STREAM_M = @OF_OPENSSL_TLS_STREAM_M@
OF_POLL_KERNEL_EVENT_OBSERVER_M = @OF_POLL_KERNEL_EVENT_OBSERVER_M@

OF_SECURE_TRANSPORT_TLS_STREAM_M = @OF_SECURE_TRANSPORT_TLS_STREAM_M@
OF_SELECT_KERNEL_EVENT_OBSERVER_M = @OF_SELECT_KERNEL_EVENT_OBSERVER_M@
OF_SUBPROCESS_M = @OF_SUBPROCESS_M@
REEXPORT_RUNTIME = @REEXPORT_RUNTIME@
REEXPORT_RUNTIME_FRAMEWORK = @REEXPORT_RUNTIME_FRAMEWORK@
RUNTIME = @RUNTIME@
RUNTIME_ARC_TESTS_M = @RUNTIME_ARC_TESTS_M@
RUNTIME_AUTORELEASE_M = @RUNTIME_AUTORELEASE_M@
RUNTIME_FRAMEWORK_LIBS = @RUNTIME_FRAMEWORK_LIBS@
RUNTIME_INSTANCE_M = @RUNTIME_INSTANCE_M@
RUNTIME_LIBS = @RUNTIME_LIBS@

SFDC_INLINE_H = @SFDC_INLINE_H@
SFDC_TARGET = @SFDC_TARGET@
SFD_FILE = @SFD_FILE@
TESTPLUGIN = @TESTPLUGIN@
TESTPLUGIN_LIBS = @TESTPLUGIN_LIBS@
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@

Added generators/library/FuncArrayGenerator.h version [2d0edf7996].





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFStream.h"
#import "OFXMLElement.h"

@interface FuncArrayGenerator: OFObject
{
	OFXMLElement *_library;
	OFStream *_include;
}

- (instancetype)initWithLibrary: (OFXMLElement *)library
			include: (OFStream *)include;
- (void)generate;
@end

Added generators/library/FuncArrayGenerator.m version [4ed090453c].





























































































































































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

#import "OFArray.h"
#import "OFXMLAttribute.h"

#import "FuncArrayGenerator.h"

#import "OFInvalidFormatException.h"
#import "OFUnsupportedVersionException.h"

#import "copyright.h"

@implementation FuncArrayGenerator
- (instancetype)initWithLibrary: (OFXMLElement *)library
			include: (OFStream *)include
{
	self = [super init];

	@try {
		OFXMLAttribute *version;

		if (![library.name isEqual: @"amiga-library"] ||
		    library.namespace != nil)
			@throw [OFInvalidFormatException exception];

		if ((version = [library attributeForName: @"version"]) == nil)
			@throw [OFInvalidFormatException exception];

		if (![version.stringValue isEqual: @"1.0"])
			@throw [OFUnsupportedVersionException
			    exceptionWithVersion: version.stringValue];

		_library = [library retain];
		_include = [include retain];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_library release];
	[_include release];

	[super dealloc];
}

- (void)generate
{
	[_include writeString: COPYRIGHT];
	[_include writeString:
	    @"/* This file is automatically generated from amiga-library.xml */"
	    @"\n\n"];

	for (OFXMLElement *function in [_library elementsForName: @"function"])
		[_include writeFormat:
		    @"(CONST_APTR)glue_%@,\n",
		    [function attributeForName: @"name"].stringValue];
}
@end

Added generators/library/GlueGenerator.h version [df3f9366c5].































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFStream.h"
#import "OFXMLElement.h"

@interface GlueGenerator: OFObject
{
	OFXMLElement *_library;
	OFStream *_header, *_impl;
}

- (instancetype)initWithLibrary: (OFXMLElement *)library
			 header: (OFStream *)header
		 implementation: (OFStream *)implementation;
- (void)generate;
@end

Added generators/library/GlueGenerator.m version [a4f73aa06c].































































































































































































































































































































































































































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

#import "OFArray.h"
#import "OFXMLAttribute.h"

#import "GlueGenerator.h"

#import "OFInvalidFormatException.h"
#import "OFUnsupportedVersionException.h"

#import "copyright.h"

@implementation GlueGenerator
- (instancetype)initWithLibrary: (OFXMLElement *)library
			 header: (OFStream *)header
		 implementation: (OFStream *)impl
{
	self = [super init];

	@try {
		OFXMLAttribute *version;

		if (![library.name isEqual: @"amiga-library"] ||
		    library.namespace != nil)
			@throw [OFInvalidFormatException exception];

		if ((version = [library attributeForName: @"version"]) == nil)
			@throw [OFInvalidFormatException exception];

		if (![version.stringValue isEqual: @"1.0"])
			@throw [OFUnsupportedVersionException
			    exceptionWithVersion: version.stringValue];

		_library = [library retain];
		_header = [header retain];
		_impl = [impl retain];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_library release];
	[_header release];
	[_impl release];

	[super dealloc];
}

- (void)generate
{
	[_header writeString: COPYRIGHT];
	[_impl writeString: COPYRIGHT];

	[_header writeString:
	    @"/* This file is automatically generated from amiga-library.xml */"
	    @"\n\n"];

	[_impl writeString:
	    @"/* This file is automatically generated from amiga-library.xml */"
	    @"\n\n"
	    @"#include \"config.h\"\n"
	    @"\n"
	    @"#import \"amiga-glue.h\"\n"
	    @"\n"];

	for (OFXMLElement *include in [_library elementsForName: @"include"])
		[_header writeFormat: @"#import \"%@\"\n", include.stringValue];

	[_header writeString:
	    @"\n"
	    @"#ifdef OF_AMIGAOS_M68K\n"
	    @"# define PPC_PARAMS(...) (void)\n"
	    @"# define M68K_ARG(type, name, reg)\t\t\\\n"
	    @"\tregister type reg##name __asm__(#reg);\t\\\n"
	    @"\ttype name = reg##name;\n"
	    @"#else\n"
	    @"# define PPC_PARAMS(...) (__VA_ARGS__)\n"
	    @"# define M68K_ARG(...)\n"
	    @"#endif\n"
	    @"\n"];
	[_impl writeString:
	    @"#ifdef OF_MORPHOS\n"
	    @"/* All __saveds functions in this file need to use the SysV "
	    @"ABI */\n"
	    @"__asm__ (\n"
	    @"    \".section .text\\n\"\n"
	    @"    \".align 2\\n\"\n"
	    @"    \"__restore_r13:\\n\"\n"
	    @"    \"\tlwz\t%r13, 44(%r12)\\n\"\n"
	    @"    \"\tblr\\n\"\n"
	    @");\n"
	    @"#endif\n"];

	for (OFXMLElement *function in
	    [_library elementsForName: @"function"]) {
		OFString *name =
		    [function attributeForName: @"name"].stringValue;
		OFString *returnType =
		    [function attributeForName: @"return-type"].stringValue;
		OFArray OF_GENERIC(OFXMLElement *) *arguments =
		    [function elementsForName: @"argument"];
		size_t argumentIndex;

		if (returnType == nil)
			returnType = @"void";

		[_header writeFormat:
		    @"extern %@%@glue_%@",
		    returnType,
		    (![returnType hasSuffix: @"*"] ? @" " : @""),
		    name];

		[_impl writeFormat: @"\n"
				    @"%@ __saveds\n"
				    @"glue_%@",
				    returnType, name];

		if (arguments.count > 0) {
			[_header writeString: @" PPC_PARAMS("];
			[_impl writeString: @" PPC_PARAMS("];
		} else {
			[_header writeString: @"(void"];
			[_impl writeString: @"(void"];
		}

		argumentIndex = 0;
		for (OFXMLElement *argument in arguments) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;
			OFString *argType =
			    [argument attributeForName: @"type"].stringValue;

			if (argumentIndex++ > 0) {
				[_header writeString: @", "];
				[_impl writeString: @", "];
			}

			[_header writeString: argType];
			[_impl writeString: argType];
			if (![argType hasSuffix: @"*"]) {
				[_header writeString: @" "];
				[_impl writeString: @" "];
			}
			[_header writeString: argName];
			[_impl writeString: argName];
		}

		[_header writeString: @");\n"];

		[_impl writeString: @")\n{\n"];
		for (OFXMLElement *argument in arguments) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;
			OFString *argType =
			    [argument attributeForName: @"type"].stringValue;
			OFString *m68kReg = [argument
			    attributeForName: @"m68k-reg"].stringValue;

			[_impl writeFormat: @"\tM68K_ARG(%@, %@, %@)\n",
					    argType, argName, m68kReg];
		}

		if (arguments.count > 0)
			[_impl writeString: @"\n"];

		if (![returnType isEqual: @"void"])
			[_impl writeString: @"\treturn "];
		else
			[_impl writeString: @"\t"];

		[_impl writeFormat: @"%@(", name];

		argumentIndex = 0;
		for (OFXMLElement *argument in arguments) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;

			if (argumentIndex++ > 0)
				[_impl writeString: @", "];

			[_impl writeString: argName];
		}

		[_impl writeString: @");\n}\n"];
	}
}
@end

Added generators/library/LibraryGenerator.m version [0e687972e3].



































































































































































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

#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
	     implementation: runtimeGlue] autorelease];
	FuncArrayGenerator *runtimeFuncArrayGenerator;
	runtimeFuncArrayGenerator = [[[FuncArrayGenerator alloc]
	    initWithLibrary: runtimeLibrary
		    include: runtimeFuncArray] autorelease];

	[runtimeLinkLibGenerator generate];
	[runtimeGlueGenerator generate];
	[runtimeFuncArrayGenerator generate];

	[OFApplication terminate];
}
@end

Added generators/library/LinkLibGenerator.h version [58647bf17f].



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFStream.h"
#import "OFXMLElement.h"

@interface LinkLibGenerator: OFObject
{
	OFXMLElement *_library;
	OFStream *_impl;
}

- (instancetype)initWithLibrary: (OFXMLElement *)library
		 implementation: (OFStream *)impl;
- (void)generate;
@end

Added generators/library/LinkLibGenerator.m version [e9161f0524].





































































































































































































































































































































































































































































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

#import "OFArray.h"
#import "OFXMLAttribute.h"

#import "LinkLibGenerator.h"

#import "OFInvalidFormatException.h"
#import "OFUnsupportedVersionException.h"

#import "copyright.h"

@implementation LinkLibGenerator
- (instancetype)initWithLibrary: (OFXMLElement *)library
		 implementation: (OFStream *)impl
{
	self = [super init];

	@try {
		OFXMLAttribute *version;

		if (![library.name isEqual: @"amiga-library"] ||
		    library.namespace != nil)
			@throw [OFInvalidFormatException exception];

		if ((version = [library attributeForName: @"version"]) == nil)
			@throw [OFInvalidFormatException exception];

		if (![version.stringValue isEqual: @"1.0"])
			@throw [OFUnsupportedVersionException
			    exceptionWithVersion: version.stringValue];

		_library = [library retain];
		_impl = [impl retain];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_library release];
	[_impl release];

	[super dealloc];
}

- (void)generate
{
	OFString *libBase = [_library attributeForName: @"base"].stringValue;
	OFArray OF_GENERIC(OFXMLElement *) *functions;
	size_t funcIndex = 0;

	[_impl writeString: COPYRIGHT];
	[_impl writeString:
	    @"/* This file is automatically generated from amiga-library.xml */"
	    @"\n\n"
	    @"#include \"config.h\"\n"
	    @"\n"];

	for (OFXMLElement *include in [_library elementsForName: @"include"])
		[_impl writeFormat: @"#import \"%@\"\n",
				    include.stringValue];

	[_impl writeFormat: @"\n"
			    @"extern struct Library *%@;\n"
			    @"\n",
			    libBase];

	functions = [_library elementsForName: @"function"];
	for (OFXMLElement *function in functions) {
		OFString *name =
		    [function attributeForName: @"name"].stringValue;
		OFString *returnType =
		    [function attributeForName: @"return-type"].stringValue;
		OFArray OF_GENERIC(OFXMLElement *) *arguments =
		    [function elementsForName: @"argument"];
		size_t argumentIndex;

		if (returnType == nil)
			returnType = @"void";

		[_impl writeFormat: @"%@\n%@(", returnType, name];

		argumentIndex = 0;
		for (OFXMLElement *argument in
		    [function elementsForName: @"argument"]) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;
			OFString *argType =
			    [argument attributeForName: @"type"].stringValue;

			if (argumentIndex++ > 0)
				[_impl writeString: @", "];

			[_impl writeString: argType];
			if (![argType hasSuffix: @"*"])
				[_impl writeString: @" "];
			[_impl writeString: argName];
		}

		[_impl writeFormat:
		    @")\n"
		    @"{\n"
		    @"#if defined(OF_AMIGAOS_M68K)\n"
		    @"\tregister struct Library *a6 __asm__(\"a6\") = %@;\n"
		    @"\t(void)a6;\n"
		    @"\t", libBase];

		if (![returnType isEqual: @"void"])
			[_impl writeString: @"return "];

		[_impl writeString: @"(("];
		[_impl writeString: returnType];
		if (![returnType hasSuffix: @"*"])
			[_impl writeString: @" "];
		[_impl writeString: @"(*)("];

		argumentIndex = 0;
		for (OFXMLElement *argument in arguments) {
			OFString *argType =
			    [argument attributeForName: @"type"].stringValue;
			OFString *m68kReg = [argument
			    attributeForName: @"m68k-reg"].stringValue;

			if (argumentIndex++ > 0)
				[_impl writeString: @", "];

			[_impl writeString: argType];
			if (![argType hasSuffix: @"*"])
				[_impl writeString: @" "];
			[_impl writeFormat: @"__asm__(\"%@\")",
						    m68kReg];
		}

		[_impl writeFormat: @"))(((uintptr_t)%@) - %zu))(",
				    libBase, 30 + funcIndex * 6];

		argumentIndex = 0;
		for (OFXMLElement *argument in
		    [function elementsForName: @"argument"]) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;

			if (argumentIndex++ > 0)
				[_impl writeString: @", "];

			[_impl writeString: argName];
		}

		[_impl writeFormat: @");\n"
				    @"#elif defined(OF_MORPHOS)\n"
				    @"\t__asm__ __volatile__ (\n"
				    @"\t    \"mr\t\t%%%%r12, %%0\"\n"
				    @"\t    :: \"r\"(%@) : \"r12\"\n"
				    @"\t);\n"
				    @"\n"
				    @"\t",
				    libBase, libBase];

		if (![returnType isEqual: @"void"])
			[_impl writeString: @"return "];

		[_impl writeString: @"__extension__ (("];
		[_impl writeString: returnType];
		if (![returnType hasSuffix: @"*"])
			[_impl writeString: @" "];
		[_impl writeString: @"(*)("];

		argumentIndex = 0;
		for (OFXMLElement *argument in arguments) {
			OFString *argType =
			    [argument attributeForName: @"type"].stringValue;

			if (argumentIndex++ > 0)
				[_impl writeString: @", "];

			[_impl writeString: argType];
		}

		[_impl writeFormat: @"))*(void **)(((uintptr_t)%@) - %zu))(",
				    libBase, 28 + funcIndex * 6];

		argumentIndex = 0;
		for (OFXMLElement *argument in
		    [function elementsForName: @"argument"]) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;

			if (argumentIndex++ > 0)
				[_impl writeString: @", "];

			[_impl writeString: argName];
		}

		[_impl writeString: @");\n"
				    @"#endif\n"];

		if ([function attributeForName: @"noreturn"] != nil)
			[_impl writeString: @"\n\tOF_UNREACHABLE\n"];

		[_impl writeString: @"}\n"];

		if (++funcIndex < functions.count)
			[_impl writeString: @"\n"];
	}
}
@end

Added generators/library/Makefile version [b62d874949].























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
include ../../extra.mk

PROG_NOINST = gen_libraries${PROG_SUFFIX}
SRCS = FuncArrayGenerator.m	\
       GlueGenerator.m		\
       LibraryGenerator.m	\
       LinkLibGenerator.m

include ../../buildsys.mk

.PHONY: run
run: all
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw${OBJFW_LIB_MAJOR}.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../../src/libobjfw.so; then \
		${LN_S} ../../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/objfw${OBJFW_LIB_MAJOR}.dll; then \
		${LN_S} ../../src/objfw${OBJFW_LIB_MAJOR}.dll \
			objfw${OBJFW_LIB_MAJOR}.dll; \
	fi
	if test -f ../../src/libobjfw.dylib; then \
		${LN_S} ../../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/libobjfwrt.so; then \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll; then \
		${LN_S} ../../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll \
			objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	fi
	if test -f ../../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_FRAMEWORK_PATH=../../src:../../src/runtime$${DYLD_FRAMEWORK_PATH+:}$$DYLD_FRAMEWORK_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	ASAN_OPTIONS=allocator_may_return_null=1 \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	rm -f objfw${OBJFW_LIB_MAJOR}.dll; \
	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

CPPFLAGS += -I../../src -I../../src/exceptions -I../../src/runtime -I../..
LIBS := -L../../src -lobjfw -L../../src/runtime ${RUNTIME_LIBS} ${LIBS}
LD = ${OBJC}

Added generators/library/copyright.h version [c0b22b50fd].













































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFString.h"

#define COPYRIGHT \
    @"/*\n"								       \
    @" * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>\n"	       \
    @" *\n"								       \
    @" * All rights reserved.\n"					       \
    @" *\n"								       \
    @" * This file is part of ObjFW. It may be distributed under the terms "   \
    @"of the\n"								       \
    @" * Q Public License 1.0, which can be found in the file LICENSE.QPL "    \
    @"included in\n"							       \
    @" * the packaging of this file.\n"					       \
    @" *\n"								       \
    @" * Alternatively, it may be distributed under the terms of the GNU "     \
    @"General\n"							       \
    @" * Public License, either version 2 or 3, which can be found in the "    \
    @"file\n"								       \
    @" * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the "	       \
    @"packaging of this\n"						       \
    @" * file.\n"							       \
    @" */\n"								       \
    @"\n"

Renamed and modified generators/Makefile [0e25880b06] to generators/unicode/Makefile [9009fb89e7].

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
include ../extra.mk

PROG_NOINST = gen_tables${PROG_SUFFIX}
SRCS = TableGenerator.m



.PHONY: run
run: all
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}

	rm -f objfwrt.dll libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../src/libobjfw.so; then \
		${LN_S} ../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../src/objfw.dll; then \
		${LN_S} ../src/objfw.dll objfw.dll; \

	fi
	if test -f ../src/libobjfw.dylib; then \
		${LN_S} ../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../src/runtime/libobjfwrt.so; then \
		${LN_S} ../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../src/runtime/objfwrt.dll; then \
		${LN_S} ../src/runtime/objfwrt.dll objfwrt.dll; \

	fi
	if test -f ../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_FRAMEWORK_PATH=../src:../src/runtime$${DYLD_FRAMEWORK_PATH+:}$$DYLD_FRAMEWORK_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	ASAN_OPTIONS=allocator_may_return_null=1 \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f objfw.so.${OBJFW_LIB_MAJOR_MINOR} objfw.dll; \

	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f objfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} objfwrt.dll; \

	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

include ../buildsys.mk

CPPFLAGS += -I../src -I../src/exceptions -I../src/runtime -I..
LIBS := -L../src -lobjfw -L../src/runtime ${RUNTIME_LIBS} ${LIBS}
LD = ${OBJC}
|




>
>




|


>
|

|
|
|

|
|


|
|
>

|
|


|
|

|

|
|

|
|
>

|
|


|
|



|





|
>


|
>



<
|
<
|

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
include ../../extra.mk

PROG_NOINST = gen_tables${PROG_SUFFIX}
SRCS = TableGenerator.m

include ../../buildsys.mk

.PHONY: run
run: all
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw${OBJFW_LIB_MAJOR}.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../../src/libobjfw.so; then \
		${LN_S} ../../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/objfw${OBJFW_LIB_MAJOR}.dll; then \
		${LN_S} ../../src/objfw${OBJFW_LIB_MAJOR}.dll \
			objfw${OBJFW_LIB_MAJOR}.dll; \
	fi
	if test -f ../../src/libobjfw.dylib; then \
		${LN_S} ../../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/libobjfwrt.so; then \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll; then \
		${LN_S} ../../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll \
			objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	fi
	if test -f ../../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_FRAMEWORK_PATH=../../src:../../src/runtime$${DYLD_FRAMEWORK_PATH+:}$$DYLD_FRAMEWORK_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	ASAN_OPTIONS=allocator_may_return_null=1 \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	rm -f objfw${OBJFW_LIB_MAJOR}.dll; \
	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT


CPPFLAGS += -I../../src -I../../src/exceptions -I../../src/runtime -I../..

LIBS := -L../../src -lobjfw -L../../src/runtime ${RUNTIME_LIBS} ${LIBS}
LD = ${OBJC}

Renamed and modified generators/TableGenerator.h [1738fc87e6] to generators/unicode/TableGenerator.h [d5ba8e12b8].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

@class OFString;

@interface TableGenerator: OFObject <OFApplicationDelegate,
    OFHTTPClientDelegate>
{
	OFHTTPClient *_HTTPClient;
	of_unichar_t _uppercaseTable[0x110000];
	of_unichar_t _lowercaseTable[0x110000];
	of_unichar_t _titlecaseTable[0x110000];
	of_unichar_t _casefoldingTable[0x110000];
	OFString *_decompositionTable[0x110000];
	OFString *_decompositionCompatTable[0x110000];
	char _uppercaseTableUsed[0x1100];
	char _lowercaseTableUsed[0x1100];
	char _titlecaseTableUsed[0x1100];
	char _casefoldingTableUsed[0x1100];
	char _decompositionTableUsed[0x1100];
	char _decompositionCompatTableUsed[0x1100];
	size_t _uppercaseTableSize;
	size_t _lowercaseTableSize;
	size_t _titlecaseTableSize;
	size_t _casefoldingTableSize;
	size_t _decompositionTableSize;
	size_t _decompositionCompatTableSize;
	enum {
		STATE_UNICODE_DATA,
		STATE_CASE_FOLDING
	} _state;
}

- (void)parseUnicodeData: (OFHTTPResponse *)response;
- (void)parseCaseFolding: (OFHTTPResponse *)response;
- (void)applyDecompositionRecursivelyForTable: (OFString *[0x110000])table;
- (void)writeFiles;
- (void)writeTablesToFile: (OFString *)path;
- (void)writeHeaderToFile: (OFString *)path;
@end







|
|
|
|





|





|



|
|










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

@class OFString;

@interface TableGenerator: OFObject <OFApplicationDelegate,
    OFHTTPClientDelegate>
{
	OFHTTPClient *_HTTPClient;
	OFUnichar _uppercaseTable[0x110000];
	OFUnichar _lowercaseTable[0x110000];
	OFUnichar _titlecaseTable[0x110000];
	OFUnichar _caseFoldingTable[0x110000];
	OFString *_decompositionTable[0x110000];
	OFString *_decompositionCompatTable[0x110000];
	char _uppercaseTableUsed[0x1100];
	char _lowercaseTableUsed[0x1100];
	char _titlecaseTableUsed[0x1100];
	char _caseFoldingTableUsed[0x1100];
	char _decompositionTableUsed[0x1100];
	char _decompositionCompatTableUsed[0x1100];
	size_t _uppercaseTableSize;
	size_t _lowercaseTableSize;
	size_t _titlecaseTableSize;
	size_t _caseFoldingTableSize;
	size_t _decompositionTableSize;
	size_t _decompositionCompatTableSize;
	enum {
		stateUnicodeData,
		stateCaseFolding
	} _state;
}

- (void)parseUnicodeData: (OFHTTPResponse *)response;
- (void)parseCaseFolding: (OFHTTPResponse *)response;
- (void)applyDecompositionRecursivelyForTable: (OFString *[0x110000])table;
- (void)writeFiles;
- (void)writeTablesToFile: (OFString *)path;
- (void)writeHeaderToFile: (OFString *)path;
@end

Renamed and modified generators/TableGenerator.m [e134a30212] to generators/unicode/TableGenerator.m [442fb49647].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFStdIOStream.h"

#import "OFOutOfRangeException.h"

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

#define UNICODE_DATA_URL \
	@"http://www.unicode.org/Public/UNIDATA/UnicodeData.txt"
#define CASE_FOLDING_URL \
	@"http://www.unicode.org/Public/UNIDATA/CaseFolding.txt"

OF_APPLICATION_DELEGATE(TableGenerator)

@implementation TableGenerator
- (instancetype)init
{
	self = [super init];

	@try {
		_HTTPClient = [[OFHTTPClient alloc] init];
		_HTTPClient.delegate = self;

		_uppercaseTableSize           = SIZE_MAX;
		_lowercaseTableSize           = SIZE_MAX;
		_titlecaseTableSize           = SIZE_MAX;
		_casefoldingTableSize         = SIZE_MAX;
		_decompositionTableSize       = SIZE_MAX;
		_decompositionCompatTableSize = SIZE_MAX;
	} @catch (id e) {
		@throw e;
		[self release];

	}

	return self;
}

- (void)applicationDidFinishLaunching
{
	OFHTTPRequest *request;

	[of_stdout writeString: @"Downloading UnicodeData.txt…"];
	_state = STATE_UNICODE_DATA;
	request = [OFHTTPRequest requestWithURL:
	    [OFURL URLWithString: UNICODE_DATA_URL]];
	[_HTTPClient asyncPerformRequest: request];
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response
	  exception: (id)exception
{
	if (exception != nil)
		@throw exception;

	[of_stdout writeLine: @" done"];

	switch (_state) {
	case STATE_UNICODE_DATA:
		[self parseUnicodeData: response];
		break;
	case STATE_CASE_FOLDING:
		[self parseCaseFolding: response];
		break;
	}
}

- (void)parseUnicodeData: (OFHTTPResponse *)response
{
	OFString *line;
	OFHTTPRequest *request;

	[of_stdout writeString: @"Parsing UnicodeData.txt…"];

	while ((line = [response readLine]) != nil) {
		void *pool2;
		OFArray OF_GENERIC(OFString *) *components;
		of_unichar_t codePoint;

		if (line.length == 0)
			continue;

		pool2 = objc_autoreleasePoolPush();

		components = [line componentsSeparatedByString: @";"];
		if (components.count != 15) {
			of_log(@"Invalid line: %@\n", line);
			[OFApplication terminateWithStatus: 1];
		}

		codePoint = (of_unichar_t)[[components objectAtIndex: 0]
		    unsignedLongLongValueWithBase: 16];

		if (codePoint > 0x10FFFF)
			@throw [OFOutOfRangeException exception];

		_uppercaseTable[codePoint] = (of_unichar_t)[[components
		    objectAtIndex: 12] unsignedLongLongValueWithBase: 16];
		_lowercaseTable[codePoint] = (of_unichar_t)[[components
		    objectAtIndex: 13] unsignedLongLongValueWithBase: 16];
		_titlecaseTable[codePoint] = (of_unichar_t)[[components
		    objectAtIndex: 14] unsignedLongLongValueWithBase: 16];

		if ([[components objectAtIndex: 5] length] > 0) {
			OFArray *decomposed = [[components objectAtIndex: 5]
			    componentsSeparatedByString: @" "];
			bool compat = false;
			OFMutableString *string;

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

			string = [OFMutableString string];

			for (OFString *character in decomposed) {
				of_unichar_t unichar = (of_unichar_t)[character
				    unsignedLongLongValueWithBase: 16];

				[string appendCharacters: &unichar
						  length: 1];
			}

			[string makeImmutable];

			if (!compat)
				_decompositionTable[codePoint] = [string copy];
			_decompositionCompatTable[codePoint] = [string copy];
		}

		objc_autoreleasePoolPop(pool2);
	}

	[self applyDecompositionRecursivelyForTable: _decompositionTable];
	[self applyDecompositionRecursivelyForTable: _decompositionCompatTable];

	[of_stdout writeLine: @" done"];

	[of_stdout writeString: @"Downloading CaseFolding.txt…"];
	_state = STATE_CASE_FOLDING;
	request = [OFHTTPRequest requestWithURL:
	    [OFURL URLWithString: CASE_FOLDING_URL]];
	[_HTTPClient asyncPerformRequest: request];
}

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

	[of_stdout writeString: @"Parsing CaseFolding.txt…"];

	while ((line = [response readLine]) != nil) {
		void *pool2;
		OFArray OF_GENERIC(OFString *) *components;
		of_unichar_t codePoint;

		if (line.length == 0 || [line hasPrefix: @"#"])
			continue;

		pool2 = objc_autoreleasePoolPush();

		components = [line componentsSeparatedByString: @"; "];
		if (components.count != 4) {
			of_log(@"Invalid line: %s\n", line);
			[OFApplication terminateWithStatus: 1];
		}

		if (![[components objectAtIndex: 1] isEqual: @"S"] &&
		    ![[components objectAtIndex: 1] isEqual: @"C"])
			continue;

		codePoint = (of_unichar_t)[[components objectAtIndex: 0]
		    unsignedLongLongValueWithBase: 16];

		if (codePoint > 0x10FFFF)
			@throw [OFOutOfRangeException exception];

		_casefoldingTable[codePoint] = (of_unichar_t)[[components
		    objectAtIndex: 2] unsignedLongLongValueWithBase: 16];

		objc_autoreleasePoolPop(pool2);
	}

	[of_stdout writeLine: @" done"];

	[self writeFiles];
}

- (void)applyDecompositionRecursivelyForTable: (OFString *[0x110000])table
{
	bool done;

	do {
		done = true;

		for (of_unichar_t i = 0; i < 0x110000; i++) {
			void *pool;
			const of_unichar_t *characters;
			size_t length;
			OFMutableString *replacement;
			bool changed = false;

			if (table[i] == nil)
				continue;








|
|
|
|















|



<

>









|
|

|











|


|


|










|




|








|



|





|

|

|










|






|



















|

|
|

|







|




|








|







|





|





|











|

|







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
#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
{
	self = [super init];

	@try {
		_HTTPClient = [[OFHTTPClient alloc] init];
		_HTTPClient.delegate = self;

		_uppercaseTableSize           = SIZE_MAX;
		_lowercaseTableSize           = SIZE_MAX;
		_titlecaseTableSize           = SIZE_MAX;
		_caseFoldingTableSize         = SIZE_MAX;
		_decompositionTableSize       = SIZE_MAX;
		_decompositionCompatTableSize = SIZE_MAX;
	} @catch (id e) {

		[self release];
		@throw e;
	}

	return self;
}

- (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
{
	if (exception != nil)
		@throw exception;

	[OFStdOut writeLine: @" done"];

	switch (_state) {
	case stateUnicodeData:
		[self parseUnicodeData: response];
		break;
	case stateCaseFolding:
		[self parseCaseFolding: response];
		break;
	}
}

- (void)parseUnicodeData: (OFHTTPResponse *)response
{
	OFString *line;
	OFHTTPRequest *request;

	[OFStdOut writeString: @"Parsing UnicodeData.txt…"];

	while ((line = [response readLine]) != nil) {
		void *pool2;
		OFArray OF_GENERIC(OFString *) *components;
		OFUnichar codePoint;

		if (line.length == 0)
			continue;

		pool2 = objc_autoreleasePoolPush();

		components = [line componentsSeparatedByString: @";"];
		if (components.count != 15) {
			OFLog(@"Invalid line: %@\n", line);
			[OFApplication terminateWithStatus: 1];
		}

		codePoint = (OFUnichar)[[components objectAtIndex: 0]
		    unsignedLongLongValueWithBase: 16];

		if (codePoint > 0x10FFFF)
			@throw [OFOutOfRangeException exception];

		_uppercaseTable[codePoint] = (OFUnichar)[[components
		    objectAtIndex: 12] unsignedLongLongValueWithBase: 16];
		_lowercaseTable[codePoint] = (OFUnichar)[[components
		    objectAtIndex: 13] unsignedLongLongValueWithBase: 16];
		_titlecaseTable[codePoint] = (OFUnichar)[[components
		    objectAtIndex: 14] unsignedLongLongValueWithBase: 16];

		if ([[components objectAtIndex: 5] length] > 0) {
			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
				    unsignedLongLongValueWithBase: 16];

				[string appendCharacters: &unichar
						  length: 1];
			}

			[string makeImmutable];

			if (!compat)
				_decompositionTable[codePoint] = [string copy];
			_decompositionCompatTable[codePoint] = [string copy];
		}

		objc_autoreleasePoolPop(pool2);
	}

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

	[OFStdOut writeString: @"Parsing CaseFolding.txt…"];

	while ((line = [response readLine]) != nil) {
		void *pool2;
		OFArray OF_GENERIC(OFString *) *components;
		OFUnichar codePoint;

		if (line.length == 0 || [line hasPrefix: @"#"])
			continue;

		pool2 = objc_autoreleasePoolPush();

		components = [line componentsSeparatedByString: @"; "];
		if (components.count != 4) {
			OFLog(@"Invalid line: %s\n", line);
			[OFApplication terminateWithStatus: 1];
		}

		if (![[components objectAtIndex: 1] isEqual: @"S"] &&
		    ![[components objectAtIndex: 1] isEqual: @"C"])
			continue;

		codePoint = (OFUnichar)[[components objectAtIndex: 0]
		    unsignedLongLongValueWithBase: 16];

		if (codePoint > 0x10FFFF)
			@throw [OFOutOfRangeException exception];

		_caseFoldingTable[codePoint] = (OFUnichar)[[components
		    objectAtIndex: 2] unsignedLongLongValueWithBase: 16];

		objc_autoreleasePoolPop(pool2);
	}

	[OFStdOut writeLine: @" done"];

	[self writeFiles];
}

- (void)applyDecompositionRecursivelyForTable: (OFString *[0x110000])table
{
	bool done;

	do {
		done = true;

		for (OFUnichar i = 0; i < 0x110000; i++) {
			void *pool;
			const OFUnichar *characters;
			size_t length;
			OFMutableString *replacement;
			bool changed = false;

			if (table[i] == nil)
				continue;

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
	} while (!done);
}

- (void)writeFiles
{
	OFURL *URL;

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

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

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

	[of_stdout writeLine: @" done"];

	[OFApplication terminate];
}

- (void)writeTablesToFile: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file = [OFFile fileWithPath: path
				       mode: @"w"];

	[file writeString: COPYRIGHT
	    @"#include \"config.h\"\n"
	    @"\n"
	    @"#import \"OFString.h\"\n\n"
	    @"static const of_unichar_t emptyPage[0x100] = { 0 };\n"
	    @"static const char *emptyDecompositionPage[0x100] = { NULL };\n"
	    @"\n"];

	/* Write uppercasePage%u */
	for (of_unichar_t i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (of_unichar_t j = i; j < i + 0x100; j++) {
			if (_uppercaseTable[j] != 0) {
				isEmpty = false;
				_uppercaseTableSize = i >> 8;
				_uppercaseTableUsed[_uppercaseTableSize] = 1;
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const of_unichar_t "
					   @"uppercasePage%u[0x100] = {\n",
					   i >> 8];

			for (of_unichar_t j = i; j < i + 0x100; j += 8)
				[file writeFormat:
				    @"\t%u, %u, %u, %u, %u, %u, %u, %u,\n",
				    _uppercaseTable[j],
				    _uppercaseTable[j + 1],
				    _uppercaseTable[j + 2],
				    _uppercaseTable[j + 3],
				    _uppercaseTable[j + 4],
				    _uppercaseTable[j + 5],
				    _uppercaseTable[j + 6],
				    _uppercaseTable[j + 7]];

			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write lowercasePage%u */
	for (of_unichar_t i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (of_unichar_t j = i; j < i + 0x100; j++) {
			if (_lowercaseTable[j] != 0) {
				isEmpty = false;
				_lowercaseTableSize = i >> 8;
				_lowercaseTableUsed[_lowercaseTableSize] = 1;
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const of_unichar_t "
					   @"lowercasePage%u[0x100] = {\n",
					   i >> 8];

			for (of_unichar_t j = i; j < i + 0x100; j += 8)
				[file writeFormat:
				    @"\t%u, %u, %u, %u, %u, %u, %u, %u,\n",
				    _lowercaseTable[j],
				    _lowercaseTable[j + 1],
				    _lowercaseTable[j + 2],
				    _lowercaseTable[j + 3],
				    _lowercaseTable[j + 4],
				    _lowercaseTable[j + 5],
				    _lowercaseTable[j + 6],
				    _lowercaseTable[j + 7]];

			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write titlecasePage%u if it does NOT match uppercasePage%u */
	for (of_unichar_t i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (of_unichar_t j = i; j < i + 0x100; j++) {
			if (_titlecaseTable[j] != 0) {
				isEmpty = !memcmp(_uppercaseTable + i,
				    _titlecaseTable + i,
				    256 * sizeof(of_unichar_t));
				_titlecaseTableSize = i >> 8;
				_titlecaseTableUsed[_titlecaseTableSize] =
				    (isEmpty ? 2 : 1);
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const of_unichar_t "
					   @"titlecasePage%u[0x100] = {\n",
					   i >> 8];

			for (of_unichar_t j = i; j < i + 0x100; j += 8)
				[file writeFormat:
				    @"\t%u, %u, %u, %u, %u, %u, %u, %u,\n",
				    _titlecaseTable[j],
				    _titlecaseTable[j + 1],
				    _titlecaseTable[j + 2],
				    _titlecaseTable[j + 3],
				    _titlecaseTable[j + 4],
				    _titlecaseTable[j + 5],
				    _titlecaseTable[j + 6],
				    _titlecaseTable[j + 7]];

			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write casefoldingPage%u if it does NOT match lowercasePage%u */
	for (of_unichar_t i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (of_unichar_t j = i; j < i + 0x100; j++) {
			if (_casefoldingTable[j] != 0) {
				isEmpty = !memcmp(_lowercaseTable + i,
				    _casefoldingTable + i,
				    256 * sizeof(of_unichar_t));
				_casefoldingTableSize = i >> 8;
				_casefoldingTableUsed[_casefoldingTableSize] =
				    (isEmpty ? 2 : 1);
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const of_unichar_t "
					   @"casefoldingPage%u[0x100] = {\n",
					   i >> 8];

			for (of_unichar_t j = i; j < i + 0x100; j += 8)
				[file writeFormat:
				    @"\t%u, %u, %u, %u, %u, %u, %u, %u,\n",
				    _casefoldingTable[j],
				    _casefoldingTable[j + 1],
				    _casefoldingTable[j + 2],
				    _casefoldingTable[j + 3],
				    _casefoldingTable[j + 4],
				    _casefoldingTable[j + 5],
				    _casefoldingTable[j + 6],
				    _casefoldingTable[j + 7]];

			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write decompositionPage%u */
	for (of_unichar_t i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (of_unichar_t j = i; j < i + 0x100; j++) {
			if (_decompositionTable[j] != nil) {
				isEmpty = false;
				_decompositionTableSize = i >> 8;
				_decompositionTableUsed[
				    _decompositionTableSize] = 1;
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const char *const "
					   @"decompositionPage%u[0x100] = {\n",
					   i >> 8];

			for (of_unichar_t j = i; j < i + 0x100; j++) {
				if ((j - i) % 2 == 0)
					[file writeString: @"\t"];
				else
					[file writeString: @" "];

				if (_decompositionTable[j] != nil) {
					const char *UTF8String =







|

|


|


|














|




|


|











|



|


















|


|











|



|


















|


|



|










|



|

















|
|


|
|

|
|
|
|








|
|


|


|
|
|
|
|
|
|
|








|


|
















|







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
	} 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
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file = [OFFile fileWithPath: path
				       mode: @"w"];

	[file writeString: COPYRIGHT
	    @"#include \"config.h\"\n"
	    @"\n"
	    @"#import \"OFString.h\"\n\n"
	    @"static const OFUnichar emptyPage[0x100] = { 0 };\n"
	    @"static const char *emptyDecompositionPage[0x100] = { NULL };\n"
	    @"\n"];

	/* Write uppercasePage%u */
	for (OFUnichar i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (OFUnichar j = i; j < i + 0x100; j++) {
			if (_uppercaseTable[j] != 0) {
				isEmpty = false;
				_uppercaseTableSize = i >> 8;
				_uppercaseTableUsed[_uppercaseTableSize] = 1;
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const OFUnichar "
					   @"uppercasePage%u[0x100] = {\n",
					   i >> 8];

			for (OFUnichar j = i; j < i + 0x100; j += 8)
				[file writeFormat:
				    @"\t%u, %u, %u, %u, %u, %u, %u, %u,\n",
				    _uppercaseTable[j],
				    _uppercaseTable[j + 1],
				    _uppercaseTable[j + 2],
				    _uppercaseTable[j + 3],
				    _uppercaseTable[j + 4],
				    _uppercaseTable[j + 5],
				    _uppercaseTable[j + 6],
				    _uppercaseTable[j + 7]];

			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write lowercasePage%u */
	for (OFUnichar i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (OFUnichar j = i; j < i + 0x100; j++) {
			if (_lowercaseTable[j] != 0) {
				isEmpty = false;
				_lowercaseTableSize = i >> 8;
				_lowercaseTableUsed[_lowercaseTableSize] = 1;
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const OFUnichar "
					   @"lowercasePage%u[0x100] = {\n",
					   i >> 8];

			for (OFUnichar j = i; j < i + 0x100; j += 8)
				[file writeFormat:
				    @"\t%u, %u, %u, %u, %u, %u, %u, %u,\n",
				    _lowercaseTable[j],
				    _lowercaseTable[j + 1],
				    _lowercaseTable[j + 2],
				    _lowercaseTable[j + 3],
				    _lowercaseTable[j + 4],
				    _lowercaseTable[j + 5],
				    _lowercaseTable[j + 6],
				    _lowercaseTable[j + 7]];

			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write titlecasePage%u if it does NOT match uppercasePage%u */
	for (OFUnichar i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (OFUnichar j = i; j < i + 0x100; j++) {
			if (_titlecaseTable[j] != 0) {
				isEmpty = !memcmp(_uppercaseTable + i,
				    _titlecaseTable + i,
				    256 * sizeof(OFUnichar));
				_titlecaseTableSize = i >> 8;
				_titlecaseTableUsed[_titlecaseTableSize] =
				    (isEmpty ? 2 : 1);
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const OFUnichar "
					   @"titlecasePage%u[0x100] = {\n",
					   i >> 8];

			for (OFUnichar j = i; j < i + 0x100; j += 8)
				[file writeFormat:
				    @"\t%u, %u, %u, %u, %u, %u, %u, %u,\n",
				    _titlecaseTable[j],
				    _titlecaseTable[j + 1],
				    _titlecaseTable[j + 2],
				    _titlecaseTable[j + 3],
				    _titlecaseTable[j + 4],
				    _titlecaseTable[j + 5],
				    _titlecaseTable[j + 6],
				    _titlecaseTable[j + 7]];

			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write caseFoldingPage%u if it does NOT match lowercasePage%u */
	for (OFUnichar i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (OFUnichar j = i; j < i + 0x100; j++) {
			if (_caseFoldingTable[j] != 0) {
				isEmpty = !memcmp(_lowercaseTable + i,
				    _caseFoldingTable + i,
				    256 * sizeof(OFUnichar));
				_caseFoldingTableSize = i >> 8;
				_caseFoldingTableUsed[_caseFoldingTableSize] =
				    (isEmpty ? 2 : 1);
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const OFUnichar "
					   @"caseFoldingPage%u[0x100] = {\n",
					   i >> 8];

			for (OFUnichar j = i; j < i + 0x100; j += 8)
				[file writeFormat:
				    @"\t%u, %u, %u, %u, %u, %u, %u, %u,\n",
				    _caseFoldingTable[j],
				    _caseFoldingTable[j + 1],
				    _caseFoldingTable[j + 2],
				    _caseFoldingTable[j + 3],
				    _caseFoldingTable[j + 4],
				    _caseFoldingTable[j + 5],
				    _caseFoldingTable[j + 6],
				    _caseFoldingTable[j + 7]];

			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write decompositionPage%u */
	for (OFUnichar i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (OFUnichar j = i; j < i + 0x100; j++) {
			if (_decompositionTable[j] != nil) {
				isEmpty = false;
				_decompositionTableSize = i >> 8;
				_decompositionTableUsed[
				    _decompositionTableSize] = 1;
				break;
			}
		}

		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const char *const "
					   @"decompositionPage%u[0x100] = {\n",
					   i >> 8];

			for (OFUnichar j = i; j < i + 0x100; j++) {
				if ((j - i) % 2 == 0)
					[file writeString: @"\t"];
				else
					[file writeString: @" "];

				if (_decompositionTable[j] != nil) {
					const char *UTF8String =
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write decompCompatPage%u if it does NOT match decompositionPage%u */
	for (of_unichar_t i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (of_unichar_t j = i; j < i + 0x100; j++) {
			if (_decompositionCompatTable[j] != 0) {
				/*
				 * We bulk-compare pointers via memcmp here.
				 * This is safe, as we always set the same
				 * pointer in both tables if both are the same.
				 */
				isEmpty = !memcmp(_decompositionTable + i,







|


|







508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
			[file writeString: @"};\n\n"];

			objc_autoreleasePoolPop(pool2);
		}
	}

	/* Write decompCompatPage%u if it does NOT match decompositionPage%u */
	for (OFUnichar i = 0; i < 0x110000; i += 0x100) {
		bool isEmpty = true;

		for (OFUnichar j = i; j < i + 0x100; j++) {
			if (_decompositionCompatTable[j] != 0) {
				/*
				 * We bulk-compare pointers via memcmp here.
				 * This is safe, as we always set the same
				 * pointer in both tables if both are the same.
				 */
				isEmpty = !memcmp(_decompositionTable + i,
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const char *const "
					   @"decompCompatPage%u[0x100] = {\n",
					   i >> 8];

			for (of_unichar_t j = i; j < i + 0x100; j++) {
				if ((j - i) % 2 == 0)
					[file writeString: @"\t"];
				else
					[file writeString: @" "];

				if (_decompositionCompatTable[j] != nil) {
					const char *UTF8String =







|







537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
		if (!isEmpty) {
			void *pool2 = objc_autoreleasePoolPush();

			[file writeFormat: @"static const char *const "
					   @"decompCompatPage%u[0x100] = {\n",
					   i >> 8];

			for (OFUnichar j = i; j < i + 0x100; j++) {
				if ((j - i) % 2 == 0)
					[file writeString: @"\t"];
				else
					[file writeString: @" "];

				if (_decompositionCompatTable[j] != nil) {
					const char *UTF8String =
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
	/*
	 * Those are currently set to the last index.
	 * But from now on, we need the size.
	 */
	_uppercaseTableSize++;
	_lowercaseTableSize++;
	_titlecaseTableSize++;
	_casefoldingTableSize++;
	_decompositionTableSize++;
	_decompositionCompatTableSize++;

	/* Write of_unicode_uppercase_table */
	[file writeFormat: @"const of_unichar_t *const "
			   @"of_unicode_uppercase_table[0x%X] = {\n\t",
			   _uppercaseTableSize];

	for (of_unichar_t i = 0; i < _uppercaseTableSize; i++) {
		if (_uppercaseTableUsed[i])
			[file writeFormat: @"uppercasePage%u", i];
		else
			[file writeString: @"emptyPage"];

		if (i + 1 < _uppercaseTableSize) {
			if ((i + 1) % 4 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write of_unicode_lowercase_table */
	[file writeFormat: @"const of_unichar_t *const "
			   @"of_unicode_lowercase_table[0x%X] = {\n\t",
			   _lowercaseTableSize];

	for (of_unichar_t i = 0; i < _lowercaseTableSize; i++) {
		if (_lowercaseTableUsed[i])
			[file writeFormat: @"lowercasePage%u", i];
		else
			[file writeString: @"emptyPage"];

		if (i + 1 < _lowercaseTableSize) {
			if ((i + 1) % 4 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write of_unicode_titlecase_table */
	[file writeFormat: @"const of_unichar_t *const "
			   @"of_unicode_titlecase_table[0x%X] = {\n\t",
			   _titlecaseTableSize];

	for (of_unichar_t i = 0; i < _titlecaseTableSize; i++) {
		if (_titlecaseTableUsed[i] == 1)
			[file writeFormat: @"titlecasePage%u", i];
		else if (_titlecaseTableUsed[i] == 2)
			[file writeFormat: @"uppercasePage%u", i];
		else
			[file writeString: @"emptyPage"];

		if (i + 1 < _titlecaseTableSize) {
			if ((i + 1) % 4 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write of_unicode_casefolding_table */
	[file writeFormat: @"const of_unichar_t *const "
			   @"of_unicode_casefolding_table[0x%X] = {\n\t",
			   _casefoldingTableSize];

	for (of_unichar_t i = 0; i < _casefoldingTableSize; i++) {
		if (_casefoldingTableUsed[i] == 1)
			[file writeFormat: @"casefoldingPage%u", i];
		else if (_casefoldingTableUsed[i] == 2)
			[file writeFormat: @"lowercasePage%u", i];
		else
			[file writeString: @"emptyPage"];

		if (i + 1 < _casefoldingTableSize) {
			if ((i + 1) % 3 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write of_unicode_decomposition_table */
	[file writeFormat: @"const char *const "
			   @"*of_unicode_decomposition_table[0x%X] = {\n\t",
			   _decompositionTableSize];

	for (of_unichar_t i = 0; i < _decompositionTableSize; i++) {
		if (_decompositionTableUsed[i])
			[file writeFormat: @"decompositionPage%u", i];
		else
			[file writeString: @"emptyDecompositionPage"];

		if (i + 1 < _decompositionTableSize) {
			if ((i + 1) % 3 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write of_unicode_decomposition_compat_table */
	[file writeFormat: @"const char *const "
			   @"*of_unicode_decomposition_compat_table[0x%X] = {"
			   @"\n\t",
			   _decompositionCompatTableSize];

	for (of_unichar_t i = 0; i < _decompositionCompatTableSize; i++) {
		if (_decompositionCompatTableUsed[i] == 1)
			[file writeFormat: @"decompCompatPage%u", i];
		else if (_decompositionCompatTableUsed[i] == 2)
			[file writeFormat: @"decompositionPage%u", i];
		else
			[file writeString: @"emptyDecompositionPage"];








|



|
|
|


|















|
|
|


|















|
|
|


|

















|
|
|
|

|
|
|
|




|









|

|


|















|

|



|







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
	/*
	 * Those are currently set to the last index.
	 * But from now on, we need the size.
	 */
	_uppercaseTableSize++;
	_lowercaseTableSize++;
	_titlecaseTableSize++;
	_caseFoldingTableSize++;
	_decompositionTableSize++;
	_decompositionCompatTableSize++;

	/* Write OFUnicodeUppercaseTable */
	[file writeFormat: @"const OFUnichar *const "
			   @"OFUnicodeUppercaseTable[0x%X] = {\n\t",
			   _uppercaseTableSize];

	for (OFUnichar i = 0; i < _uppercaseTableSize; i++) {
		if (_uppercaseTableUsed[i])
			[file writeFormat: @"uppercasePage%u", i];
		else
			[file writeString: @"emptyPage"];

		if (i + 1 < _uppercaseTableSize) {
			if ((i + 1) % 4 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write OFUnicodeLowercaseTable */
	[file writeFormat: @"const OFUnichar *const "
			   @"OFUnicodeLowercaseTable[0x%X] = {\n\t",
			   _lowercaseTableSize];

	for (OFUnichar i = 0; i < _lowercaseTableSize; i++) {
		if (_lowercaseTableUsed[i])
			[file writeFormat: @"lowercasePage%u", i];
		else
			[file writeString: @"emptyPage"];

		if (i + 1 < _lowercaseTableSize) {
			if ((i + 1) % 4 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write OFUnicodeTitlecaseTable */
	[file writeFormat: @"const OFUnichar *const "
			   @"OFUnicodeTitlecaseTable[0x%X] = {\n\t",
			   _titlecaseTableSize];

	for (OFUnichar i = 0; i < _titlecaseTableSize; i++) {
		if (_titlecaseTableUsed[i] == 1)
			[file writeFormat: @"titlecasePage%u", i];
		else if (_titlecaseTableUsed[i] == 2)
			[file writeFormat: @"uppercasePage%u", i];
		else
			[file writeString: @"emptyPage"];

		if (i + 1 < _titlecaseTableSize) {
			if ((i + 1) % 4 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write OFUnicodeCaseFoldingTable */
	[file writeFormat: @"const OFUnichar *const "
			   @"OFUnicodeCaseFoldingTable[0x%X] = {\n\t",
			   _caseFoldingTableSize];

	for (OFUnichar i = 0; i < _caseFoldingTableSize; i++) {
		if (_caseFoldingTableUsed[i] == 1)
			[file writeFormat: @"caseFoldingPage%u", i];
		else if (_caseFoldingTableUsed[i] == 2)
			[file writeFormat: @"lowercasePage%u", i];
		else
			[file writeString: @"emptyPage"];

		if (i + 1 < _caseFoldingTableSize) {
			if ((i + 1) % 3 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write OFUnicodeDecompositionTable */
	[file writeFormat: @"const char *const "
			   @"*OFUnicodeDecompositionTable[0x%X] = {\n\t",
			   _decompositionTableSize];

	for (OFUnichar i = 0; i < _decompositionTableSize; i++) {
		if (_decompositionTableUsed[i])
			[file writeFormat: @"decompositionPage%u", i];
		else
			[file writeString: @"emptyDecompositionPage"];

		if (i + 1 < _decompositionTableSize) {
			if ((i + 1) % 3 == 0)
				[file writeString: @",\n\t"];
			else
				[file writeString: @", "];
		}
	}

	[file writeString: @"\n};\n\n"];

	/* Write OFUnicodeDecompositionCompatTable */
	[file writeFormat: @"const char *const "
			   @"*OFUnicodeDecompositionCompatTable[0x%X] = {"
			   @"\n\t",
			   _decompositionCompatTableSize];

	for (OFUnichar i = 0; i < _decompositionCompatTableSize; i++) {
		if (_decompositionCompatTableUsed[i] == 1)
			[file writeFormat: @"decompCompatPage%u", i];
		else if (_decompositionCompatTableUsed[i] == 2)
			[file writeFormat: @"decompositionPage%u", i];
		else
			[file writeString: @"emptyDecompositionPage"];

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
	OFFile *file = [OFFile fileWithPath: path
				       mode: @"w"];

	[file writeString: COPYRIGHT
	    @"#import \"OFString.h\"\n\n"];

	[file writeFormat:
	    @"#define OF_UNICODE_UPPERCASE_TABLE_SIZE 0x%X\n"
	    @"#define OF_UNICODE_LOWERCASE_TABLE_SIZE 0x%X\n"
	    @"#define OF_UNICODE_TITLECASE_TABLE_SIZE 0x%X\n"
	    @"#define OF_UNICODE_CASEFOLDING_TABLE_SIZE 0x%X\n"
	    @"#define OF_UNICODE_DECOMPOSITION_TABLE_SIZE 0x%X\n"
	    @"#define OF_UNICODE_DECOMPOSITION_COMPAT_TABLE_SIZE 0x%X\n\n",
	    _uppercaseTableSize, _lowercaseTableSize, _titlecaseTableSize,
	    _casefoldingTableSize, _decompositionTableSize,
	    _decompositionCompatTableSize];

	[file writeString:
	    @"#ifdef __cplusplus\n"
	    @"extern \"C\" {\n"
	    @"#endif\n"
	    @"extern const of_unichar_t *const _Nonnull\n"
	    @"    of_unicode_uppercase_table["
	    @"OF_UNICODE_UPPERCASE_TABLE_SIZE];\n"
	    @"extern const of_unichar_t *const _Nonnull\n"
	    @"    of_unicode_lowercase_table["
	    @"OF_UNICODE_LOWERCASE_TABLE_SIZE];\n"
	    @"extern const of_unichar_t *const _Nonnull\n"
	    @"    of_unicode_titlecase_table["
	    @"OF_UNICODE_TITLECASE_TABLE_SIZE];\n"
	    @"extern const of_unichar_t *const _Nonnull\n"
	    @"    of_unicode_casefolding_table["
	    @"OF_UNICODE_CASEFOLDING_TABLE_SIZE];\n"
	    @"extern const char *const _Nullable *const _Nonnull\n"
	    @"    of_unicode_decomposition_table["
	    @"OF_UNICODE_DECOMPOSITION_TABLE_SIZE];\n"
	    @"extern const char *const _Nullable *const _Nonnull\n"
	    @"    of_unicode_decomposition_compat_table["
	    @"OF_UNICODE_DECOMPOSITION_COMPAT_TABLE_SIZE];\n"

	    @"#ifdef __cplusplus\n"
	    @"}\n"
	    @"#endif\n"];

	objc_autoreleasePoolPop(pool);
}
@end







|
|
|
|
|
|

|






|
|
<
|
|
<
|
|
<
|
|
<

|
|

|
<
>







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
	OFFile *file = [OFFile fileWithPath: path
				       mode: @"w"];

	[file writeString: COPYRIGHT
	    @"#import \"OFString.h\"\n\n"];

	[file writeFormat:
	    @"#define OFUnicodeUppercaseTableSize 0x%X\n"
	    @"#define OFUnicodeLowercaseTableSize 0x%X\n"
	    @"#define OFUnicodeTitlecaseTableSize 0x%X\n"
	    @"#define OFUnicodeCaseFoldingTableSize 0x%X\n"
	    @"#define OFUnicodeDecompositionTableSize 0x%X\n"
	    @"#define OFUnicodeDecompositionCompatTableSize 0x%X\n\n",
	    _uppercaseTableSize, _lowercaseTableSize, _titlecaseTableSize,
	    _caseFoldingTableSize, _decompositionTableSize,
	    _decompositionCompatTableSize];

	[file writeString:
	    @"#ifdef __cplusplus\n"
	    @"extern \"C\" {\n"
	    @"#endif\n"
	    @"extern const OFUnichar *const _Nonnull\n"
	    @"    OFUnicodeUppercaseTable[OFUnicodeUppercaseTableSize];\n"

	    @"extern const OFUnichar *const _Nonnull\n"
	    @"    OFUnicodeLowercaseTable[OFUnicodeLowercaseTableSize];\n"

	    @"extern const OFUnichar *const _Nonnull\n"
	    @"    OFUnicodeTitlecaseTable[OFUnicodeTitlecaseTableSize];\n"

	    @"extern const OFUnichar *const _Nonnull\n"
	    @"    OFUnicodeCaseFoldingTable[OFUnicodeCaseFoldingTableSize];\n"

	    @"extern const char *const _Nullable *const _Nonnull\n"
	    @"    OFUnicodeDecompositionTable["
	    @"OFUnicodeDecompositionTableSize];\n"
	    @"extern const char *const _Nullable *const _Nonnull\n"
	    @"    OFUnicodeDecompositionCompatTable["

	    @"OFUnicodeDecompositionCompatTableSize];\n"
	    @"#ifdef __cplusplus\n"
	    @"}\n"
	    @"#endif\n"];

	objc_autoreleasePoolPop(pool);
}
@end

Renamed and modified generators/copyright.h [5f052aece5] to generators/unicode/copyright.h [c0b22b50fd].

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, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFString.h"

#define COPYRIGHT \
    @"/*\n"								       \
    @" * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, " \
    @"2017,\n"								       \
    @" *               2018, 2019, 2020\n"				       \
    @" *   Jonathan Schleifer <js@nil.im>\n"				       \
    @" *\n"								       \
    @" * All rights reserved.\n"					       \
    @" *\n"								       \
    @" * This file is part of ObjFW. It may be distributed under the terms "   \
    @"of the\n"								       \
    @" * Q Public License 1.0, which can be found in the file LICENSE.QPL "    \
    @"included in\n"							       \

<
<
|

















<
<
<
|







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


 * 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 "OFString.h"

#define COPYRIGHT \
    @"/*\n"								       \



    @" * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>\n"	       \
    @" *\n"								       \
    @" * All rights reserved.\n"					       \
    @" *\n"								       \
    @" * This file is part of ObjFW. It may be distributed under the terms "   \
    @"of the\n"								       \
    @" * Q Public License 1.0, which can be found in the file LICENSE.QPL "    \
    @"included in\n"							       \

Added objfw.spec version [01416894bd].

















































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
%global libobjfw_major 0
%global libobjfw_minor 0
%global libobjfwrt_major 0
%global libobjfwrt_minor 0
%global libobjfwtls_major 0
%global libobjfwtls_minor 0
%if 0%{?suse_version}
%global libobjfw_pkgname libobjfw%{libobjfw_major}
%global libobjfwrt_pkgname libobjfwrt%{libobjfwrt_major}
%global libobjfwtls_pkgname libobjfwtls%{libobjfwtls_major}
%else
%global libobjfw_pkgname libobjfw
%global libobjfwrt_pkgname libobjfwrt
%global libobjfwtls_pkgname libobjfwtls
%endif

Name:          objfw
Version:       1.1dev
Release:       1%{?dist}
Summary:       Portable, lightweight framework for the Objective-C language

%if 0%{?suse_version}
License:       QPL-1.0 or GPL-3.0 or GPL-2.0
Group:         Development/Languages/C and C++
%else
License:       QPL or GPLv3 or GPLv2
%endif
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}
Requires:      ofhttp%{_isa} = %{version}-%{release}

%description
ObjFW is a portable, lightweight framework for the Objective-C language. It
enables you to write an application in Objective-C that will run on any
platform supported by ObjFW without having to worry about differences between
operating systems or various frameworks you would otherwise need if you want to
be portable.

It supports all modern Objective-C features when using Clang, but is also
compatible with GCC ≥ 4.6 to allow maximum portability.

ObjFW also comes with its own lightweight and extremely fast Objective-C
runtime, which in real world use cases was found to be significantly faster
than both GNU's and Apple's runtime.

%package -n %{libobjfw_pkgname}
Summary:       ObjFW library
Requires:      %{libobjfwrt_pkgname}%{_isa} = %{version}-%{release}

%description -n %{libobjfw_pkgname}
The %{libobjfw_pkgname} package contains the library needed by programs using
ObjFW.

%package -n %{libobjfw_pkgname}-devel
Summary:       Header files, libraries and tools for %{libobjfw_pkgname}
Requires:      %{libobjfw_pkgname}%{_isa} = %{version}-%{release}
Requires:      %{libobjfwrt_pkgname}-devel = %{version}-%{release}

%description -n %{libobjfw_pkgname}-devel
The %{libobjfw_pkgname}-devel package contains the header files, libraries and
tools to develop programs using ObjFW.

%package -n %{libobjfwrt_pkgname}
Summary:       ObjFW Objective-C runtime library

%description -n %{libobjfwrt_pkgname}
The %{libobjfwrt_pkgname} package contains ObjFW's Objective-C runtime library.

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

%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}

%description -n %{libobjfwtls_pkgname}-devel
The %{libobjfwtls_pkgname}-devel package contains header files and libraries
for TLS support for ObjFW.

%package -n ofarc
Summary:       Utility for handling ZIP, Tar and LHA archives
Requires:      %{libobjfw_pkgname}%{_isa} = %{version}-%{release}
Requires:      %{libobjfwrt_pkgname}%{_isa} = %{version}-%{release}

%description -n ofarc
ofarc is a multi-format archive utility that allows creating, listing,
extracting and modifying ZIP, Tar and LHA archives using ObjFW's classes for
various archive types.

%package -n ofdns
Summary:       Utility for performing DNS requests on the command line
Requires:      %{libobjfw_pkgname}%{_isa} = %{version}-%{release}
Requires:      %{libobjfwrt_pkgname}%{_isa} = %{version}-%{release}

%description -n ofdns
ofdns is an utility for performing DNS requests on the command line using
ObjFW's DNS resolver.

%package -n ofhash
Summary:       Utility to hash files with various cryptographic hash functions
Requires:      %{libobjfw_pkgname}%{_isa} = %{version}-%{release}
Requires:      %{libobjfwrt_pkgname}%{_isa} = %{version}-%{release}

%description -n ofhash
ofhash is an utility to hash files with various cryptographic hash functions
(even using different algorithms at once) using ObjFW's classes for various
cryptographic hashes.

%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
%make_build

%install
%make_install

%check
make -C tests run

%if 0%{?suse_version}
%post -n %{libobjfw_pkgname} -p /sbin/ldconfig
%postun -n %{libobjfw_pkgname} -p /sbin/ldconfig
%post -n %{libobjfwrt_pkgname} -p /sbin/ldconfig
%postun -n %{libobjfwrt_pkgname} -p /sbin/ldconfig
%endif

%files
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n %{libobjfw_pkgname}
%{_libdir}/libobjfw.so.%{libobjfw_major}
%{_libdir}/libobjfw.so.%{libobjfw_major}.%{libobjfw_minor}.0
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n %{libobjfw_pkgname}-devel
%{_libdir}/libobjfw.so
%dir %{_includedir}/ObjFW
%{_includedir}/ObjFW
%{_bindir}/objfw-compile
%{_bindir}/objfw-config
%{_bindir}/objfw-new
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n %{libobjfwrt_pkgname}
%{_libdir}/libobjfwrt.so.%{libobjfwrt_major}
%{_libdir}/libobjfwrt.so.%{libobjfwrt_major}.%{libobjfwrt_minor}.0
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n %{libobjfwrt_pkgname}-devel
%{_libdir}/libobjfwrt.so
%{_includedir}/ObjFWRT/ObjFWRT.h
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n %{libobjfwtls_pkgname}
%{_libdir}/libobjfwtls.so.%{libobjfwtls_major}
%{_libdir}/libobjfwtls.so.%{libobjfwtls_major}.%{libobjfwtls_minor}.0
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n %{libobjfwtls_pkgname}-devel
%{_libdir}/libobjfwtls.so
%{_includedir}/ObjFWTLS/ObjFWTLS.h
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n ofarc
%{_bindir}/ofarc
%{_datadir}/ofarc/lang/de.json
%{_datadir}/ofarc/lang/languages.json
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n ofdns
%{_bindir}/ofdns
%{_datadir}/ofdns/lang/de.json
%{_datadir}/ofdns/lang/languages.json
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n ofhash
%{_bindir}/ofhash
%{_datadir}/ofhash/lang/de.json
%{_datadir}/ofhash/lang/languages.json
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

%files -n ofhttp
%{_bindir}/ofhttp
%{_datadir}/ofhttp/lang/de.json
%{_datadir}/ofhttp/lang/languages.json
%license LICENSE.QPL
%license LICENSE.GPLv3
%license LICENSE.GPLv2

Modified src/Makefile from [da1634a438] to [6b94befb31].

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
include ../extra.mk

SUBDIRS = ${RUNTIME} exceptions ${ENCODINGS} forwarding invocation
SUBDIRS_AFTER = ${BRIDGE}
DISTCLEAN = Info.plist objfw-defs.h

SHARED_LIB = ${OBJFW_SHARED_LIB}
STATIC_LIB = ${OBJFW_STATIC_LIB}
FRAMEWORK = ${OBJFW_FRAMEWORK}
LIB_MAJOR = ${OBJFW_LIB_MAJOR}
LIB_MINOR = ${OBJFW_LIB_MINOR}

SRCS = OFASN1BitString.m		\
       OFASN1Boolean.m			\
       OFASN1Enumerated.m		\
       OFASN1IA5String.m		\
       OFASN1Integer.m			\
       OFASN1NumericString.m		\
       OFASN1ObjectIdentifier.m		\
       OFASN1OctetString.m		\
       OFASN1PrintableString.m		\
       OFASN1UTF8String.m		\
       OFASN1Value.m			\
       OFApplication.m			\
       OFArray.m			\
       OFBlock.m			\
       OFCharacterSet.m			\
       OFColor.m			\
       OFConstantString.m		\
       OFCountedSet.m			\
       OFData.m				\
       OFData+ASN1DERParsing.m		\
       OFData+CryptoHashing.m		\
       OFData+MessagePackParsing.m	\
       OFDate.m				\
       OFDictionary.m			\
       OFEnumerator.m			\
       OFFileManager.m			\
       OFGZIPStream.m			\
       OFHMAC.m				\
       OFInflate64Stream.m		\
       OFInflateStream.m		\
       OFInvocation.m			\
       OFLHAArchive.m			\
       OFLHAArchiveEntry.m		\
       OFList.m				\
       OFLocale.m			\
       OFMapTable.m			\
       OFMD5Hash.m			\

       OFMessagePackExtension.m		\
       OFMethodSignature.m		\
       OFMutableArray.m			\
       OFMutableData.m			\
       OFMutableDictionary.m		\
       OFMutableLHAArchiveEntry.m	\
       OFMutablePair.m			\
       OFMutableSet.m			\
       OFMutableString.m		\
       OFMutableTarArchiveEntry.m	\
       OFMutableTriple.m		\
       OFMutableURL.m			\
       OFMutableZIPArchiveEntry.m	\


       OFNull.m				\
       OFNumber.m			\
       OFObject.m			\
       OFObject+KeyValueCoding.m	\
       OFObject+Serialization.m		\

       OFOptionsParser.m		\

       OFPair.m				\
       ${OF_PROCESS_M}			\
       OFRIPEMD160Hash.m		\
       OFRunLoop.m			\
       OFSandbox.m			\
       OFSecureData.m			\
       OFSeekableStream.m		\
       OFSet.m				\
       OFSHA1Hash.m			\
       OFSHA224Hash.m			\
       OFSHA224Or256Hash.m		\
       OFSHA256Hash.m			\
       OFSHA384Hash.m			\
       OFSHA384Or512Hash.m		\
       OFSHA512Hash.m			\





       OFSortedList.m			\
       OFStdIOStream.m			\
       OFStream.m			\
       OFString.m			\
       OFString+CryptoHashing.m		\
       OFString+JSONParsing.m		\
       OFString+PropertyListParsing.m	\
       OFString+Serialization.m		\
       OFString+URLEncoding.m		\
       OFString+XMLEscaping.m		\
       OFString+XMLUnescaping.m		\

       OFSystemInfo.m			\
       OFTarArchive.m			\
       OFTarArchiveEntry.m		\
       OFThread.m			\
       OFTimer.m			\
       OFTriple.m			\
       OFURL.m				\
       OFURLHandler.m			\

       OFValue.m			\
       OFXMLAttribute.m			\
       OFXMLCDATA.m			\
       OFXMLCharacters.m		\
       OFXMLComment.m			\
       OFXMLElement.m			\
       OFXMLElement+Serialization.m	\
       OFXMLElementBuilder.m		\
       OFXMLNode.m			\
       OFXMLParser.m			\
       OFXMLProcessingInstructions.m	\
       OFZIPArchive.m			\
       OFZIPArchiveEntry.m		\
       base64.m				\
       crc16.m				\
       crc32.m				\
       huffman_tree.m			\
       of_asprintf.m			\
       of_strptime.m			\
       once.m				\
       pbkdf2.m				\
       scrypt.m				\
       ${UNICODE_M}			\
       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_THREADS}		\
       ${USE_SRCS_WINDOWS}
SRCS_FILES = OFFile.m			\
	     OFINICategory.m		\
	     OFINIFile.m		\
	     OFSettings.m		\
	     OFString+PathAdditions.m
SRCS_IPX = OFIPXSocket.m	\
	   OFSPXSocket.m	\
	   OFSPXStreamSocket.m
SRCS_PLUGINS = OFPlugin.m
SRCS_SCTP = OFSCTPSocket.m
SRCS_SOCKETS = OFDNSQuery.m			\
	       OFDNSResolver.m			\
	       OFDNSResourceRecord.m		\
	       OFDNSResponse.m			\
	       OFDatagramSocket.m		\
	       OFHTTPClient.m			\
	       OFHTTPCookie.m			\
	       OFHTTPCookieManager.m		\
	       OFHTTPRequest.m			\
	       OFHTTPResponse.m			\
	       OFHTTPServer.m			\
	       OFSequencedPacketSocket.m	\

	       OFStreamSocket.m			\
	       OFTCPSocket.m			\

	       OFUDPSocket.m			\
	       socket.m				\
	       ${USE_SRCS_IPX}			\
	       ${USE_SRCS_SCTP}





SRCS_THREADS = OFCondition.m		\
	       OFMutex.m		\
	       OFRecursiveMutex.m	\
	       OFThreadPool.m		\
	       condition.m		\
	       mutex.m			\
	       thread.m			\

	       tlskey.m
SRCS_WINDOWS = OFWin32ConsoleStdIOStream.m	\
	       OFWindowsRegistryKey.m

INCLUDES_ATOMIC = atomic.h			\
		  atomic_builtins.h		\
		  atomic_no_threads.h		\
		  atomic_osatomic.h		\
		  atomic_powerpc.h		\
		  atomic_sync_builtins.h	\
		  atomic_x86.h
INCLUDES := ${SRCS:.m=.h}			\
	    OFASN1DERRepresentation.h		\
	    OFCollection.h			\
	    OFCryptoHash.h			\
	    OFJSONRepresentation.h		\
	    OFKernelEventObserver.h		\
	    OFKeyValueCoding.h			\
	    OFLocking.h				\
	    OFMessagePackRepresentation.h	\
	    OFSerialization.h			\
	    OFTLSSocket.h			\
	    ObjFW.h				\
	    block.h				\
	    macros.h				\
	    objfw-defs.h			\
	    platform.h				\
	    ${USE_INCLUDES_ATOMIC}


SRCS += OFAdjacentArray.m		\
	OFAdjacentSubarray.m		\

	OFBitSetCharacterSet.m		\
	OFBytesValue.m			\


	OFCountedMapTableSet.m		\
	OFDimensionValue.m		\
	OFInvertedCharacterSet.m	\
	OFLHADecompressingStream.m	\
	OFMapTableDictionary.m		\
	OFMapTableSet.m			\
	OFMutableAdjacentArray.m	\
	OFMutableMapTableDictionary.m	\
	OFMutableMapTableSet.m		\
	OFMutableUTF8String.m		\
	OFNonretainedObjectValue.m	\
	OFPointValue.m			\
	OFPointerValue.m		\
	OFRangeCharacterSet.m		\
	OFRangeValue.m			\


	OFRectangleValue.m		\

	OFSubarray.m			\
	OFUTF8String.m			\
	${LIBBASES_M}			\
	${RUNTIME_AUTORELEASE_M}	\
	${RUNTIME_INSTANCE_M}

SRCS_FILES += OFFileURLHandler.m	\
	      OFINIFileSettings.m
SRCS_SOCKETS += OFDNSResolverSettings.m			\

		OFHTTPURLHandler.m			\
		OFHostAddressResolver.m			\
		OFIPSocketAsyncConnector.m		\
		OFKernelEventObserver.m			\
		${OF_EPOLL_KERNEL_EVENT_OBSERVER_M}	\
		${OF_KQUEUE_KERNEL_EVENT_OBSERVER_M}	\
		${OF_POLL_KERNEL_EVENT_OBSERVER_M}	\
		${OF_SELECT_KERNEL_EVENT_OBSERVER_M}	\
		OFTCPSocketSOCKS5Connector.m

OBJS_EXTRA = ${RUNTIME_RUNTIME_A}	\
	     ${EXCEPTIONS_EXCEPTIONS_A} \
	     ${ENCODINGS_ENCODINGS_A}	\
	     ${FORWARDING_FORWARDING_A}	\
	     ${INVOCATION_INVOCATION_A}


LIB_OBJS_EXTRA = ${RUNTIME_RUNTIME_LIB_A}	\
		 ${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}


|
|








<
<
<
<
<
<
<
<
<
<
<
|







<
|














<

>













>
>





>

>

<


<
<
<
<







>
>
>
>
>




|






>








>










|


<
<
<
<
<
<
<
<
<
<










<
<
<

<












>


>

<

|
>
>
>
>
>


<
<
|
|
|
>
|



|
|
<
|
|
|
|

<

|





<
<

<





>
|

>


>
>

|













>
>
|
>




|
>



>




<





|
<
<
<
<
>
>
|
<
<
<
<
>
>










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
include ../extra.mk

SUBDIRS = ${RUNTIME} exceptions encodings forwarding
SUBDIRS_AFTER = ${BRIDGE} ${TLS}
DISTCLEAN = Info.plist objfw-defs.h

SHARED_LIB = ${OBJFW_SHARED_LIB}
STATIC_LIB = ${OBJFW_STATIC_LIB}
FRAMEWORK = ${OBJFW_FRAMEWORK}
LIB_MAJOR = ${OBJFW_LIB_MAJOR}
LIB_MINOR = ${OBJFW_LIB_MINOR}












SRCS = OFApplication.m			\
       OFArray.m			\
       OFBlock.m			\
       OFCharacterSet.m			\
       OFColor.m			\
       OFConstantString.m		\
       OFCountedSet.m			\
       OFData.m				\

       OFData+CryptographicHashing.m	\
       OFData+MessagePackParsing.m	\
       OFDate.m				\
       OFDictionary.m			\
       OFEnumerator.m			\
       OFFileManager.m			\
       OFGZIPStream.m			\
       OFHMAC.m				\
       OFInflate64Stream.m		\
       OFInflateStream.m		\
       OFInvocation.m			\
       OFLHAArchive.m			\
       OFLHAArchiveEntry.m		\
       OFList.m				\
       OFLocale.m			\

       OFMD5Hash.m			\
       OFMapTable.m			\
       OFMessagePackExtension.m		\
       OFMethodSignature.m		\
       OFMutableArray.m			\
       OFMutableData.m			\
       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	\
       OFObject+Serialization.m		\
       OFOnce.m				\
       OFOptionsParser.m		\
       OFPBKDF2.m			\
       OFPair.m				\

       OFRIPEMD160Hash.m		\
       OFRunLoop.m			\




       OFSHA1Hash.m			\
       OFSHA224Hash.m			\
       OFSHA224Or256Hash.m		\
       OFSHA256Hash.m			\
       OFSHA384Hash.m			\
       OFSHA384Or512Hash.m		\
       OFSHA512Hash.m			\
       OFScrypt.m			\
       OFSecureData.m			\
       OFSeekableStream.m		\
       OFSerialization.m		\
       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}		\
       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			\
       OFXMLElement+Serialization.m	\
       OFXMLElementBuilder.m		\
       OFXMLNode.m			\
       OFXMLParser.m			\
       OFXMLProcessingInstruction.m	\
       OFZIPArchive.m			\
       OFZIPArchiveEntry.m		\










       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_THREADS}		\
       ${USE_SRCS_WINDOWS}
SRCS_FILES = OFFile.m			\
	     OFINICategory.m		\
	     OFINIFile.m		\
	     OFSettings.m		\
	     OFString+PathAdditions.m



SRCS_PLUGINS = OFPlugin.m

SRCS_SOCKETS = OFDNSQuery.m			\
	       OFDNSResolver.m			\
	       OFDNSResourceRecord.m		\
	       OFDNSResponse.m			\
	       OFDatagramSocket.m		\
	       OFHTTPClient.m			\
	       OFHTTPCookie.m			\
	       OFHTTPCookieManager.m		\
	       OFHTTPRequest.m			\
	       OFHTTPResponse.m			\
	       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		\
	OFMutableUTF8String.m		\
	OFNonretainedObjectValue.m	\
	OFPointValue.m			\
	OFPointerValue.m		\
	OFRangeCharacterSet.m		\
	OFRangeValue.m			\
	OFRectValue.m			\
	OFSandbox.m			\
	OFSizeValue.m			\
	OFStrPTime.m			\
	OFSubarray.m			\
	OFUTF8String.m			\
	${LIBBASES_M}			\
	${RUNTIME_AUTORELEASE_M}	\
	${RUNTIME_INSTANCE_M}		\
       ${UNICODE_M}
SRCS_FILES += OFFileURLHandler.m	\
	      OFINIFileSettings.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
LIB_OBJS_EXTRA = exceptions/exceptions.lib.a	\




		 encodings/encodings.lib.a	\
		 forwarding/forwarding.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}

Deleted src/OFASN1BitString.h version [dd9e3e424b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1DERRepresentation.h"
#import "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

@class OFData;

/**
 * @brief An ASN.1 BitString.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1BitString: OFObject <OFASN1DERRepresentation>
{
	OFData *_bitStringValue;
	size_t _bitStringLength;
}

/**
 * @brief The BitString value.
 */
@property (readonly, nonatomic) OFData *bitStringValue;

/**
 * @brief The length of the BitString in bits.
 */
@property (readonly, nonatomic) size_t bitStringLength;

/**
 * @brief Creates an ASN.1 BitString with the specified BitString value and
 *	  length.
 *
 * @param bitStringValue The value of the BitString
 * @param bitStringLength The length of the BitString in bits
 * @return A new, autoreleased OFASN1BitString
 */
+ (instancetype)bitStringWithBitStringValue: (OFData *)bitStringValue
			    bitStringLength: (size_t)bitStringLength;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated ASN.1 BitString with the specified
 *	  BitString value and length.
 *
 * @param bitStringValue The value of the BitString
 * @param bitStringLength The length of the BitString in bits
 * @return An initialized OFASN1BitString
 */
- (instancetype)initWithBitStringValue: (OFData *)bitStringValue
		       bitStringLength: (size_t)bitStringLength
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 BitString with the specified
 *	  arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized OFASN1BitString
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































































































































































Deleted src/OFASN1BitString.m version [58adebf4a8].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1BitString.h"
#import "OFData.h"
#import "OFString.h"

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

@implementation OFASN1BitString
@synthesize bitStringValue = _bitStringValue;
@synthesize bitStringLength = _bitStringLength;

+ (instancetype)bitStringWithBitStringValue: (OFData *)bitStringValue
			    bitStringLength: (size_t)bitStringLength
{
	return [[[self alloc]
	    initWithBitStringValue: bitStringValue
		   bitStringLength: bitStringLength] autorelease];
}

- (instancetype)initWithBitStringValue: (OFData *)bitStringValue
		       bitStringLength: (size_t)bitStringLength
{
	self = [super init];

	@try {
		if (bitStringValue.count * bitStringValue.itemSize !=
		    OF_ROUND_UP_POW2(8, bitStringLength) / 8)
			@throw [OFInvalidFormatException exception];

		_bitStringValue = [bitStringValue copy];
		_bitStringLength = bitStringLength;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	void *pool = objc_autoreleasePoolPush();
	OFData *bitStringValue;
	size_t bitStringLength;

	@try {
		unsigned char unusedBits;
		size_t count = DEREncodedContents.count;

		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_BIT_STRING || constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1 || count == 0)
			@throw [OFInvalidFormatException exception];

		unusedBits =
		    *(unsigned char *)[DEREncodedContents itemAtIndex: 0];

		if (unusedBits > 7)
			@throw [OFInvalidFormatException exception];

		/*
		 * Can't have any bits of the last byte unused if we have no
		 * byte.
		 */
		if (count == 1 && unusedBits != 0)
			@throw [OFInvalidFormatException exception];

		if (SIZE_MAX / 8 < count - 1)
			@throw [OFOutOfRangeException exception];

		bitStringLength = (count - 1) * 8;
		bitStringValue = [DEREncodedContents subdataWithRange:
		    of_range(1, count - 1)];

		if (unusedBits != 0)
			bitStringLength -= unusedBits;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithBitStringValue: bitStringValue
			    bitStringLength: bitStringLength];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_bitStringValue release];

	[super dealloc];
}

- (OFData *)ASN1DERRepresentation
{
	size_t bitStringValueCount = [_bitStringValue count];
	size_t roundedUpLength = OF_ROUND_UP_POW2(8, _bitStringLength);
	unsigned char unusedBits = roundedUpLength - _bitStringLength;
	unsigned char header[] = {
		OF_ASN1_TAG_NUMBER_BIT_STRING,
		bitStringValueCount + 1,
		unusedBits
	};
	OFMutableData *data;

	if (bitStringValueCount + 1 > UINT8_MAX ||
	    bitStringValueCount != roundedUpLength / 8)
		@throw [OFInvalidFormatException exception];

	data = [OFMutableData
	    dataWithCapacity: sizeof(header) + bitStringValueCount];
	[data addItems: header
		 count: sizeof(header)];
	[data addItems: [_bitStringValue items]
		 count: bitStringValueCount];

	[data makeImmutable];

	return data;
}

- (bool)isEqual: (id)object
{
	OFASN1BitString *bitString;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1BitString class]])
		return false;

	bitString = object;

	if (![bitString->_bitStringValue isEqual: _bitStringValue])
		return false;
	if (bitString->_bitStringLength != _bitStringLength)
		return false;

	return true;
}

- (unsigned long)hash
{
	return _bitStringValue.hash + (unsigned long)_bitStringLength;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<OFASN1BitString: %@ (%zu bits)>",
					   _bitStringValue, _bitStringLength];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































Deleted src/OFASN1Boolean.h version [f9c4678a9e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1DERRepresentation.h"
#import "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @brief An ASN.1 Boolean.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1Boolean: OFObject <OFASN1DERRepresentation>
{
	bool _booleanValue;
}

/**
 * @brief The Boolean value.
 */
@property (readonly, nonatomic) bool booleanValue;

/**
 * @brief Creates an ASN.1 Boolean with the specified Boolean value.
 *
 * @param booleanValue The value of the Boolean
 * @return A new, autoreleased OFASN1Boolean
 */
+ (instancetype)booleanWithBooleanValue: (bool)booleanValue;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated ASN.1 Boolean with the specified
 *	  Boolean value.
 *
 * @param booleanValue The value of the Boolean
 * @return An initialized OFASN1Boolean
 */
- (instancetype)initWithBooleanValue: (bool)booleanValue
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 Boolean with the specified
 *	  arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized OFASN1Boolean
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































Deleted src/OFASN1Boolean.m version [ab43e337d1].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1Boolean.h"
#import "OFData.h"
#import "OFString.h"

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

@implementation OFASN1Boolean
@synthesize booleanValue = _booleanValue;

+ (instancetype)booleanWithBooleanValue: (bool)booleanValue
{
	return [[[self alloc] initWithBooleanValue: booleanValue] autorelease];
}

- (instancetype)initWithBooleanValue: (bool)booleanValue
{
	self = [super init];

	_booleanValue = booleanValue;

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	unsigned char value;

	@try {
		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_BOOLEAN || constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1 ||
		    DEREncodedContents.count != 1)
			@throw [OFInvalidFormatException exception];

		value = *(unsigned char *)[DEREncodedContents itemAtIndex: 0];

		if (value != 0 && value != 0xFF)
			@throw [OFInvalidFormatException exception];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return [self initWithBooleanValue: !!value];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (OFData *)ASN1DERRepresentation
{
	char buffer[] = {
		OF_ASN1_TAG_NUMBER_BOOLEAN,
		1,
		(_booleanValue ? 0xFF : 0x00)
	};

	return [OFData dataWithItems: buffer
			       count: sizeof(buffer)];
}

- (bool)isEqual: (id)object
{
	OFASN1Boolean *boolean;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1Boolean class]])
		return false;

	boolean = object;

	if (boolean->_booleanValue != _booleanValue)
		return false;

	return true;
}

- (unsigned long)hash
{
	return _booleanValue;
}

- (OFString *)description
{
	return (_booleanValue
	    ? @"<OFASN1Boolean: true>"
	    : @"<OFASN1Boolean: false>");
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































































































































































































Deleted src/OFASN1DERRepresentation.h version [878337daf1].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

OF_ASSUME_NONNULL_BEGIN

@class OFData;

/**
 * @protocol OFASN1DERRepresentation \
 *	     OFASN1DERRepresentation.h ObjFW/OFASN1DERRepresentation.h
 *
 * @brief A protocol implemented by classes that support encoding to ASN.1 DER
 *	  representation.
 */
@protocol OFASN1DERRepresentation
/**
 * @brief The object in ASN.1 DER representation.
 */
@property (readonly, nonatomic) OFData *ASN1DERRepresentation;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































Deleted src/OFASN1Enumerated.h version [5baa4a294f].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @brief An ASN.1 Enumerated.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1Enumerated: OFObject
{
	long long _longLongValue;
}

/**
 * @brief The integer value.
 */
@property (readonly, nonatomic) long long longLongValue;

/**
 * @brief Creates an ASN.1 Enumerated with the specified integer value.
 *
 * @param value The `long long` value of the Enumerated
 * @return A new, autoreleased OFASN1Enumerated
 */
+ (instancetype)enumeratedWithLongLong: (long long)value;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated ASN.1 Enumerated with the specified
 *	  integer value.
 *
 * @param value The `long long` value of the Enumerated
 * @return An initialized OFASN1Enumerated
 */
- (instancetype)initWithLongLong: (long long)value OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 Enumerated with the specified
 *	  arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized OFASN1Enumerated
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































Deleted src/OFASN1Enumerated.m version [ba2cabe5ff].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1Enumerated.h"
#import "OFData.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"

extern long long of_asn1_der_integer_parse(const unsigned char *buffer,
    size_t length);

@implementation OFASN1Enumerated
@synthesize longLongValue = _longLongValue;

+ (instancetype)enumeratedWithLongLong: (long long)value
{
	return [[[self alloc] initWithLongLong: value] autorelease];
}

- (instancetype)initWithLongLong: (long long)value
{
	self = [super init];

	_longLongValue = value;

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	long long value;

	@try {
		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_ENUMERATED || constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1)
			@throw [OFInvalidArgumentException exception];

		value = of_asn1_der_integer_parse(
		    DEREncodedContents.items, DEREncodedContents.count);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return [self initWithLongLong: value];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (bool)isEqual: (id)object
{
	OFASN1Enumerated *enumerated;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1Enumerated class]])
		return false;

	enumerated = object;

	if (enumerated->_longLongValue != _longLongValue)
		return false;

	return true;
}

- (unsigned long)hash
{
	return (unsigned long)_longLongValue;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<OFASN1Enumerated: %lld>",
					   _longLongValue];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































































































Deleted src/OFASN1IA5String.h version [c414c3313c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;

/**
 * @brief An ASN.1 IA5String.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1IA5String: OFObject
{
	OFString *_IA5StringValue;
}

/**
 * @brief The IA5String value.
 */
@property (readonly, nonatomic) OFString *IA5StringValue;

/**
 * @brief The string value.
 */
@property (readonly, nonatomic) OFString *stringValue;

/**
 * @brief Creates an IA5String with the specified string value.
 *
 * @param stringValue The string value of the IA5String
 * @return A new, autoreleased OFASN1IA5String
 */
+ (instancetype)stringWithStringValue: (OFString *)stringValue;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated IA5String with the specified string
 *	  value.
 *
 * @param stringValue The string value of the IA5String
 * @return An initialized OFASN1IA5String
 */
- (instancetype)initWithStringValue: (OFString *)stringValue
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 IA5String with the specified
 *	  arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized OFASN1IA5String
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































Deleted src/OFASN1IA5String.m version [6816147795].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1IA5String.h"
#import "OFData.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"

@implementation OFASN1IA5String
@synthesize IA5StringValue = _IA5StringValue;

+ (instancetype)stringWithStringValue: (OFString *)stringValue
{
	return [[[self alloc] initWithStringValue: stringValue] autorelease];
}

- (instancetype)initWithStringValue: (OFString *)stringValue
{
	self = [super init];

	@try {
		_IA5StringValue = [stringValue copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	void *pool = objc_autoreleasePoolPush();
	OFString *IA5StringValue;

	@try {
		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_IA5_STRING || constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1)
			@throw [OFInvalidArgumentException exception];

		IA5StringValue = [OFString
		    stringWithCString: DEREncodedContents.items
			     encoding: OF_STRING_ENCODING_ASCII
			       length: DEREncodedContents.count];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithStringValue: IA5StringValue];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_IA5StringValue release];

	[super dealloc];
}

- (OFString *)stringValue
{
	return self.IA5StringValue;
}

- (bool)isEqual: (id)object
{
	OFASN1IA5String *IA5String;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1IA5String class]])
		return false;

	IA5String = object;

	if (![IA5String->_IA5StringValue isEqual: _IA5StringValue])
		return false;

	return true;
}

- (unsigned long)hash
{
	return _IA5StringValue.hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<OFASN1IA5String: %@>",
					   _IA5StringValue];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































Deleted src/OFASN1Integer.h version [a73f7cf242].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @brief An ASN.1 Integer.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1Integer: OFObject
{
	long long _longLongValue;
}

/**
 * @brief The Integer value.
 */
@property (readonly, nonatomic) long long longLongValue;

/**
 * @brief Creates an ASN.1 Integer with the specified integer value.
 *
 * @param value The `long long` value of the Integer
 * @return A new, autoreleased OFASN1Integer
 */
+ (instancetype)integerWithLongLong: (long long)value;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated ASN.1 Integer with the specified
 *	  integer value.
 *
 * @param value The `long long` value of the Integer
 * @return An initialized OFASN1Integer
 */
- (instancetype)initWithLongLong: (long long)value OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 Integer with the specified
 *	  arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized OFASN1Integer
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































Deleted src/OFASN1Integer.m version [e6486b0fce].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1Integer.h"
#import "OFData.h"
#import "OFString.h"

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

long long
of_asn1_der_integer_parse(const unsigned char *buffer, size_t length)
{
	unsigned long long value = 0;

	/* TODO: Support for big numbers */
	if (length > sizeof(unsigned long long) &&
	    (length != sizeof(unsigned long long) + 1 || buffer[0] != 0))
		@throw [OFOutOfRangeException exception];

	if (length >= 2 && ((buffer[0] == 0 && !(buffer[1] & 0x80)) ||
	    (buffer[0] == 0xFF && buffer[1] & 0x80)))
		@throw [OFInvalidFormatException exception];

	if (length >= 1 && buffer[0] & 0x80)
		value = ~0ull;

	while (length--)
		value = (value << 8) | *buffer++;

	return value;
}

@implementation OFASN1Integer
@synthesize longLongValue = _longLongValue;

+ (instancetype)integerWithLongLong: (long long)value
{
	return [[[self alloc] initWithLongLong: value] autorelease];
}

- (instancetype)initWithLongLong: (long long)value
{
	self = [super init];

	_longLongValue = value;

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	long long value;

	@try {
		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_INTEGER || constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1)
			@throw [OFInvalidArgumentException exception];

		value = of_asn1_der_integer_parse(
		    DEREncodedContents.items, DEREncodedContents.count);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return [self initWithLongLong: value];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (bool)isEqual: (id)object
{
	OFASN1Integer *integer;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1Integer class]])
		return false;

	integer = object;

	if (integer->_longLongValue != _longLongValue)
		return false;

	return true;
}

- (unsigned long)hash
{
	return (unsigned long)_longLongValue;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<OFASN1Integer: %lld>",
					   _longLongValue];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































Deleted src/OFASN1NumericString.h version [3a5c85e7cd].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;

/**
 * @brief An ASN.1 NumericString.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1NumericString: OFObject
{
	OFString *_numericStringValue;
}

/**
 * @brief The NumericString value.
 */
@property (readonly, nonatomic) OFString *numericStringValue;

/**
 * @brief The string value.
 */
@property (readonly, nonatomic) OFString *stringValue;

/**
 * @brief Creates an NumericString with the specified string value.
 *
 * @param stringValue The string value of the NumericString
 * @return A new, autoreleased OFASN1NumericString
 */
+ (instancetype)stringWithStringValue: (OFString *)stringValue;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated NumericString with the specified
 *	  string value.
 *
 * @param stringValue The string value of the NumericString
 * @return An initialized OFASN1NumericString
 */
- (instancetype)initWithStringValue: (OFString *)stringValue
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 NumericString with the
 *	  specified arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized ASN.1 NumericString
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































Deleted src/OFASN1NumericString.m version [1f3277f085].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1NumericString.h"
#import "OFData.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"

@implementation OFASN1NumericString
@synthesize numericStringValue = _numericStringValue;

+ (instancetype)stringWithStringValue: (OFString *)stringValue
{
	return [[[self alloc] initWithStringValue: stringValue] autorelease];
}

- (instancetype)initWithStringValue: (OFString *)stringValue
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		const char *cString = stringValue.UTF8String;
		size_t length = stringValue.UTF8StringLength;

		for (size_t i = 0; i < length; i++)
			if (!of_ascii_isdigit(cString[i]) && cString[i] != ' ')
				@throw [OFInvalidEncodingException exception];

		_numericStringValue = [stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	void *pool = objc_autoreleasePoolPush();
	OFString *numericStringValue;

	@try {
		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_NUMERIC_STRING ||
		    constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1)
			@throw [OFInvalidArgumentException exception];

		numericStringValue = [OFString
		    stringWithCString: DEREncodedContents.items
			     encoding: OF_STRING_ENCODING_ASCII
			       length: DEREncodedContents.count];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithStringValue: numericStringValue];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_numericStringValue release];

	[super dealloc];
}

- (OFString *)stringValue
{
	return self.numericStringValue;
}

- (bool)isEqual: (id)object
{
	OFASN1NumericString *numericString;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1NumericString class]])
		return false;

	numericString = object;

	if (![numericString->_numericStringValue isEqual: _numericStringValue])
		return false;

	return true;
}

- (unsigned long)hash
{
	return _numericStringValue.hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<OFASN1NumericString: %@>",
					   _numericStringValue];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































Deleted src/OFASN1ObjectIdentifier.h version [4f0a319e02].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjetType);
@class OFNumber;

/**
 * @brief An ASN.1 ObjectIdentifier.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1ObjectIdentifier: OFObject
{
	OFArray OF_GENERIC(OFNumber *) *_subidentifiers;
}

/**
 * @brief The subidentifiers of the ObjectIdentifier.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(OFNumber *) *subidentifiers;

/**
 * @brief Creates an ASN.1 ObjectIdentifier with the specified subidentifiers.
 *
 * @param subidentifiers The subidentifiers of the ASN.1 ObjectIdentifier
 * @return A new, autoreleased OFASN1ObjectIdentifier
 */
+ (instancetype)objectIdentifierWithSubidentifiers:
    (OFArray OF_GENERIC(OFNumber *) *)subidentifiers;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated ASN.1 ObjectIdentifier with the
 *	  specified subidentifiers.
 *
 * @param subidentifiers The subidentifiers of the ASN.1 ObjectIdentifier
 * @return An initialized OFASN1ObjectIdentifier
 */
- (instancetype)initWithSubidentifiers:
    (OFArray OF_GENERIC(OFNumber *) *)subidentifiers OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 ObjectIdentifier with the
 *	  specified arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized OFASN1ObjectIdentifier
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































Deleted src/OFASN1ObjectIdentifier.m version [0496bf5735].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1ObjectIdentifier.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFNumber.h"
#import "OFString.h"

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

@implementation OFASN1ObjectIdentifier
@synthesize subidentifiers = _subidentifiers;

+ (instancetype)objectIdentifierWithSubidentifiers:
    (OFArray OF_GENERIC(OFNumber *) *)subidentifiers
{
	return [[[self alloc]
	    initWithSubidentifiers: subidentifiers] autorelease];
}

- (instancetype)initWithSubidentifiers:
    (OFArray OF_GENERIC(OFNumber *) *)subidentifiers
{
	self = [super init];

	@try {
		if (subidentifiers.count < 1)
			@throw [OFInvalidFormatException exception];

		switch ([[subidentifiers objectAtIndex: 0] longLongValue]) {
		case 0:
		case 1:
		case 2:
			break;
		default:
			@throw [OFInvalidFormatException exception];
		}

		_subidentifiers = [subidentifiers copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray OF_GENERIC(OFNumber *) *subidentifiers;

	@try {
		const unsigned char *items = DEREncodedContents.items;
		size_t count = DEREncodedContents.count;
		unsigned long long value = 0;
		uint_fast8_t bits = 0;

		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_OBJECT_IDENTIFIER ||
		    constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1 || count == 0)
			@throw [OFInvalidArgumentException exception];

		subidentifiers = [OFMutableArray array];

		for (size_t i = 0; i < count; i++) {
			if (bits == 0 && items[i] == 0x80)
				@throw [OFInvalidFormatException exception];

			value = (value << 7) | (items[i] & 0x7F);
			bits += 7;

			if (bits > sizeof(unsigned long long) * 8)
				@throw [OFOutOfRangeException exception];

			if (items[i] & 0x80)
				continue;

			if (subidentifiers.count == 0) {
				if (value < 40)
					[subidentifiers addObject:
					    [OFNumber numberWithInt: 0]];
				else if (value < 80) {
					[subidentifiers addObject:
					    [OFNumber numberWithInt: 1]];
					value -= 40;
				} else {
					[subidentifiers addObject:
					    [OFNumber numberWithInt: 2]];
					value -= 80;
				}
			}

			[subidentifiers addObject:
			    [OFNumber numberWithUnsignedLongLong: value]];

			value = 0;
			bits = 0;
		}

		if (items[count - 1] & 0x80)
			@throw [OFInvalidFormatException exception];

		[subidentifiers makeImmutable];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithSubidentifiers: subidentifiers];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_subidentifiers release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFASN1ObjectIdentifier *objectIdentifier;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1ObjectIdentifier class]])
		return false;

	objectIdentifier = object;

	if (![objectIdentifier->_subidentifiers isEqual: _subidentifiers])
		return false;

	return true;
}

- (unsigned long)hash
{
	return _subidentifiers.hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFASN1ObjectIdentifier: %@>",
	    [_subidentifiers componentsJoinedByString: @"."]];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































































































































































































































































































































































Deleted src/OFASN1OctetString.h version [6d83ed8333].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

@class OFData;

/**
 * @brief An ASN.1 OctetString.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1OctetString: OFObject
{
	OFData *_octetStringValue;
}

/**
 * @brief The OctetString value.
 */
@property (readonly, nonatomic) OFData *octetStringValue;

/**
 * @brief Creates an OctetString with the specified value.
 *
 * @param octetStringValue The OctetString value
 * @return A new, autoreleased OFASN1OctetString
 */
+ (instancetype)octetStringWithOctetStringValue: (OFData *)octetStringValue;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OctetString with the specified
 *	  value.
 *
 * @param octetStringValue The OctetString value
 * @return An initialized OFASN1OctetString
 */
- (instancetype)initWithOctetStringValue: (OFData *)octetStringValue
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 OctetString with the specified
 *	  arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized ASN.1 OctetString
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































Deleted src/OFASN1OctetString.m version [3d5ad35b08].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1OctetString.h"
#import "OFData.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"

@implementation OFASN1OctetString
@synthesize octetStringValue = _octetStringValue;

+ (instancetype)octetStringWithOctetStringValue: (OFData *)octetStringValue
{
	return [[[self alloc]
	    initWithOctetStringValue: octetStringValue] autorelease];
}

- (instancetype)initWithOctetStringValue: (OFData *)octetStringValue
{
	self = [super init];

	@try {
		_octetStringValue = [octetStringValue copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	@try {
		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_OCTET_STRING ||
		    constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1)
			@throw [OFInvalidArgumentException exception];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return [self initWithOctetStringValue: DEREncodedContents];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_octetStringValue release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFASN1OctetString *octetString;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1OctetString class]])
		return false;

	octetString = object;

	if (![octetString->_octetStringValue isEqual: _octetStringValue])
		return false;

	return true;
}

- (unsigned long)hash
{
	return _octetStringValue.hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<OFASN1OctetString: %@>",
					   _octetStringValue];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































Deleted src/OFASN1PrintableString.h version [302daaf706].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;

/**
 * @brief An ASN.1 PrintableString.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1PrintableString: OFObject
{
	OFString *_printableStringValue;
}

/**
 * @brief The PrintableString value.
 */
@property (readonly, nonatomic) OFString *printableStringValue;

/**
 * @brief The string value.
 */
@property (readonly, nonatomic) OFString *stringValue;

/**
 * @brief Creates a PrintableString with the specified string value.
 *
 * @param stringValue The string value of the PrintableString
 * @return A new, autoreleased OFASN1PrintableString
 */
+ (instancetype)stringWithStringValue: (OFString *)stringValue;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated PrintableString with the specified
 *	  string value.
 *
 * @param stringValue The string value of the PrintableString
 * @return An initialized OFASN1PrintableString
 */
- (instancetype)initWithStringValue: (OFString *)stringValue
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 PrintableString with the
 *	  specified arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized OFASN1PrintableString
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































Deleted src/OFASN1PrintableString.m version [d326d7d84e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1PrintableString.h"
#import "OFData.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"

@implementation OFASN1PrintableString
@synthesize printableStringValue = _printableStringValue;

+ (instancetype)stringWithStringValue: (OFString *)stringValue
{
	return [[[self alloc] initWithStringValue: stringValue] autorelease];
}

- (instancetype)initWithStringValue: (OFString *)stringValue
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		const char *cString = stringValue.UTF8String;
		size_t length = stringValue.UTF8StringLength;

		for (size_t i = 0; i < length; i++) {
			if (of_ascii_isalnum(cString[i]))
				continue;

			switch (cString[i]) {
			case ' ':
			case '\'':
			case '(':
			case ')':
			case '+':
			case ',':
			case '-':
			case '.':
			case '/':
			case ':':
			case '=':
			case '?':
				continue;
			default:
				@throw [OFInvalidEncodingException exception];
			}
		}

		_printableStringValue = [stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	void *pool = objc_autoreleasePoolPush();
	OFString *printableStringValue;

	@try {
		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_PRINTABLE_STRING ||
		    constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1)
			@throw [OFInvalidArgumentException exception];

		printableStringValue = [OFString
		    stringWithCString: DEREncodedContents.items
			     encoding: OF_STRING_ENCODING_ASCII
			       length: DEREncodedContents.count];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithStringValue: printableStringValue];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_printableStringValue release];

	[super dealloc];
}

- (OFString *)stringValue
{
	return self.printableStringValue;
}

- (bool)isEqual: (id)object
{
	OFASN1PrintableString *printableString;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1PrintableString class]])
		return false;

	printableString = object;

	if (![printableString->_printableStringValue isEqual:
	    _printableStringValue])
		return false;

	return true;
}

- (unsigned long)hash
{
	return _printableStringValue.hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<OFASN1PrintableString: %@>",
					   _printableStringValue];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































































































Deleted src/OFASN1UTF8String.h version [8f256e2e35].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;

/**
 * @brief An ASN.1 UTF8String.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1UTF8String: OFObject
{
	OFString *_UTF8StringValue;
}

/**
 * @brief The UTF8String value.
 */
@property (readonly, nonatomic) OFString *UTF8StringValue;

/**
 * @brief The string value.
 */
@property (readonly, nonatomic) OFString *stringValue;

/**
 * @brief Creates a UTF8String with the specified string value.
 *
 * @param stringValue The string value of the UTF8String
 * @return A new, autoreleased OFASN1UTF8String
 */
+ (instancetype)stringWithStringValue: (OFString *)stringValue;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated UTF8String with the specified
 *	  string value.
 *
 * @param stringValue The string value of the UTF8String
 * @return An initialized OFASN1UTF8String
 */
- (instancetype)initWithStringValue: (OFString *)stringValue
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated ASN.1 UTF8String with the specified
 *	  arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized OFASN1UTF8String
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































Deleted src/OFASN1UTF8String.m version [fe525c2cd3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1UTF8String.h"
#import "OFData.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"

@implementation OFASN1UTF8String
@synthesize UTF8StringValue = _UTF8StringValue;

+ (instancetype)stringWithStringValue: (OFString *)stringValue
{
	return [[[self alloc] initWithStringValue: stringValue] autorelease];
}

- (instancetype)initWithStringValue: (OFString *)stringValue
{
	self = [super init];

	@try {
		_UTF8StringValue = [stringValue copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	void *pool = objc_autoreleasePoolPush();
	OFString *UTF8StringValue;

	@try {
		if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL ||
		    tagNumber != OF_ASN1_TAG_NUMBER_UTF8_STRING || constructed)
			@throw [OFInvalidArgumentException exception];

		if (DEREncodedContents.itemSize != 1)
			@throw [OFInvalidArgumentException exception];

		UTF8StringValue = [OFString
		    stringWithUTF8String: DEREncodedContents.items
				  length: DEREncodedContents.count];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithStringValue: UTF8StringValue];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_UTF8StringValue release];

	[super dealloc];
}

- (OFString *)stringValue
{
	return self.UTF8StringValue;
}

- (bool)isEqual: (id)object
{
	OFASN1UTF8String *UTF8String;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1UTF8String class]])
		return false;

	UTF8String = object;

	if (![UTF8String->_UTF8StringValue isEqual: _UTF8StringValue])
		return false;

	return true;
}

- (unsigned long)hash
{
	return _UTF8StringValue.hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<OFASN1UTF8String: %@>",
					   _UTF8StringValue];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































































Deleted src/OFASN1Value.h version [80070a2f85].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFData;

/**
 * @brief ASN.1 tag class.
 */
typedef enum {
	/** Universal */
	OF_ASN1_TAG_CLASS_UNIVERSAL	   = 0x0,
	/** Application */
	OF_ASN1_TAG_CLASS_APPLICATION	   = 0x1,
	/** Context specific */
	OF_ASN1_TAG_CLASS_CONTEXT_SPECIFIC = 0x2,
	/** Private */
	OF_ASN1_TAG_CLASS_PRIVATE	   = 0x3
} of_asn1_tag_class_t;

/**
 * @brief ASN.1 tag number.
 */
typedef enum {
	/** Boolean */
	OF_ASN1_TAG_NUMBER_BOOLEAN	     = 0x01,
	/** Integer */
	OF_ASN1_TAG_NUMBER_INTEGER	     = 0x02,
	/** Bit string */
	OF_ASN1_TAG_NUMBER_BIT_STRING	     = 0x03,
	/** Octet string */
	OF_ASN1_TAG_NUMBER_OCTET_STRING	     = 0x04,
	/** Null */
	OF_ASN1_TAG_NUMBER_NULL		     = 0x05,
	/** Object Identifier */
	OF_ASN1_TAG_NUMBER_OBJECT_IDENTIFIER = 0x06,
	/** Enumerated */
	OF_ASN1_TAG_NUMBER_ENUMERATED	     = 0x0A,
	/** UTF-8 string */
	OF_ASN1_TAG_NUMBER_UTF8_STRING	     = 0x0C,
	/** Sequence */
	OF_ASN1_TAG_NUMBER_SEQUENCE	     = 0x10,
	/** Set */
	OF_ASN1_TAG_NUMBER_SET		     = 0x11,
	/** NumericString */
	OF_ASN1_TAG_NUMBER_NUMERIC_STRING    = 0x12,
	/** PrintableString */
	OF_ASN1_TAG_NUMBER_PRINTABLE_STRING  = 0x13,
	/** IA5String */
	OF_ASN1_TAG_NUMBER_IA5_STRING	     = 0x16
} of_asn1_tag_number_t;

/**
 * @brief A class representing an ASN.1 value.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFASN1Value: OFObject
{
	of_asn1_tag_class_t _tagClass;
	of_asn1_tag_number_t _tagNumber;
	bool _constructed;
	OFData *_DEREncodedContents;
}

/**
 * @brief The tag class of the value's type.
 */
@property (readonly, nonatomic) of_asn1_tag_class_t tagClass;

/**
 * @brief The tag number of the value's type.
 */
@property (readonly, nonatomic) of_asn1_tag_number_t tagNumber;

/**
 * @brief Whether the value if of a constructed type.
 */
@property (readonly, nonatomic, getter=isConstructed) bool constructed;

/**
 * @brief The DER-encoded contents octets of the value.
 */
@property (readonly, nonatomic) OFData *DEREncodedContents;

/**
 * @brief Creates a new ASN.1 value with the specified arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return A new ASN.1 value
 */
+ (instancetype)valueWithTagClass: (of_asn1_tag_class_t)tagClass
			tagNumber: (of_asn1_tag_number_t)tagNumber
		      constructed: (bool)constructed
	       DEREncodedContents: (OFData *)DEREncodedContents;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated ASN.1 value with the specified
 *	  arguments.
 *
 * @param tagClass The tag class of the value's type
 * @param tagNumber The tag number of the value's type
 * @param constructed Whether the value if of a constructed type
 * @param DEREncodedContents The DER-encoded contents octets of the value.
 * @return An initialized ASN.1 value
 */
- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
    OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































Deleted src/OFASN1Value.m version [e6d6355cac].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFASN1Value.h"
#import "OFData.h"
#import "OFString.h"

#import "OFInvalidFormatException.h"

@implementation OFASN1Value
@synthesize tagClass = _tagClass, tagNumber = _tagNumber;
@synthesize constructed = _constructed;
@synthesize DEREncodedContents = _DEREncodedContents;

+ (instancetype)valueWithTagClass: (of_asn1_tag_class_t)tagClass
			tagNumber: (of_asn1_tag_number_t)tagNumber
		      constructed: (bool)constructed
	       DEREncodedContents: (OFData *)DEREncodedContents
{
	return [[[self alloc]
	      initWithTagClass: tagClass
		     tagNumber: tagNumber
		   constructed: constructed
	    DEREncodedContents: DEREncodedContents] autorelease];
}

- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass
		       tagNumber: (of_asn1_tag_number_t)tagNumber
		     constructed: (bool)constructed
	      DEREncodedContents: (OFData *)DEREncodedContents
{
	self = [super init];

	@try {
		if (DEREncodedContents.itemSize != 1)
			@throw [OFInvalidFormatException exception];

		_tagClass = tagClass;
		_tagNumber = tagNumber;
		_constructed = constructed;
		_DEREncodedContents = [DEREncodedContents copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_DEREncodedContents release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFASN1Value *value;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFASN1Value class]])
		return false;

	value = object;

	if (value->_tagClass != _tagClass)
		return false;
	if (value->_tagNumber != _tagNumber)
		return false;
	if (value->_constructed != _constructed)
		return false;
	if (![value->_DEREncodedContents isEqual: _DEREncodedContents])
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD(hash, _tagClass & 0xFF);
	OF_HASH_ADD(hash, _tagNumber & 0xFF);
	OF_HASH_ADD(hash, _constructed);
	OF_HASH_ADD_HASH(hash, _DEREncodedContents.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFASN1Value:\n"
	    @"\tTag class = %x\n"
	    @"\tTag number = %x\n"
	    @"\tConstructed = %u\n"
	    @"\tDER-encoded contents = %@\n"
	    @">",
	    _tagClass, _tagNumber, _constructed,
	    _DEREncodedContents.description];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































































































Renamed and modified src/of_asprintf.h [5347c6d019] to src/OFASPrintF.h [090f42d33a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#import "macros.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __cplusplus
extern "C" {
#endif
extern int of_asprintf(
    char *_Nullable *_Nonnull, const char *_Nonnull, ...);
extern int of_vasprintf(
    char *_Nullable *_Nonnull, const char *_Nonnull, va_list);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|
<
<






25
26
27
28
29
30
31
32


33
34
35
36
37
38
#import "macros.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __cplusplus
extern "C" {
#endif
extern int OFVASPrintF(


    char *_Nullable *_Nonnull, const char *_Nonnull, va_list);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Renamed and modified src/of_asprintf.m [cd42320784] to src/OFASPrintF.m [31044637f2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#endif

#import "OFString.h"
#import "OFLocale.h"

#import "OFInitializationFailedException.h"

#define MAX_SUBFORMAT_LEN 64

#ifndef HAVE_ASPRINTF
/*
 * (v)asprintf might be declared, but HAVE_ASPRINTF not defined because
 * configure determined it is broken. In this case, we must make sure there is
 * no name clash.
 */
# define asprintf asprintf_
# define vasprintf vasprintf_
#endif

struct context {
	const char *format;
	size_t formatLen;
	char subformat[MAX_SUBFORMAT_LEN + 1];
	size_t subformatLen;
	va_list arguments;
	char *buffer;
	size_t bufferLen;
	size_t i, last;
	enum {
		STATE_STRING,
		STATE_FORMAT_FLAGS,
		STATE_FORMAT_FIELD_WIDTH,
		STATE_FORMAT_LENGTH_MODIFIER,
		STATE_FORMAT_CONVERSION_SPECIFIER
	} state;
	enum {
		LENGTH_MODIFIER_NONE,
		LENGTH_MODIFIER_HH,
		LENGTH_MODIFIER_H,
		LENGTH_MODIFIER_L,
		LENGTH_MODIFIER_LL,
		LENGTH_MODIFIER_J,
		LENGTH_MODIFIER_Z,
		LENGTH_MODIFIER_T,
		LENGTH_MODIFIER_CAPITAL_L
	} lengthModifier;
	bool useLocale;
};

#ifdef HAVE_ASPRINTF_L
static locale_t cLocale;

OF_CONSTRUCTOR()
{
	if ((cLocale = newlocale(LC_ALL_MASK, "C", NULL)) == NULL)
		@throw [OFInitializationFailedException exception];
}
#endif

#ifndef HAVE_ASPRINTF
static int
vasprintf(char **string, const char *format, va_list arguments)
{
	int expectedLength, length;
	va_list argumentsCopy;

	va_copy(argumentsCopy, arguments);

	expectedLength = vsnprintf(NULL, 0, format, argumentsCopy);
	if (expectedLength == -1)
		/*
		 * We have no way to know how large it is. Let's try 64 KB and
		 * hope.
		 */
		expectedLength = 65535;

	if ((*string = malloc((size_t)expectedLength + 1)) == NULL)
		return -1;

	length = vsnprintf(*string, (size_t)expectedLength + 1,
	    format, arguments);



	if (length == -1 || length > expectedLength) {

		free(*string);
		*string = NULL;
		return -1;
	}



	/*
	 * In case we could not determine the size, resize to the actual size
	 * needed, but ignore any failure to do so.
	 */
	if (length < expectedLength) {
		char *resized;


		if ((resized = realloc(*string, length + 1)) != NULL)
			*string = resized;
	}

	return length;
}

static int







|











|


|






|
|
|
|
|


|
|
|
|
|
|
|
|
|


















|
|

|

<
|
|
<
<
<
<

|
|

|
|

>
>
|
>
|
<
|
|

>
>
|
<
<
<
|
|

>
|







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
#endif

#import "OFString.h"
#import "OFLocale.h"

#import "OFInitializationFailedException.h"

#define maxSubformatLen 64

#ifndef HAVE_ASPRINTF
/*
 * (v)asprintf might be declared, but HAVE_ASPRINTF not defined because
 * configure determined it is broken. In this case, we must make sure there is
 * no name clash.
 */
# define asprintf asprintf_
# define vasprintf vasprintf_
#endif

struct Context {
	const char *format;
	size_t formatLen;
	char subformat[maxSubformatLen + 1];
	size_t subformatLen;
	va_list arguments;
	char *buffer;
	size_t bufferLen;
	size_t i, last;
	enum {
		stateString,
		stateFormatFlags,
		stateFormatFieldWidth,
		stateFormatLengthModifier,
		stateFormatConversionSpecifier
	} state;
	enum {
		lengthModifierNone,
		lengthModifierHH,
		lengthModifierH,
		lengthModifierL,
		lengthModifierLL,
		lengthModifierJ,
		lengthModifierZ,
		lengthModifierT,
		lengthModifierCapitalL
	} lengthModifier;
	bool useLocale;
};

#ifdef HAVE_ASPRINTF_L
static locale_t cLocale;

OF_CONSTRUCTOR()
{
	if ((cLocale = newlocale(LC_ALL_MASK, "C", NULL)) == NULL)
		@throw [OFInitializationFailedException exception];
}
#endif

#ifndef HAVE_ASPRINTF
static int
vasprintf(char **string, const char *format, va_list arguments)
{
	int length;
	size_t bufferLength = 128;

	*string = NULL;


	for (;;) {
		free(*string);





		if ((*string = malloc(bufferLength)) == NULL)
			return -1;

		length = vsnprintf(*string, bufferLength - 1, format,
		    arguments);

		if (length >= 0 && (size_t)length < bufferLength - 1)
			break;

		if (bufferLength > INT_MAX / 2) {
			free(*string);

			return -1;
		}

		bufferLength <<= 1;
	}




	if (length > 0 && (size_t)length != bufferLength - 1) {
		char *resized = realloc(*string, length + 1);

		/* Ignore if making it smaller failed. */
		if (resized != NULL)
			*string = resized;
	}

	return length;
}

static int
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
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
	va_end(arguments);

	return ret;
}
#endif

static bool
appendString(struct context *ctx, const char *append, size_t appendLen)
{
	char *newBuf;

	if (appendLen == 0)
		return true;

	if ((newBuf = realloc(ctx->buffer,
	    ctx->bufferLen + appendLen + 1)) == NULL)
		return false;

	memcpy(newBuf + ctx->bufferLen, append, appendLen);

	ctx->buffer = newBuf;
	ctx->bufferLen += appendLen;

	return true;
}

static bool
appendSubformat(struct context *ctx, const char *subformat,
    size_t subformatLen)
{
	if (ctx->subformatLen + subformatLen > MAX_SUBFORMAT_LEN)
		return false;

	memcpy(ctx->subformat + ctx->subformatLen, subformat, subformatLen);
	ctx->subformatLen += subformatLen;
	ctx->subformat[ctx->subformatLen] = 0;

	return true;
}

static bool
stringState(struct context *ctx)
{
	if (ctx->format[ctx->i] == '%') {
		if (ctx->i > 0)
			if (!appendString(ctx, ctx->format + ctx->last,
			    ctx->i - ctx->last))
				return false;

		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		ctx->last = ctx->i + 1;
		ctx->state = STATE_FORMAT_FLAGS;
	}

	return true;
}

static bool
formatFlagsState(struct context *ctx)
{
	switch (ctx->format[ctx->i]) {
	case '-':
	case '+':
	case ' ':
	case '#':
	case '0':
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		break;
	case ',':
		/* ObjFW extension: Use decimal point from locale */
		ctx->useLocale = true;
		break;
	default:
		ctx->state = STATE_FORMAT_FIELD_WIDTH;
		ctx->i--;

		break;
	}

	return true;
}

static bool
formatFieldWidthState(struct context *ctx)
{
	if ((ctx->format[ctx->i] >= '0' && ctx->format[ctx->i] <= '9') ||
	    ctx->format[ctx->i] == '*' || ctx->format[ctx->i] == '.') {
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;
	} else {
		ctx->state = STATE_FORMAT_LENGTH_MODIFIER;
		ctx->i--;
	}

	return true;
}

static bool
formatLengthModifierState(struct context *ctx)
{
	/* Only one allowed */
	switch (ctx->format[ctx->i]) {
	case 'h': /* and also hh */
		if (ctx->formatLen > ctx->i + 1 &&
		    ctx->format[ctx->i + 1] == 'h') {
			if (!appendSubformat(ctx, ctx->format + ctx->i, 2))
				return false;

			ctx->i++;
			ctx->lengthModifier = LENGTH_MODIFIER_HH;
		} else {
			if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
				return false;

			ctx->lengthModifier = LENGTH_MODIFIER_H;
		}

		break;
	case 'l': /* and also ll */
		if (ctx->formatLen > ctx->i + 1 &&
		    ctx->format[ctx->i + 1] == 'l') {
#ifndef OF_WINDOWS
			if (!appendSubformat(ctx, ctx->format + ctx->i, 2))
				return false;
#else
			if (!appendSubformat(ctx, "I64", 3))
				return false;
#endif

			ctx->i++;
			ctx->lengthModifier = LENGTH_MODIFIER_LL;
		} else {
			if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
				return false;

			ctx->lengthModifier = LENGTH_MODIFIER_L;
		}

		break;
	case 'j':
#if defined(OF_WINDOWS)
		if (!appendSubformat(ctx, "I64", 3))
			return false;
#elif defined(_NEWLIB_VERSION)
		if (!appendSubformat(ctx, "ll", 2))
			return false;
#else
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;
#endif

		ctx->lengthModifier = LENGTH_MODIFIER_J;

		break;
	case 'z':
#if defined(OF_WINDOWS)
		if (sizeof(size_t) == 8)
			if (!appendSubformat(ctx, "I64", 3))
				return false;
#elif defined(_NEWLIB_VERSION)
		if (!appendSubformat(ctx, "l", 1))
			return false;
#else
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;
#endif

		ctx->lengthModifier = LENGTH_MODIFIER_Z;

		break;
	case 't':
#if defined(OF_WINDOWS)
		if (sizeof(ptrdiff_t) == 8)
			if (!appendSubformat(ctx, "I64", 3))
				return false;
#elif defined(_NEWLIB_VERSION)
		if (!appendSubformat(ctx, "l", 1))
			return false;
#else
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;
#endif

		ctx->lengthModifier = LENGTH_MODIFIER_T;

		break;
	case 'L':
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		ctx->lengthModifier = LENGTH_MODIFIER_CAPITAL_L;

		break;
#ifdef OF_WINDOWS
	case 'I': /* win32 strangeness (I64 instead of ll or j) */
		if (ctx->formatLen > ctx->i + 2 &&
		    ctx->format[ctx->i + 1] == '6' &&
		    ctx->format[ctx->i + 2] == '4') {
			if (!appendSubformat(ctx, ctx->format + ctx->i, 3))
				return false;

			ctx->i += 2;
			ctx->lengthModifier = LENGTH_MODIFIER_LL;
		} else
			ctx->i--;

		break;
#endif
#ifdef OF_IOS
	case 'q': /* iOS uses this for PRI?64 */
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		ctx->lengthModifier = LENGTH_MODIFIER_LL;

		break;
#endif
	default:
		ctx->i--;

		break;
	}

	ctx->state = STATE_FORMAT_CONVERSION_SPECIFIER;
	return true;
}

static bool
formatConversionSpecifierState(struct context *ctx)
{
	char *tmp = NULL;
	int tmpLen = 0;
#ifndef HAVE_ASPRINTF_L
	OFString *point;
#endif

	if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
		return false;

	switch (ctx->format[ctx->i]) {
	case '@':
		if (ctx->lengthModifier != LENGTH_MODIFIER_NONE)
			return false;

		ctx->subformat[ctx->subformatLen - 1] = 's';

		@try {
			id object;








|



















|


|










|











|






|
















|









|






|







|










|




|















|




|







|







|







|







|







|







|






|











|










|









|




|












|







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
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
	va_end(arguments);

	return ret;
}
#endif

static bool
appendString(struct Context *ctx, const char *append, size_t appendLen)
{
	char *newBuf;

	if (appendLen == 0)
		return true;

	if ((newBuf = realloc(ctx->buffer,
	    ctx->bufferLen + appendLen + 1)) == NULL)
		return false;

	memcpy(newBuf + ctx->bufferLen, append, appendLen);

	ctx->buffer = newBuf;
	ctx->bufferLen += appendLen;

	return true;
}

static bool
appendSubformat(struct Context *ctx, const char *subformat,
    size_t subformatLen)
{
	if (ctx->subformatLen + subformatLen > maxSubformatLen)
		return false;

	memcpy(ctx->subformat + ctx->subformatLen, subformat, subformatLen);
	ctx->subformatLen += subformatLen;
	ctx->subformat[ctx->subformatLen] = 0;

	return true;
}

static bool
stringState(struct Context *ctx)
{
	if (ctx->format[ctx->i] == '%') {
		if (ctx->i > 0)
			if (!appendString(ctx, ctx->format + ctx->last,
			    ctx->i - ctx->last))
				return false;

		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		ctx->last = ctx->i + 1;
		ctx->state = stateFormatFlags;
	}

	return true;
}

static bool
formatFlagsState(struct Context *ctx)
{
	switch (ctx->format[ctx->i]) {
	case '-':
	case '+':
	case ' ':
	case '#':
	case '0':
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		break;
	case ',':
		/* ObjFW extension: Use decimal point from locale */
		ctx->useLocale = true;
		break;
	default:
		ctx->state = stateFormatFieldWidth;
		ctx->i--;

		break;
	}

	return true;
}

static bool
formatFieldWidthState(struct Context *ctx)
{
	if ((ctx->format[ctx->i] >= '0' && ctx->format[ctx->i] <= '9') ||
	    ctx->format[ctx->i] == '*' || ctx->format[ctx->i] == '.') {
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;
	} else {
		ctx->state = stateFormatLengthModifier;
		ctx->i--;
	}

	return true;
}

static bool
formatLengthModifierState(struct Context *ctx)
{
	/* Only one allowed */
	switch (ctx->format[ctx->i]) {
	case 'h': /* and also hh */
		if (ctx->formatLen > ctx->i + 1 &&
		    ctx->format[ctx->i + 1] == 'h') {
			if (!appendSubformat(ctx, ctx->format + ctx->i, 2))
				return false;

			ctx->i++;
			ctx->lengthModifier = lengthModifierHH;
		} else {
			if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
				return false;

			ctx->lengthModifier = lengthModifierH;
		}

		break;
	case 'l': /* and also ll */
		if (ctx->formatLen > ctx->i + 1 &&
		    ctx->format[ctx->i + 1] == 'l') {
#ifndef OF_WINDOWS
			if (!appendSubformat(ctx, ctx->format + ctx->i, 2))
				return false;
#else
			if (!appendSubformat(ctx, "I64", 3))
				return false;
#endif

			ctx->i++;
			ctx->lengthModifier = lengthModifierLL;
		} else {
			if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
				return false;

			ctx->lengthModifier = lengthModifierL;
		}

		break;
	case 'j':
#if defined(OF_WINDOWS)
		if (!appendSubformat(ctx, "I64", 3))
			return false;
#elif defined(_NEWLIB_VERSION) || defined(OF_HPUX)
		if (!appendSubformat(ctx, "ll", 2))
			return false;
#else
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;
#endif

		ctx->lengthModifier = lengthModifierJ;

		break;
	case 'z':
#if defined(OF_WINDOWS)
		if (sizeof(size_t) == 8)
			if (!appendSubformat(ctx, "I64", 3))
				return false;
#elif defined(_NEWLIB_VERSION) || defined(OF_HPUX)
		if (!appendSubformat(ctx, "l", 1))
			return false;
#else
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;
#endif

		ctx->lengthModifier = lengthModifierZ;

		break;
	case 't':
#if defined(OF_WINDOWS)
		if (sizeof(ptrdiff_t) == 8)
			if (!appendSubformat(ctx, "I64", 3))
				return false;
#elif defined(_NEWLIB_VERSION) || defined(OF_HPUX)
		if (!appendSubformat(ctx, "l", 1))
			return false;
#else
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;
#endif

		ctx->lengthModifier = lengthModifierT;

		break;
	case 'L':
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		ctx->lengthModifier = lengthModifierCapitalL;

		break;
#ifdef OF_WINDOWS
	case 'I': /* win32 strangeness (I64 instead of ll or j) */
		if (ctx->formatLen > ctx->i + 2 &&
		    ctx->format[ctx->i + 1] == '6' &&
		    ctx->format[ctx->i + 2] == '4') {
			if (!appendSubformat(ctx, ctx->format + ctx->i, 3))
				return false;

			ctx->i += 2;
			ctx->lengthModifier = lengthModifierLL;
		} else
			ctx->i--;

		break;
#endif
#ifdef OF_IOS
	case 'q': /* iOS uses this for PRI?64 */
		if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
			return false;

		ctx->lengthModifier = lengthModifierLL;

		break;
#endif
	default:
		ctx->i--;

		break;
	}

	ctx->state = stateFormatConversionSpecifier;
	return true;
}

static bool
formatConversionSpecifierState(struct Context *ctx)
{
	char *tmp = NULL;
	int tmpLen = 0;
#ifndef HAVE_ASPRINTF_L
	OFString *point;
#endif

	if (!appendSubformat(ctx, ctx->format + ctx->i, 1))
		return false;

	switch (ctx->format[ctx->i]) {
	case '@':
		if (ctx->lengthModifier != lengthModifierNone)
			return false;

		ctx->subformat[ctx->subformatLen - 1] = 's';

		@try {
			id object;

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
		} @catch (id e) {
			free(ctx->buffer);
			@throw e;
		}

		break;
	case 'C':
		if (ctx->lengthModifier != LENGTH_MODIFIER_NONE)
			return false;

		ctx->subformat[ctx->subformatLen - 1] = 's';

		{
			char buffer[5];
			size_t len = of_string_utf8_encode(
			    va_arg(ctx->arguments, of_unichar_t), buffer);

			if (len == 0)
				return false;

			buffer[len] = 0;
			tmpLen = asprintf(&tmp, ctx->subformat, buffer);
		}

		break;
	case 'S':
		if (ctx->lengthModifier != LENGTH_MODIFIER_NONE)
			return false;

		ctx->subformat[ctx->subformatLen - 1] = 's';

		{
			const of_unichar_t *arg =
			    va_arg(ctx->arguments, const of_unichar_t *);
			size_t j, len = of_string_utf32_length(arg);
			char *buffer;

			if (SIZE_MAX / 4 < len || (SIZE_MAX / 4) - len < 1)
				return false;

			if ((buffer = malloc((len * 4) + 1)) == NULL)
				return false;

			j = 0;
			for (size_t i = 0; i < len; i++) {
				size_t clen = of_string_utf8_encode(arg[i],
				    buffer + j);

				if (clen == 0) {
					free(buffer);
					return false;
				}

				j += clen;
			}
			buffer[j] = 0;

			tmpLen = asprintf(&tmp, ctx->subformat, buffer);

			free(buffer);
		}

		break;
	case 'd':
	case 'i':
		switch (ctx->lengthModifier) {
		case LENGTH_MODIFIER_NONE:
		case LENGTH_MODIFIER_HH:
		case LENGTH_MODIFIER_H:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, int));
			break;
		case LENGTH_MODIFIER_L:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, long));
			break;
		case LENGTH_MODIFIER_LL:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, long long));
			break;
		case LENGTH_MODIFIER_J:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, intmax_t));
			break;
		case LENGTH_MODIFIER_Z:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, ssize_t));
			break;
		case LENGTH_MODIFIER_T:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, ptrdiff_t));
			break;
		default:
			return false;
		}

		break;
	case 'o':
	case 'u':
	case 'x':
	case 'X':
		switch (ctx->lengthModifier) {
		case LENGTH_MODIFIER_NONE:
		case LENGTH_MODIFIER_HH:
		case LENGTH_MODIFIER_H:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, unsigned int));
			break;
		case LENGTH_MODIFIER_L:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, unsigned long));
			break;
		case LENGTH_MODIFIER_LL:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, unsigned long long));
			break;
		case LENGTH_MODIFIER_J:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, uintmax_t));
			break;
		case LENGTH_MODIFIER_Z:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, size_t));
			break;
		case LENGTH_MODIFIER_T:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, ptrdiff_t));
			break;
		default:
			return false;
		}

		break;
	case 'f':
	case 'F':
	case 'e':
	case 'E':
	case 'g':
	case 'G':
	case 'a':
	case 'A':
		switch (ctx->lengthModifier) {
		case LENGTH_MODIFIER_NONE:
		case LENGTH_MODIFIER_L:
#ifdef HAVE_ASPRINTF_L
			if (!ctx->useLocale)
				tmpLen = asprintf_l(&tmp, cLocale,
				    ctx->subformat,
				    va_arg(ctx->arguments, double));
			else
#endif
				tmpLen = asprintf(&tmp, ctx->subformat,
				    va_arg(ctx->arguments, double));
			break;
		case LENGTH_MODIFIER_CAPITAL_L:
#ifdef HAVE_ASPRINTF_L
			if (!ctx->useLocale)
				tmpLen = asprintf_l(&tmp, cLocale,
				    ctx->subformat,
				    va_arg(ctx->arguments, long double));
			else
#endif







|






|
|










|





|
|
|










|




















|
|
|



|



|



|



|



|













|
|
|



|



|



|



|



|

















|
|










|







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
		} @catch (id e) {
			free(ctx->buffer);
			@throw e;
		}

		break;
	case 'C':
		if (ctx->lengthModifier != lengthModifierNone)
			return false;

		ctx->subformat[ctx->subformatLen - 1] = 's';

		{
			char buffer[5];
			size_t len = OFUTF8StringEncode(
			    va_arg(ctx->arguments, OFUnichar), buffer);

			if (len == 0)
				return false;

			buffer[len] = 0;
			tmpLen = asprintf(&tmp, ctx->subformat, buffer);
		}

		break;
	case 'S':
		if (ctx->lengthModifier != lengthModifierNone)
			return false;

		ctx->subformat[ctx->subformatLen - 1] = 's';

		{
			const OFUnichar *arg =
			    va_arg(ctx->arguments, const OFUnichar *);
			size_t j, len = OFUTF32StringLength(arg);
			char *buffer;

			if (SIZE_MAX / 4 < len || (SIZE_MAX / 4) - len < 1)
				return false;

			if ((buffer = malloc((len * 4) + 1)) == NULL)
				return false;

			j = 0;
			for (size_t i = 0; i < len; i++) {
				size_t clen = OFUTF8StringEncode(arg[i],
				    buffer + j);

				if (clen == 0) {
					free(buffer);
					return false;
				}

				j += clen;
			}
			buffer[j] = 0;

			tmpLen = asprintf(&tmp, ctx->subformat, buffer);

			free(buffer);
		}

		break;
	case 'd':
	case 'i':
		switch (ctx->lengthModifier) {
		case lengthModifierNone:
		case lengthModifierHH:
		case lengthModifierH:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, int));
			break;
		case lengthModifierL:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, long));
			break;
		case lengthModifierLL:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, long long));
			break;
		case lengthModifierJ:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, intmax_t));
			break;
		case lengthModifierZ:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, ssize_t));
			break;
		case lengthModifierT:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, ptrdiff_t));
			break;
		default:
			return false;
		}

		break;
	case 'o':
	case 'u':
	case 'x':
	case 'X':
		switch (ctx->lengthModifier) {
		case lengthModifierNone:
		case lengthModifierHH:
		case lengthModifierH:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, unsigned int));
			break;
		case lengthModifierL:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, unsigned long));
			break;
		case lengthModifierLL:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, unsigned long long));
			break;
		case lengthModifierJ:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, uintmax_t));
			break;
		case lengthModifierZ:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, size_t));
			break;
		case lengthModifierT:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, ptrdiff_t));
			break;
		default:
			return false;
		}

		break;
	case 'f':
	case 'F':
	case 'e':
	case 'E':
	case 'g':
	case 'G':
	case 'a':
	case 'A':
		switch (ctx->lengthModifier) {
		case lengthModifierNone:
		case lengthModifierL:
#ifdef HAVE_ASPRINTF_L
			if (!ctx->useLocale)
				tmpLen = asprintf_l(&tmp, cLocale,
				    ctx->subformat,
				    va_arg(ctx->arguments, double));
			else
#endif
				tmpLen = asprintf(&tmp, ctx->subformat,
				    va_arg(ctx->arguments, double));
			break;
		case lengthModifierCapitalL:
#ifdef HAVE_ASPRINTF_L
			if (!ctx->useLocale)
				tmpLen = asprintf_l(&tmp, cLocale,
				    ctx->subformat,
				    va_arg(ctx->arguments, long double));
			else
#endif
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
			tmp = tmp2;
		}
#endif

		break;
	case 'c':
		switch (ctx->lengthModifier) {
		case LENGTH_MODIFIER_NONE:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, int));
			break;
		case LENGTH_MODIFIER_L:
#ifdef HAVE_WCHAR_H
# if WINT_MAX >= INT_MAX
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, wint_t));
# else
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, int));
# endif
			break;
#endif
		default:
			return false;
		}

		break;
	case 's':
		switch (ctx->lengthModifier) {
		case LENGTH_MODIFIER_NONE:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, const char *));
			break;
#ifdef HAVE_WCHAR_T
		case LENGTH_MODIFIER_L:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, const wchar_t *));
			break;
#endif
		default:
			return false;
		}

		break;
	case 'p':
		if (ctx->lengthModifier != LENGTH_MODIFIER_NONE)
			return false;

		tmpLen = asprintf(&tmp, ctx->subformat,
		    va_arg(ctx->arguments, void *));

		break;
	case 'n':
		switch (ctx->lengthModifier) {
		case LENGTH_MODIFIER_NONE:
			*va_arg(ctx->arguments, int *) = (int)ctx->bufferLen;
			break;
		case LENGTH_MODIFIER_HH:
			*va_arg(ctx->arguments, signed char *) =
			    (signed char)ctx->bufferLen;
			break;
		case LENGTH_MODIFIER_H:
			*va_arg(ctx->arguments, short *) =
			    (short)ctx->bufferLen;
			break;
		case LENGTH_MODIFIER_L:
			*va_arg(ctx->arguments, long *) =
			    (long)ctx->bufferLen;
			break;
		case LENGTH_MODIFIER_LL:
			*va_arg(ctx->arguments, long long *) =
			    (long long)ctx->bufferLen;
			break;
		case LENGTH_MODIFIER_J:
			*va_arg(ctx->arguments, intmax_t *) =
			    (intmax_t)ctx->bufferLen;
			break;
		case LENGTH_MODIFIER_Z:
			*va_arg(ctx->arguments, size_t *) =
			    (size_t)ctx->bufferLen;
			break;
		case LENGTH_MODIFIER_T:
			*va_arg(ctx->arguments, ptrdiff_t *) =
			    (ptrdiff_t)ctx->bufferLen;
			break;
		default:
			return false;
		}

		break;
	case '%':
		if (ctx->lengthModifier != LENGTH_MODIFIER_NONE)
			return false;

		if (!appendString(ctx, "%", 1))
			return false;

		break;
	default:







|



|

















|




|










|








|


|



|



|



|



|



|



|









|







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
			tmp = tmp2;
		}
#endif

		break;
	case 'c':
		switch (ctx->lengthModifier) {
		case lengthModifierNone:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, int));
			break;
		case lengthModifierL:
#ifdef HAVE_WCHAR_H
# if WINT_MAX >= INT_MAX
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, wint_t));
# else
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, int));
# endif
			break;
#endif
		default:
			return false;
		}

		break;
	case 's':
		switch (ctx->lengthModifier) {
		case lengthModifierNone:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, const char *));
			break;
#ifdef HAVE_WCHAR_T
		case lengthModifierL:
			tmpLen = asprintf(&tmp, ctx->subformat,
			    va_arg(ctx->arguments, const wchar_t *));
			break;
#endif
		default:
			return false;
		}

		break;
	case 'p':
		if (ctx->lengthModifier != lengthModifierNone)
			return false;

		tmpLen = asprintf(&tmp, ctx->subformat,
		    va_arg(ctx->arguments, void *));

		break;
	case 'n':
		switch (ctx->lengthModifier) {
		case lengthModifierNone:
			*va_arg(ctx->arguments, int *) = (int)ctx->bufferLen;
			break;
		case lengthModifierHH:
			*va_arg(ctx->arguments, signed char *) =
			    (signed char)ctx->bufferLen;
			break;
		case lengthModifierH:
			*va_arg(ctx->arguments, short *) =
			    (short)ctx->bufferLen;
			break;
		case lengthModifierL:
			*va_arg(ctx->arguments, long *) =
			    (long)ctx->bufferLen;
			break;
		case lengthModifierLL:
			*va_arg(ctx->arguments, long long *) =
			    (long long)ctx->bufferLen;
			break;
		case lengthModifierJ:
			*va_arg(ctx->arguments, intmax_t *) =
			    (intmax_t)ctx->bufferLen;
			break;
		case lengthModifierZ:
			*va_arg(ctx->arguments, size_t *) =
			    (size_t)ctx->bufferLen;
			break;
		case lengthModifierT:
			*va_arg(ctx->arguments, ptrdiff_t *) =
			    (ptrdiff_t)ctx->bufferLen;
			break;
		default:
			return false;
		}

		break;
	case '%':
		if (ctx->lengthModifier != lengthModifierNone)
			return false;

		if (!appendString(ctx, "%", 1))
			return false;

		break;
	default:
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
			free(tmp);
			return false;
		}

		free(tmp);
	}

	memset(ctx->subformat, 0, MAX_SUBFORMAT_LEN);
	ctx->subformatLen = 0;
	ctx->lengthModifier = LENGTH_MODIFIER_NONE;
	ctx->useLocale = false;

	ctx->last = ctx->i + 1;
	ctx->state = STATE_STRING;

	return true;
}

static bool (*states[])(struct context *) = {
	stringState,
	formatFlagsState,
	formatFieldWidthState,
	formatLengthModifierState,
	formatConversionSpecifierState
};

int
of_vasprintf(char **string, const char *format, va_list arguments)
{
	struct context ctx;

	ctx.format = format;
	ctx.formatLen = strlen(format);
	memset(ctx.subformat, 0, MAX_SUBFORMAT_LEN + 1);
	ctx.subformatLen = 0;
	va_copy(ctx.arguments, arguments);
	ctx.bufferLen = 0;
	ctx.last = 0;
	ctx.state = STATE_STRING;
	ctx.lengthModifier = LENGTH_MODIFIER_NONE;
	ctx.useLocale = false;

	if ((ctx.buffer = malloc(1)) == NULL)
		return -1;

	for (ctx.i = 0; ctx.i < ctx.formatLen; ctx.i++) {
		if (!states[ctx.state](&ctx)) {
			free(ctx.buffer);
			return -1;
		}
	}

	if (ctx.state != STATE_STRING) {
		free(ctx.buffer);
		return -1;
	}

	if (!appendString(&ctx, ctx.format + ctx.last,
	    ctx.formatLen - ctx.last)) {
		free(ctx.buffer);
		return -1;
	}

	ctx.buffer[ctx.bufferLen] = 0;

	*string = ctx.buffer;
	return (ctx.bufferLen <= INT_MAX ? (int)ctx.bufferLen : -1);
}

int
of_asprintf(char **string, const char *format, ...)
{
	va_list arguments;
	int ret;

	va_start(arguments, format);
	ret = of_vasprintf(string, format, arguments);
	va_end(arguments);

	return ret;
}







|

|



|




|








|

|



|




|
|












|















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













			free(tmp);
			return false;
		}

		free(tmp);
	}

	memset(ctx->subformat, 0, maxSubformatLen);
	ctx->subformatLen = 0;
	ctx->lengthModifier = lengthModifierNone;
	ctx->useLocale = false;

	ctx->last = ctx->i + 1;
	ctx->state = stateString;

	return true;
}

static bool (*states[])(struct Context *) = {
	stringState,
	formatFlagsState,
	formatFieldWidthState,
	formatLengthModifierState,
	formatConversionSpecifierState
};

int
OFVASPrintF(char **string, const char *format, va_list arguments)
{
	struct Context ctx;

	ctx.format = format;
	ctx.formatLen = strlen(format);
	memset(ctx.subformat, 0, maxSubformatLen + 1);
	ctx.subformatLen = 0;
	va_copy(ctx.arguments, arguments);
	ctx.bufferLen = 0;
	ctx.last = 0;
	ctx.state = stateString;
	ctx.lengthModifier = lengthModifierNone;
	ctx.useLocale = false;

	if ((ctx.buffer = malloc(1)) == NULL)
		return -1;

	for (ctx.i = 0; ctx.i < ctx.formatLen; ctx.i++) {
		if (!states[ctx.state](&ctx)) {
			free(ctx.buffer);
			return -1;
		}
	}

	if (ctx.state != stateString) {
		free(ctx.buffer);
		return -1;
	}

	if (!appendString(&ctx, ctx.format + ctx.last,
	    ctx.formatLen - ctx.last)) {
		free(ctx.buffer);
		return -1;
	}

	ctx.buffer[ctx.bufferLen] = 0;

	*string = ctx.buffer;
	return (ctx.bufferLen <= INT_MAX ? (int)ctx.bufferLen : -1);
}













Modified src/OFAdjacentArray.h from [4bd8d9c907] to [9facc27db0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFAdjacentArray.m from [b6ccf04769] to [e305e33733].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	self = [self init];

	@try {
		id object;

		[_array addItem: &firstObject];







|
<







57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	self = [self init];

	@try {
		id object;

		[_array addItem: &firstObject];
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
		@throw e;
	}

	@try {
		for (size_t i = 0; i < count; i++)
			[objects[i] retain];

		[_array addItems: objects
			   count: count];
	} @catch (id e) {
		for (size_t i = 0; i < count; i++)
			[objects[i] release];

		/* Prevent double-release of objects */
		[_array release];
		_array = nil;

		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	self = [self init];

	@try {
		bool ok = true;

		for (size_t i = 0; i < count; i++) {
			if (objects[i] == nil)
				ok = false;

			[objects[i] retain];
		}

		if (!ok)
			@throw [OFInvalidArgumentException exception];

		[_array addItems: objects
			   count: count];
	} @catch (id e) {
		for (size_t i = 0; i < count; i++)
			[objects[i] release];

		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if ((![element.name isEqual: @"OFArray"] &&
		    ![element.name isEqual: @"OFMutableArray"]) ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		for (OFXMLElement *child in
		    [element elementsForNamespace: OF_SERIALIZATION_NS]) {
			void *pool2 = objc_autoreleasePoolPush();
			id object;

			object = child.objectByDeserializing;
			[_array addItem: &object];
			[object retain];








|
<















|
<
















|
<




















|



|







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
		@throw e;
	}

	@try {
		for (size_t i = 0; i < count; i++)
			[objects[i] retain];

		[_array addItems: objects count: count];

	} @catch (id e) {
		for (size_t i = 0; i < count; i++)
			[objects[i] release];

		/* Prevent double-release of objects */
		[_array release];
		_array = nil;

		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	self = [self init];

	@try {
		bool ok = true;

		for (size_t i = 0; i < count; i++) {
			if (objects[i] == nil)
				ok = false;

			[objects[i] retain];
		}

		if (!ok)
			@throw [OFInvalidArgumentException exception];

		[_array addItems: objects count: count];

	} @catch (id e) {
		for (size_t i = 0; i < count; i++)
			[objects[i] release];

		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if ((![element.name isEqual: @"OFArray"] &&
		    ![element.name isEqual: @"OFMutableArray"]) ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		for (OFXMLElement *child in
		    [element elementsForNamespace: OFSerializationNS]) {
			void *pool2 = objc_autoreleasePoolPush();
			id object;

			object = child.objectByDeserializing;
			[_array addItem: &object];
			[object retain];

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
}

- (id)objectAtIndexedSubscript: (size_t)idx
{
	return *((id *)[_array itemAtIndex: idx]);
}

- (void)getObjects: (id *)buffer
	   inRange: (of_range_t)range
{
	id const *objects = _array.items;
	size_t count = _array.count;

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

	for (size_t i = 0; i < range.length; i++)
		buffer[i] = objects[range.location + i];
}

- (size_t)indexOfObject: (id)object
{
	id const *objects;
	size_t count;

	if (object == nil)
		return OF_NOT_FOUND;

	objects = _array.items;
	count = _array.count;

	for (size_t i = 0; i < count; i++)
		if ([objects[i] isEqual: object])
			return i;

	return OF_NOT_FOUND;
}

- (size_t)indexOfObjectIdenticalTo: (id)object
{
	id const *objects;
	size_t count;

	if (object == nil)
		return OF_NOT_FOUND;

	objects = _array.items;
	count = _array.count;

	for (size_t i = 0; i < count; i++)
		if (objects[i] == object)
			return i;

	return OF_NOT_FOUND;
}


- (OFArray *)objectsInRange: (of_range_t)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _array.count)
		@throw [OFOutOfRangeException exception];

	if ([self isKindOfClass: [OFMutableArray class]])
		return [OFArray
		    arrayWithObjects: (id *)_array.items + range.location
			       count: range.length];

	return [OFAdjacentSubarray arrayWithArray: self
					    range: range];
}

- (bool)isEqual: (id)object
{
	OFArray *otherArray;
	id const *objects, *otherObjects;
	size_t count;







|
<


















|








|








|








|



|










|
<







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
}

- (id)objectAtIndexedSubscript: (size_t)idx
{
	return *((id *)[_array itemAtIndex: idx]);
}

- (void)getObjects: (id *)buffer inRange: (OFRange)range

{
	id const *objects = _array.items;
	size_t count = _array.count;

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

	for (size_t i = 0; i < range.length; i++)
		buffer[i] = objects[range.location + i];
}

- (size_t)indexOfObject: (id)object
{
	id const *objects;
	size_t count;

	if (object == nil)
		return OFNotFound;

	objects = _array.items;
	count = _array.count;

	for (size_t i = 0; i < count; i++)
		if ([objects[i] isEqual: object])
			return i;

	return OFNotFound;
}

- (size_t)indexOfObjectIdenticalTo: (id)object
{
	id const *objects;
	size_t count;

	if (object == nil)
		return OFNotFound;

	objects = _array.items;
	count = _array.count;

	for (size_t i = 0; i < count; i++)
		if (objects[i] == object)
			return i;

	return OFNotFound;
}


- (OFArray *)objectsInRange: (OFRange)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _array.count)
		@throw [OFOutOfRangeException exception];

	if ([self isKindOfClass: [OFMutableArray class]])
		return [OFArray
		    arrayWithObjects: (id *)_array.items + range.location
			       count: range.length];

	return [OFAdjacentSubarray arrayWithArray: self range: range];

}

- (bool)isEqual: (id)object
{
	OFArray *otherArray;
	id const *objects, *otherObjects;
	size_t count;
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
	return true;
}

- (unsigned long)hash
{
	id const *objects = _array.items;
	size_t count = _array.count;
	uint32_t hash;

	OF_HASH_INIT(hash);

	for (size_t i = 0; i < count; i++)
		OF_HASH_ADD_HASH(hash, [objects[i] hash]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count_
{
	size_t count = _array.count;

	if (count > INT_MAX)
		/*







|

|


|

|




|







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
	return true;
}

- (unsigned long)hash
{
	id const *objects = _array.items;
	size_t count = _array.count;
	unsigned long hash;

	OFHashInit(&hash);

	for (size_t i = 0; i < count; i++)
		OFHashAddHash(&hash, [objects[i] hash]);

	OFHashFinalize(&hash);

	return hash;
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count_
{
	size_t count = _array.count;

	if (count > INT_MAX)
		/*
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
	state->itemsPtr = (id *)_array.items;
	state->mutationsPtr = (unsigned long *)self;

	return (int)count;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (of_array_enumeration_block_t)block
{
	id const *objects = _array.items;
	size_t count = _array.count;
	bool stop = false;

	for (size_t i = 0; i < count && !stop; i++)
		block(objects[i], i, &stop);







|







338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
	state->itemsPtr = (id *)_array.items;
	state->mutationsPtr = (unsigned long *)self;

	return (int)count;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (OFArrayEnumerationBlock)block
{
	id const *objects = _array.items;
	size_t count = _array.count;
	bool stop = false;

	for (size_t i = 0; i < count && !stop; i++)
		block(objects[i], i, &stop);

Modified src/OFAdjacentSubarray.h from [577fbbfc6a] to [08f4f942e1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFAdjacentSubarray.m from [dfdd4736b7] to [76d5c349a9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
		if (![objects[i] isEqual: otherObjects[i]])
			return false;

	return true;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (of_array_enumeration_block_t)block
{
	id const *objects = self.objects;
	bool stop = false;

	for (size_t i = 0; i < _range.length && !stop; i++)
		block(objects[i], i, &stop);
}
#endif
@end







|









49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
		if (![objects[i] isEqual: otherObjects[i]])
			return false;

	return true;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (OFArrayEnumerationBlock)block
{
	id const *objects = self.objects;
	bool stop = false;

	for (size_t i = 0; i < _range.length && !stop; i++)
		block(objects[i], i, &stop);
}
#endif
@end

Modified src/OFApplication.h from [a2a9803d24] to [d7b817478b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <signal.h>

#import "OFObject.h"


OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFSandbox;
@class OFString;






/**
 * @brief Specify the class to be used as the application delegate.
 *
 * An instance of this class will be created and act as the application
 * delegate.
 *
 * For example, it can be used like this:

<
<
|
















>












>
>
>
>
>







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


 * 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 <signal.h>

#import "OFObject.h"
#import "OFNotification.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFSandbox;
@class OFString;

/**
 * @brief A notification that will be sent when the application will terminate.
 */
extern const OFNotificationName OFApplicationWillTerminateNotification;

/**
 * @brief Specify the class to be used as the application delegate.
 *
 * An instance of this class will be created and act as the application
 * delegate.
 *
 * For example, it can be used like this:
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 * - (void)applicationDidFinishLaunching
 * {
 *         [OFApplication terminate];
 * }
 * @end
 * @endcode
 */
#define OF_APPLICATION_DELEGATE(class_)					\
	int								\
	main(int argc, char *argv[])					\
	{								\
		return of_application_main(&argc, &argv,		\
		    (class_ *)[[class_ alloc] init]);			\
	}

#ifdef OF_HAVE_PLEDGE
# define OF_HAVE_SANDBOX
#endif

/**







|
|
|
|
|
|







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
 * - (void)applicationDidFinishLaunching
 * {
 *         [OFApplication terminate];
 * }
 * @end
 * @endcode
 */
#define OF_APPLICATION_DELEGATE(class_)			\
	int						\
	main(int argc, char *argv[])			\
	{						\
		return OFApplicationMain(&argc, &argv,	\
		    (class_ *)[[class_ alloc] init]);	\
	}

#ifdef OF_HAVE_PLEDGE
# define OF_HAVE_SANDBOX
#endif

/**
143
144
145
146
147
148
149




150
151
152
153
154
155
156
 * @class OFApplication OFApplication.h ObjFW/OFApplication.h
 *
 * @brief A class which represents the application as an object.
 *
 * In order to create a new OFApplication, you should create a class conforming
 * to the optional @ref OFApplicationDelegate protocol and put
 * `OF_APPLICATION_DELEGATE(NameOfYourClass)` in the .m file of that class.




 */
OF_SUBCLASSING_RESTRICTED
@interface OFApplication: OFObject
{
	OFString *_programName;
	OFArray OF_GENERIC(OFString *) *_arguments;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *_environment;







>
>
>
>







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
 * @class OFApplication OFApplication.h ObjFW/OFApplication.h
 *
 * @brief A class which represents the application as an object.
 *
 * In order to create a new OFApplication, you should create a class conforming
 * to the optional @ref OFApplicationDelegate protocol and put
 * `OF_APPLICATION_DELEGATE(NameOfYourClass)` in the .m file of that class.
 *
 * When the application is about to be terminated,
 * @ref OFApplicationDelegate#applicationWillTerminate will be called on the
 * delegate and an @ref OFApplicationWillTerminateNotification will be sent.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFApplication: OFObject
{
	OFString *_programName;
	OFArray OF_GENERIC(OFString *) *_arguments;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *_environment;
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/**
 * @brief The delegate of the application.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFApplicationDelegate> delegate;

#ifdef OF_HAVE_SANDBOX
/**
 * @brief The sandbox currently active for this application.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFSandbox *activeSandbox;

/**
 * @brief The sandbox currently active for child processes of this application.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
    OFSandbox *activeSandboxForChildProcesses;
#endif

/**
 * @brief Returns the only OFApplication instance in the application.
 *







<
<
<

<
<
<
<







206
207
208
209
210
211
212



213




214
215
216
217
218
219
220
/**
 * @brief The delegate of the application.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFApplicationDelegate> delegate;

#ifdef OF_HAVE_SANDBOX



@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFSandbox *activeSandbox;




@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
    OFSandbox *activeSandboxForChildProcesses;
#endif

/**
 * @brief Returns the only OFApplication instance in the application.
 *
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
 * @brief Terminates the application with the specified status.
 *
 * @param status The status with which the application will terminate
 */
+ (void)terminateWithStatus: (int)status OF_NO_RETURN;

#ifdef OF_HAVE_SANDBOX
/**
 * @brief Activates the specified sandbox for the application.
 *
 * This is only available if `OF_HAVE_SANDBOX` is defined.
 *
 * @warning If you allow `exec()`, but do not call
 *	    @ref activateSandboxForChildProcesses:, an `exec()`'d process does
 *	    not have its permissions restricted!
 *
 * @note Once a sandbox has been activated, you cannot activate a different
 *	 sandbox. You can however change the active sandbox and reactivate it.
 *
 * @param sandbox The sandbox to activate
 */
+ (void)activateSandbox: (OFSandbox *)sandbox;

/**
 * @brief Activates the specified sandbox for child processes of the
 *	  application.
 *
 * This is only available if `OF_HAVE_SANDBOX` is defined.
 *
 * `unveiledPaths` on the sandbox must *not* be empty, otherwise an
 * @ref OFInvalidArgumentException is raised.
 *
 * @note Once a sandbox has been activated, you cannot activate a different
 *	 sandbox. You can however change the active sandbox and reactivate it.
 *
 * @param sandbox The sandbox to activate
 */
+ (void)activateSandboxForChildProcesses: (OFSandbox *)sandbox;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Gets argc and argv.
 *







<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|







252
253
254
255
256
257
258














259















260
261
262
263
264
265
266
267
 * @brief Terminates the application with the specified status.
 *
 * @param status The status with which the application will terminate
 */
+ (void)terminateWithStatus: (int)status OF_NO_RETURN;

#ifdef OF_HAVE_SANDBOX














+ (void)of_activateSandbox: (OFSandbox *)sandbox;















+ (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Gets argc and argv.
 *
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
 * @brief Terminates the application with the specified status.
 *
 * @param status The status with which the application will terminate
 */
- (void)terminateWithStatus: (int)status OF_NO_RETURN;

#ifdef OF_HAVE_SANDBOX
/**
 * @brief Activates the specified sandbox for the application.
 *
 * This is only available if `OF_HAVE_SANDBOX` is defined.
 *
 * @warning If you allow `exec()`, but do not call
 *	    @ref activateSandboxForChildProcesses:, an `exec()`'d process does
 *	    not have its permissions restricted!
 *
 * @note Once a sandbox has been activated, you cannot activate a different
 *	 sandbox. You can however change the active sandbox and reactivate it.
 *
 * @param sandbox The sandbox to activate
 */
- (void)activateSandbox: (OFSandbox *)sandbox;

/**
 * @brief Activates the specified sandbox for child processes of the
 *	  application.
 *
 * This is only available if `OF_HAVE_SANDBOX` is defined.
 *
 * `unveiledPaths` on the sandbox must *not* be empty, otherwise an
 * @ref OFInvalidArgumentException is raised.
 *
 * @note Once a sandbox has been activated, you cannot activate a different
 *	 sandbox. You can however change the active sandbox and reactivate it.
 *
 * @param sandbox The sandbox to activate
 */
- (void)activateSandboxForChildProcesses: (OFSandbox *)sandbox;
#endif
@end

#ifdef __cplusplus
extern "C" {
#endif
extern int of_application_main(int *_Nonnull,
    char *_Nullable *_Nonnull[_Nonnull], id <OFApplicationDelegate>);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|






|
|





280
281
282
283
284
285
286














287















288
289
290
291
292
293
294
295
296
297
298
299
300
301
 * @brief Terminates the application with the specified status.
 *
 * @param status The status with which the application will terminate
 */
- (void)terminateWithStatus: (int)status OF_NO_RETURN;

#ifdef OF_HAVE_SANDBOX














- (void)of_activateSandbox: (OFSandbox *)sandbox;















- (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox;
#endif
@end

#ifdef __cplusplus
extern "C" {
#endif
extern int OFApplicationMain(int *_Nonnull, char *_Nullable *_Nonnull[_Nonnull],
    id <OFApplicationDelegate>);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFApplication.m from [ba8b4a32b1] to [bde88848cd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
30
31
32
33
34
35
36

37
38
39
40
41
42
43
#import "OFArray.h"
#import "OFDictionary.h"
#ifdef OF_AMIGAOS
# import "OFFile.h"
# import "OFFileManager.h"
#endif
#import "OFLocale.h"

#import "OFPair.h"
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"
#import "OFSandbox.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFThread+Private.h"







>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#import "OFArray.h"
#import "OFDictionary.h"
#ifdef OF_AMIGAOS
# import "OFFile.h"
# import "OFFileManager.h"
#endif
#import "OFLocale.h"
#import "OFNotificationCenter.h"
#import "OFPair.h"
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"
#import "OFSandbox.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFThread+Private.h"
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
# include <nds.h>
# undef asm
#endif

OF_DIRECT_MEMBERS
@interface OFApplication ()
- (instancetype)of_init OF_METHOD_FAMILY(init);
- (void)of_setArgumentCount: (int *)argc
	  andArgumentValues: (char **[])argv;
#ifdef OF_WINDOWS
- (void)of_setArgumentCount: (int)argc


      andWideArgumentValues: (wchar_t *[])argv;
#endif
- (void)of_run;
@end



static OFApplication *app = nil;

static void
atexitHandler(void)
{
	id <OFApplicationDelegate> delegate = app.delegate;





	if ([delegate respondsToSelector: @selector(applicationWillTerminate)])
		[delegate applicationWillTerminate];

	[delegate release];

#if defined(OF_HAVE_THREADS) && defined(OF_HAVE_SOCKETS) && defined(OF_AMIGAOS)

	of_socket_deinit();
#endif
}

int
of_application_main(int *argc, char **argv[],
    id <OFApplicationDelegate> delegate)
{
#ifdef OF_WINDOWS
	wchar_t **wargv, **wenvp;
	int wargc, si = 0;
#endif

	[[OFLocale alloc] init];

	app = [[OFApplication alloc] of_init];

#ifdef OF_WINDOWS
	if ([OFSystemInfo isWindowsNT]) {
		__wgetmainargs(&wargc, &wargv, &wenvp, _CRT_glob, &si);
		[app of_setArgumentCount: wargc


		   andWideArgumentValues: wargv];
	} else
#endif
		[app of_setArgumentCount: argc
		       andArgumentValues: argv];

	app.delegate = delegate;

	[app of_run];

	[delegate release];








|
<

|
>
>
|




>
>






>
>
>
>






|
>
|




<
|













|
>
>



|
<







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
# include <nds.h>
# undef asm
#endif

OF_DIRECT_MEMBERS
@interface OFApplication ()
- (instancetype)of_init OF_METHOD_FAMILY(init);
- (void)of_setArgumentCount: (int *)argc andArgumentValues: (char **[])argv;

#ifdef OF_WINDOWS
- (void)of_setArgumentCount: (int *)argc
	  andArgumentValues: (char **[])argv
       andWideArgumentCount: (int)wargc
      andWideArgumentValues: (wchar_t *[])wargv;
#endif
- (void)of_run;
@end

const OFNotificationName OFApplicationWillTerminateNotification =
    @"OFApplicationWillTerminateNotification";
static OFApplication *app = nil;

static void
atexitHandler(void)
{
	id <OFApplicationDelegate> delegate = app.delegate;

	[[OFNotificationCenter defaultCenter]
	    postNotificationName: OFApplicationWillTerminateNotification
			  object: app];

	if ([delegate respondsToSelector: @selector(applicationWillTerminate)])
		[delegate applicationWillTerminate];

	[delegate release];

#if defined(OF_HAVE_THREADS) && defined(OF_HAVE_SOCKETS) && \
    defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
	OFSocketDeinit();
#endif
}

int

OFApplicationMain(int *argc, char **argv[], id <OFApplicationDelegate> delegate)
{
#ifdef OF_WINDOWS
	wchar_t **wargv, **wenvp;
	int wargc, si = 0;
#endif

	[[OFLocale alloc] init];

	app = [[OFApplication alloc] of_init];

#ifdef OF_WINDOWS
	if ([OFSystemInfo isWindowsNT]) {
		__wgetmainargs(&wargc, &wargv, &wenvp, _CRT_glob, &si);
		[app of_setArgumentCount: argc
		       andArgumentValues: argv
		    andWideArgumentCount: wargc
		   andWideArgumentValues: wargv];
	} else
#endif
		[app of_setArgumentCount: argc andArgumentValues: argv];


	app.delegate = delegate;

	[app of_run];

	[delegate release];

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
	sceKernelExitGame();

	OF_UNREACHABLE
#endif
}

#ifdef OF_HAVE_SANDBOX
+ (void)activateSandbox: (OFSandbox *)sandbox
{
	[app activateSandbox: sandbox];
}

+ (void)activateSandboxForChildProcesses: (OFSandbox *)sandbox
{
	[app activateSandboxForChildProcesses: sandbox];
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_init
{
	self = [super init];

	@try {
		_environment = [[OFMutableDictionary alloc] init];

		atexit(atexitHandler);

#if defined(OF_WINDOWS)
		if ([OFSystemInfo isWindowsNT]) {
			of_char16_t *env, *env0;
			env = env0 = GetEnvironmentStringsW();

			while (*env != 0) {
				void *pool = objc_autoreleasePoolPush();
				OFString *tmp, *key, *value;
				size_t length, pos;

				length = of_string_utf16_length(env);
				tmp = [OFString stringWithUTF16String: env
							       length: length];
				env += length + 1;

				/*
				 * cmd.exe seems to add some special variables
				 * which start with a "=", even though variable
				 * names are not allowed to contain a "=".
				 */
				if ([tmp hasPrefix: @"="]) {
					objc_autoreleasePoolPop(pool);
					continue;
				}

				pos = [tmp rangeOfString: @"="].location;
				if (pos == OF_NOT_FOUND) {
					fprintf(stderr,
					    "Warning: Invalid environment "
					    "variable: %s\n", tmp.UTF8String);
					continue;
				}

				key = [tmp substringToIndex: pos];
				value = [tmp substringFromRange: pos + 1];

				[_environment setObject: value
						 forKey: key];

				objc_autoreleasePoolPop(pool);
			}

			FreeEnvironmentStringsW(env0);
		} else {
			char *env, *env0;







|

|


|

|



















|







|















|







|
<
|
<







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
	sceKernelExitGame();

	OF_UNREACHABLE
#endif
}

#ifdef OF_HAVE_SANDBOX
+ (void)of_activateSandbox: (OFSandbox *)sandbox
{
	[app of_activateSandbox: sandbox];
}

+ (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox
{
	[app of_activateSandboxForChildProcesses: sandbox];
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_init
{
	self = [super init];

	@try {
		_environment = [[OFMutableDictionary alloc] init];

		atexit(atexitHandler);

#if defined(OF_WINDOWS)
		if ([OFSystemInfo isWindowsNT]) {
			OFChar16 *env, *env0;
			env = env0 = GetEnvironmentStringsW();

			while (*env != 0) {
				void *pool = objc_autoreleasePoolPush();
				OFString *tmp, *key, *value;
				size_t length, pos;

				length = OFUTF16StringLength(env);
				tmp = [OFString stringWithUTF16String: env
							       length: length];
				env += length + 1;

				/*
				 * cmd.exe seems to add some special variables
				 * which start with a "=", even though variable
				 * names are not allowed to contain a "=".
				 */
				if ([tmp hasPrefix: @"="]) {
					objc_autoreleasePoolPop(pool);
					continue;
				}

				pos = [tmp rangeOfString: @"="].location;
				if (pos == OFNotFound) {
					fprintf(stderr,
					    "Warning: Invalid environment "
					    "variable: %s\n", tmp.UTF8String);
					continue;
				}

				key = [tmp substringToIndex: pos];
				value = [tmp substringFromIndex: pos + 1];

				[_environment setObject: value forKey: key];


				objc_autoreleasePoolPop(pool);
			}

			FreeEnvironmentStringsW(env0);
		} else {
			char *env, *env0;
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
				 */
				if ([tmp hasPrefix: @"="]) {
					objc_autoreleasePoolPop(pool);
					continue;
				}

				pos = [tmp rangeOfString: @"="].location;
				if (pos == OF_NOT_FOUND) {
					fprintf(stderr,
					    "Warning: Invalid environment "
					    "variable: %s\n", tmp.UTF8String);
					continue;
				}

				key = [tmp substringToIndex: pos];
				value = [tmp substringFromIndex: pos + 1];

				[_environment setObject: value
						 forKey: key];

				objc_autoreleasePoolPop(pool);
			}

			FreeEnvironmentStringsA(env0);
		}
#elif defined(OF_AMIGAOS)
		void *pool = objc_autoreleasePoolPush();
		OFFileManager *fileManager = [OFFileManager defaultManager];
		OFArray *envContents =
		    [fileManager contentsOfDirectoryAtPath: @"ENV:"];
		const of_string_encoding_t encoding = [OFLocale encoding];
		struct Process *proc;
		struct LocalVar *firstLocalVar;

		for (OFString *name in envContents) {
			void *pool2 = objc_autoreleasePoolPush();
			OFString *path, *value;
			OFFile *file;

			if ([name containsString: @"."])
				continue;

			path = [@"ENV:" stringByAppendingString: name];

			if ([fileManager directoryExistsAtPath: path])
				continue;

			file = [OFFile fileWithPath: path
					       mode: @"r"];

			value = [file readLineWithEncoding: encoding];
			if (value != nil)
				[_environment setObject: value
						 forKey: name];

			objc_autoreleasePoolPop(pool2);
		}

		/* Local variables override global variables */
		proc = (struct Process *)FindTask(NULL);
		firstLocalVar = (struct LocalVar *)proc->pr_LocalVars.mlh_Head;







|








<
|
<











|
















|
<



|
<







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
				 */
				if ([tmp hasPrefix: @"="]) {
					objc_autoreleasePoolPop(pool);
					continue;
				}

				pos = [tmp rangeOfString: @"="].location;
				if (pos == OFNotFound) {
					fprintf(stderr,
					    "Warning: Invalid environment "
					    "variable: %s\n", tmp.UTF8String);
					continue;
				}

				key = [tmp substringToIndex: pos];
				value = [tmp substringFromIndex: pos + 1];

				[_environment setObject: value forKey: key];


				objc_autoreleasePoolPop(pool);
			}

			FreeEnvironmentStringsA(env0);
		}
#elif defined(OF_AMIGAOS)
		void *pool = objc_autoreleasePoolPush();
		OFFileManager *fileManager = [OFFileManager defaultManager];
		OFArray *envContents =
		    [fileManager contentsOfDirectoryAtPath: @"ENV:"];
		OFStringEncoding encoding = [OFLocale encoding];
		struct Process *proc;
		struct LocalVar *firstLocalVar;

		for (OFString *name in envContents) {
			void *pool2 = objc_autoreleasePoolPush();
			OFString *path, *value;
			OFFile *file;

			if ([name containsString: @"."])
				continue;

			path = [@"ENV:" stringByAppendingString: name];

			if ([fileManager directoryExistsAtPath: path])
				continue;

			file = [OFFile fileWithPath: path mode: @"r"];


			value = [file readLineWithEncoding: encoding];
			if (value != nil)
				[_environment setObject: value forKey: name];


			objc_autoreleasePoolPop(pool2);
		}

		/* Local variables override global variables */
		proc = (struct Process *)FindTask(NULL);
		firstLocalVar = (struct LocalVar *)proc->pr_LocalVars.mlh_Head;
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

			key = [OFString stringWithCString: iter->lv_Node.ln_Name
						 encoding: encoding];
			value = [OFString
			    stringWithCString: (const char *)iter->lv_Value
				     encoding: encoding
				       length: length];

			[_environment setObject: value
					 forKey: key];
		}

		objc_autoreleasePoolPop(pool);
#elif !defined(OF_IOS)
# ifndef OF_MACOS
		char **env = environ;
# else
		char **env = *_NSGetEnviron();
# endif

		if (env != NULL) {
			const of_string_encoding_t encoding =
			    [OFLocale encoding];

			for (; *env != NULL; env++) {
				void *pool = objc_autoreleasePoolPush();
				OFString *key, *value;
				char *sep;

				if ((sep = strchr(*env, '=')) == NULL) {
					fprintf(stderr, "Warning: Invalid "
					    "environment variable: %s\n", *env);
					continue;
				}

				key = [OFString
				    stringWithCString: *env
					     encoding: encoding
					       length: sep - *env];
				value = [OFString
				    stringWithCString: sep + 1
					     encoding: encoding];

				[_environment setObject: value
						 forKey: key];

				objc_autoreleasePoolPop(pool);
			}
		}
#else
		/*
		 * iOS does not provide environ and Apple does not allow using
		 * _NSGetEnviron on iOS. Therefore, we just get a few common
		 * variables from the environment which applications might
		 * expect.
		 */

		void *pool = objc_autoreleasePoolPush();
		char *env;

		if ((env = getenv("HOME")) != NULL) {
			OFString *home = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: home
					 forKey: @"HOME"];
		}
		if ((env = getenv("PATH")) != NULL) {
			OFString *path = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: path
					 forKey: @"PATH"];
		}
		if ((env = getenv("SHELL")) != NULL) {
			OFString *shell = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: shell
					 forKey: @"SHELL"];
		}
		if ((env = getenv("TMPDIR")) != NULL) {
			OFString *tmpdir = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: tmpdir
					 forKey: @"TMPDIR"];
		}
		if ((env = getenv("USER")) != NULL) {
			OFString *user = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: user
					 forKey: @"USER"];
		}

		objc_autoreleasePoolPop(pool);
#endif

		[_environment makeImmutable];
	} @catch (id e) {







<
|
<











<
|



















<
|
<



















|
<





|
<





|
<





|
<





|
<







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

			key = [OFString stringWithCString: iter->lv_Node.ln_Name
						 encoding: encoding];
			value = [OFString
			    stringWithCString: (const char *)iter->lv_Value
				     encoding: encoding
				       length: length];

			[_environment setObject: value forKey: key];

		}

		objc_autoreleasePoolPop(pool);
#elif !defined(OF_IOS)
# ifndef OF_MACOS
		char **env = environ;
# else
		char **env = *_NSGetEnviron();
# endif

		if (env != NULL) {

			OFStringEncoding encoding = [OFLocale encoding];

			for (; *env != NULL; env++) {
				void *pool = objc_autoreleasePoolPush();
				OFString *key, *value;
				char *sep;

				if ((sep = strchr(*env, '=')) == NULL) {
					fprintf(stderr, "Warning: Invalid "
					    "environment variable: %s\n", *env);
					continue;
				}

				key = [OFString
				    stringWithCString: *env
					     encoding: encoding
					       length: sep - *env];
				value = [OFString
				    stringWithCString: sep + 1
					     encoding: encoding];

				[_environment setObject: value forKey: key];


				objc_autoreleasePoolPop(pool);
			}
		}
#else
		/*
		 * iOS does not provide environ and Apple does not allow using
		 * _NSGetEnviron on iOS. Therefore, we just get a few common
		 * variables from the environment which applications might
		 * expect.
		 */

		void *pool = objc_autoreleasePoolPush();
		char *env;

		if ((env = getenv("HOME")) != NULL) {
			OFString *home = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: home forKey: @"HOME"];

		}
		if ((env = getenv("PATH")) != NULL) {
			OFString *path = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: path forKey: @"PATH"];

		}
		if ((env = getenv("SHELL")) != NULL) {
			OFString *shell = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: shell forKey: @"SHELL"];

		}
		if ((env = getenv("TMPDIR")) != NULL) {
			OFString *tmpdir = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: tmpdir forKey: @"TMPDIR"];

		}
		if ((env = getenv("USER")) != NULL) {
			OFString *user = [[[OFString alloc]
			    initWithUTF8StringNoCopy: env
					freeWhenDone: false] autorelease];
			[_environment setObject: user forKey: @"USER"];

		}

		objc_autoreleasePoolPop(pool);
#endif

		[_environment makeImmutable];
	} @catch (id e) {
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
{
	[_arguments release];
	[_environment release];

	[super dealloc];
}

- (void)of_setArgumentCount: (int *)argc
	  andArgumentValues: (char ***)argv
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *arguments;
	of_string_encoding_t encoding;

	_argc = argc;
	_argv = argv;

	encoding = [OFLocale encoding];

#ifndef OF_NINTENDO_DS







|
<



|







471
472
473
474
475
476
477
478

479
480
481
482
483
484
485
486
487
488
489
{
	[_arguments release];
	[_environment release];

	[super dealloc];
}

- (void)of_setArgumentCount: (int *)argc andArgumentValues: (char **[])argv

{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *arguments;
	OFStringEncoding encoding;

	_argc = argc;
	_argv = argv;

	encoding = [OFLocale encoding];

#ifndef OF_NINTENDO_DS
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
		[arguments makeImmutable];
	}

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_WINDOWS
- (void)of_setArgumentCount: (int)argc


      andWideArgumentValues: (wchar_t **)argv
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *arguments;




	if (argc > 0) {
		_programName = [[OFString alloc] initWithUTF16String: argv[0]];
		arguments = [[OFMutableArray alloc] init];

		for (int i = 1; i < argc; i++)
			[arguments addObject:
			    [OFString stringWithUTF16String: argv[i]]];

		[arguments makeImmutable];
		_arguments = arguments;
	}

	objc_autoreleasePoolPop(pool);
}
#endif

- (void)getArgumentCount: (int **)argc
       andArgumentValues: (char ****)argv
{
	*argc = _argc;
	*argv = _argv;
}

- (id <OFApplicationDelegate>)delegate
{







|
>
>
|




>
>
>
|
|


|

|









|
<







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
		[arguments makeImmutable];
	}

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_WINDOWS
- (void)of_setArgumentCount: (int *)argc
	  andArgumentValues: (char **[])argv
       andWideArgumentCount: (int)wargc
      andWideArgumentValues: (wchar_t *[])wargv
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *arguments;

	_argc = argc;
	_argv = argv;

	if (wargc > 0) {
		_programName = [[OFString alloc] initWithUTF16String: wargv[0]];
		arguments = [[OFMutableArray alloc] init];

		for (int i = 1; i < wargc; i++)
			[arguments addObject:
			    [OFString stringWithUTF16String: wargv[i]]];

		[arguments makeImmutable];
		_arguments = arguments;
	}

	objc_autoreleasePoolPop(pool);
}
#endif

- (void)getArgumentCount: (int **)argc andArgumentValues: (char ****)argv

{
	*argc = _argc;
	*argv = _argv;
}

- (id <OFApplicationDelegate>)delegate
{
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
{
	[self.class terminateWithStatus: status];

	OF_UNREACHABLE
}

#ifdef OF_HAVE_SANDBOX
- (void)activateSandbox: (OFSandbox *)sandbox
{
# ifdef OF_HAVE_PLEDGE
	void *pool = objc_autoreleasePoolPush();
	of_string_encoding_t encoding = [OFLocale encoding];
	OFArray OF_GENERIC(of_sandbox_unveil_path_t) *unveiledPaths;
	size_t unveiledPathsCount;
	const char *promises;

	if (_activeSandbox != nil && sandbox != _activeSandbox)
		@throw [OFInvalidArgumentException exception];

	unveiledPaths = sandbox.unveiledPaths;
	unveiledPathsCount = unveiledPaths.count;

	for (size_t i = sandbox->_unveiledPathsIndex;
	    i < unveiledPathsCount; i++) {
		of_sandbox_unveil_path_t unveiledPath =
		    [unveiledPaths objectAtIndex: i];
		OFString *path = unveiledPath.firstObject;
		OFString *permissions = unveiledPath.secondObject;

		if (path == nil || permissions == nil)
			@throw [OFInvalidArgumentException exception];








|



|
|











|







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
{
	[self.class terminateWithStatus: status];

	OF_UNREACHABLE
}

#ifdef OF_HAVE_SANDBOX
- (void)of_activateSandbox: (OFSandbox *)sandbox
{
# ifdef OF_HAVE_PLEDGE
	void *pool = objc_autoreleasePoolPush();
	OFStringEncoding encoding = [OFLocale encoding];
	OFArray OF_GENERIC(OFSandboxUnveilPath) *unveiledPaths;
	size_t unveiledPathsCount;
	const char *promises;

	if (_activeSandbox != nil && sandbox != _activeSandbox)
		@throw [OFInvalidArgumentException exception];

	unveiledPaths = sandbox.unveiledPaths;
	unveiledPathsCount = unveiledPaths.count;

	for (size_t i = sandbox->_unveiledPathsIndex;
	    i < unveiledPathsCount; i++) {
		OFSandboxUnveilPath unveiledPath =
		    [unveiledPaths objectAtIndex: i];
		OFString *path = unveiledPath.firstObject;
		OFString *permissions = unveiledPath.secondObject;

		if (path == nil || permissions == nil)
			@throw [OFInvalidArgumentException exception];

667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
	objc_autoreleasePoolPop(pool);

	if (_activeSandbox == nil)
		_activeSandbox = [sandbox retain];
# endif
}

- (void)activateSandboxForChildProcesses: (OFSandbox *)sandbox
{
# ifdef OF_HAVE_PLEDGE
	void *pool = objc_autoreleasePoolPush();
	const char *promises;

	if (_activeSandboxForChildProcesses != nil &&
	    sandbox != _activeSandboxForChildProcesses)







|







661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
	objc_autoreleasePoolPop(pool);

	if (_activeSandbox == nil)
		_activeSandbox = [sandbox retain];
# endif
}

- (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox
{
# ifdef OF_HAVE_PLEDGE
	void *pool = objc_autoreleasePoolPush();
	const char *promises;

	if (_activeSandboxForChildProcesses != nil &&
	    sandbox != _activeSandboxForChildProcesses)

Modified src/OFArray+Private.h from [fad96984ad] to [9a457a5499].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFArray.h from [60511cb7de] to [f90f803ff9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFString;






enum {

	OF_ARRAY_SKIP_EMPTY = 1,

	OF_ARRAY_SORT_DESCENDING = 2
};










#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block for enumerating an OFArray.
 *
 * @param object The current object
 * @param index The index of the current object
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration
 */
typedef void (^of_array_enumeration_block_t)(id object, size_t index,
    bool *stop);

/**
 * @brief A block for filtering an OFArray.
 *
 * @param object The object to inspect
 * @param index The index of the object to inspect
 * @return Whether the object should be in the filtered array
 */
typedef bool (^of_array_filter_block_t)(id object, size_t index);

/**
 * @brief A block for mapping objects to objects in an OFArray.
 *
 * @param object The object to map
 * @param index The index of the object to map
 * @return The object to map to
 */
typedef id _Nonnull (^of_array_map_block_t)(id object, size_t index);

/**
 * @brief A block for folding an OFArray.
 *
 * @param left The object to which the object has been folded so far
 * @param right The object that should be added to the left object
 * @return The left and right side folded into one object
 */
typedef id _Nullable (^of_array_fold_block_t)(id _Nullable left, id right);
#endif

/**
 * @class OFArray OFArray.h ObjFW/OFArray.h
 *
 * @brief An abstract class for storing objects in an array.
 *







>
>
>
>
>
|
>
|
>
|
<
>
>
>
>
>
>
>
>
>










|
<








|








|








|







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

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFString;

/**
 * @brief Options for joining the objects of an array.
 *
 * This is a bit mask.
 */
typedef enum {
	/** Skip empty components */
	OFArraySkipEmptyComponents = 1
} OFArrayJoinOptions;


/**
 * @brief Options for sorting an array.
 *
 * This is a bit mask.
 */
typedef enum {
	/** Sort the array descending */
	OFArraySortDescending = 1
} OFArraySortOptions;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block for enumerating an OFArray.
 *
 * @param object The current object
 * @param index The index of the current object
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration
 */
typedef void (^OFArrayEnumerationBlock)(id object, size_t index, bool *stop);


/**
 * @brief A block for filtering an OFArray.
 *
 * @param object The object to inspect
 * @param index The index of the object to inspect
 * @return Whether the object should be in the filtered array
 */
typedef bool (^OFArrayFilterBlock)(id object, size_t index);

/**
 * @brief A block for mapping objects to objects in an OFArray.
 *
 * @param object The object to map
 * @param index The index of the object to map
 * @return The object to map to
 */
typedef id _Nonnull (^OFArrayMapBlock)(id object, size_t index);

/**
 * @brief A block for folding an OFArray.
 *
 * @param left The object to which the object has been folded so far
 * @param right The object that should be added to the left object
 * @return The left and right side folded into one object
 */
typedef id _Nullable (^OFArrayFoldBlock)(id _Nullable left, id right);
#endif

/**
 * @class OFArray OFArray.h ObjFW/OFArray.h
 *
 * @brief An abstract class for storing objects in an array.
 *
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
 * @brief Creates a new OFArray with the objects from the specified C array of
 *	  the specified length.
 *
 * @param objects A C array of objects
 * @param count The length of the C array
 * @return A new autoreleased OFArray
 */
+ (instancetype)
    arrayWithObjects: (ObjectType const _Nonnull *_Nonnull)objects
	       count: (size_t)count;

/**
 * @brief Initializes an OFArray with the specified object.
 *
 * @param object An object
 * @return An initialized OFArray
 */







<
|
|







178
179
180
181
182
183
184

185
186
187
188
189
190
191
192
193
 * @brief Creates a new OFArray with the objects from the specified C array of
 *	  the specified length.
 *
 * @param objects A C array of objects
 * @param count The length of the C array
 * @return A new autoreleased OFArray
 */

+ (instancetype)arrayWithObjects: (ObjectType const _Nonnull *_Nonnull)objects
			   count: (size_t)count;

/**
 * @brief Initializes an OFArray with the specified object.
 *
 * @param object An object
 * @return An initialized OFArray
 */
215
216
217
218
219
220
221







222
223
224
225
226
227
228
 * @param objects A C array of objects
 * @param count The length of the C array
 * @return An initialized OFArray
 */
- (instancetype)initWithObjects: (ObjectType const _Nonnull *_Nonnull)objects
			  count: (size_t)count;








/**
 * @brief Returns the object at the specified index in the array.
 *
 * @warning The returned object is *not* retained and autoreleased for
 *	    performance reasons!
 *
 * @param index The index of the object to return







>
>
>
>
>
>
>







226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
 * @param objects A C array of objects
 * @param count The length of the C array
 * @return An initialized OFArray
 */
- (instancetype)initWithObjects: (ObjectType const _Nonnull *_Nonnull)objects
			  count: (size_t)count;

/**
 * @brief Returns an OFEnumerator to enumerate through all objects of the array.
 *
 * @return An OFEnumerator to enumerate through all objects of the array
 */
- (OFEnumerator OF_GENERIC(ObjectType) *)objectEnumerator;

/**
 * @brief Returns the object at the specified index in the array.
 *
 * @warning The returned object is *not* retained and autoreleased for
 *	    performance reasons!
 *
 * @param index The index of the object to return
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
 * @ref setValue:forKey: is called for each object in the array.
 *
 * @note A @ref OFNull value is translated to nil!
 *
 * @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 Copies the objects at the specified range to the specified buffer.
 *
 * @param buffer The buffer to copy the objects to
 * @param range The range to copy
 */
- (void)getObjects: (ObjectType __unsafe_unretained _Nonnull *_Nonnull)buffer
	   inRange: (of_range_t)range;

/**
 * @brief Returns the index of the first object that is equivalent to the
 *	  specified object or `OF_NOT_FOUND` if it was not found.
 *
 * @param object The object whose index is returned
 * @return The index of the first object equivalent to the specified object
 *	   or `OF_NOT_FOUND` if it was not found
 */
- (size_t)indexOfObject: (ObjectType)object;

/**
 * @brief Returns the index of the first object that has the same address as the
 *	  specified object or `OF_NOT_FOUND` if it was not found.
 *
 * @param object The object whose index is returned
 * @return The index of the first object that has the same address as
 *	   the specified object or `OF_NOT_FOUND` if it was not found
 */
- (size_t)indexOfObjectIdenticalTo: (ObjectType)object;

/**
 * @brief Checks whether the array contains an object equal to the specified
 *	  object.
 *







|
<








|



|



|





|



|







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
 * @ref setValue:forKey: is called for each object in the array.
 *
 * @note A @ref OFNull value is translated to nil!
 *
 * @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 Copies the objects at the specified range to the specified buffer.
 *
 * @param buffer The buffer to copy the objects to
 * @param range The range to copy
 */
- (void)getObjects: (ObjectType __unsafe_unretained _Nonnull *_Nonnull)buffer
	   inRange: (OFRange)range;

/**
 * @brief Returns the index of the first object that is equivalent to the
 *	  specified object or `OFNotFound` if it was not found.
 *
 * @param object The object whose index is returned
 * @return The index of the first object equivalent to the specified object
 *	   or `OFNotFound` if it was not found
 */
- (size_t)indexOfObject: (ObjectType)object;

/**
 * @brief Returns the index of the first object that has the same address as the
 *	  specified object or `OFNotFound` if it was not found.
 *
 * @param object The object whose index is returned
 * @return The index of the first object that has the same address as
 *	   the specified object or `OFNotFound` if it was not found
 */
- (size_t)indexOfObjectIdenticalTo: (ObjectType)object;

/**
 * @brief Checks whether the array contains an object equal to the specified
 *	  object.
 *
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

/**
 * @brief Returns the objects in the specified range as a new OFArray.
 *
 * @param range The range for the subarray
 * @return The subarray as a new autoreleased OFArray
 */
- (OFArray OF_GENERIC(ObjectType) *)objectsInRange: (of_range_t)range;

/**
 * @brief Creates a string by joining all objects of the array.
 *
 * @param separator The string with which the objects should be joined
 * @return A string containing all objects joined by the separator
 */
- (OFString *)componentsJoinedByString: (OFString *)separator;

/**
 * @brief Creates a string by joining all objects of the array.
 *
 * @param separator The string with which the objects should be joined
 * @param options Options according to which the objects should be joined.@n
 *		  Possible values are:
 *		  Value                 | Description
 *		  ----------------------|----------------------
 * 		  `OF_ARRAY_SKIP_EMPTY` | Skip empty components
 * @return A string containing all objects joined by the separator
 */
- (OFString *)componentsJoinedByString: (OFString *)separator
			       options: (int)options;

/**
 * @brief Creates a string by calling the selector on all objects of the array
 *	  and joining the strings returned by calling the selector.
 *
 * @param separator The string with which the objects should be joined
 * @param selector The selector to perform on the objects
 * @return A string containing all objects joined by the separator
 */
- (OFString *)componentsJoinedByString: (OFString *)separator
			 usingSelector: (SEL)selector;

/**
 * @brief Creates a string by calling the selector on all objects of the array
 *	  and joining the strings returned by calling the selector.
 *
 * @param separator The string with which the objects should be joined
 * @param selector The selector to perform on the objects
 * @param options Options according to which the objects should be joined.@n
 *		  Possible values are:
 *		  Value                 | Description
 *		  ----------------------|----------------------
 * 		  `OF_ARRAY_SKIP_EMPTY` | Skip empty components
 * @return A string containing all objects joined by the separator
 */
- (OFString *)componentsJoinedByString: (OFString *)separator
			 usingSelector: (SEL)selector
			       options: (int)options;

/**
 * @brief Performs the specified selector on all objects in the array.
 *
 * @param selector The selector to perform on all objects in the array
 */
- (void)makeObjectsPerformSelector: (SEL)selector;







|













|
<
<
<
<



|


















|
<
<
<
<




|







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

/**
 * @brief Returns the objects in the specified range as a new OFArray.
 *
 * @param range The range for the subarray
 * @return The subarray as a new autoreleased OFArray
 */
- (OFArray OF_GENERIC(ObjectType) *)objectsInRange: (OFRange)range;

/**
 * @brief Creates a string by joining all objects of the array.
 *
 * @param separator The string with which the objects should be joined
 * @return A string containing all objects joined by the separator
 */
- (OFString *)componentsJoinedByString: (OFString *)separator;

/**
 * @brief Creates a string by joining all objects of the array.
 *
 * @param separator The string with which the objects should be joined
 * @param options Options according to which the objects should be joined




 * @return A string containing all objects joined by the separator
 */
- (OFString *)componentsJoinedByString: (OFString *)separator
			       options: (OFArrayJoinOptions)options;

/**
 * @brief Creates a string by calling the selector on all objects of the array
 *	  and joining the strings returned by calling the selector.
 *
 * @param separator The string with which the objects should be joined
 * @param selector The selector to perform on the objects
 * @return A string containing all objects joined by the separator
 */
- (OFString *)componentsJoinedByString: (OFString *)separator
			 usingSelector: (SEL)selector;

/**
 * @brief Creates a string by calling the selector on all objects of the array
 *	  and joining the strings returned by calling the selector.
 *
 * @param separator The string with which the objects should be joined
 * @param selector The selector to perform on the objects
 * @param options Options according to which the objects should be joined




 * @return A string containing all objects joined by the separator
 */
- (OFString *)componentsJoinedByString: (OFString *)separator
			 usingSelector: (SEL)selector
			       options: (OFArrayJoinOptions)options;

/**
 * @brief Performs the specified selector on all objects in the array.
 *
 * @param selector The selector to perform on all objects in the array
 */
- (void)makeObjectsPerformSelector: (SEL)selector;
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

/**
 * @brief Returns a copy of the array sorted using the specified selector and
 *	  options.
 *
 * @param selector The selector to use to sort the array. It's signature
 *		   should be the same as that of -[compare:].
 * @param options The options to use when sorting the array.@n
 *		  Possible values are:
 *		  Value                      | Description
 *		  ---------------------------|-------------------------
 *		  `OF_ARRAY_SORT_DESCENDING` | Sort in descending order
 * @return A sorted copy of the array
 */
- (OFArray OF_GENERIC(ObjectType) *)sortedArrayUsingSelector: (SEL)selector

						     options: (int)options;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Returns a copy of the array sorted using the specified selector and
 *	  options.
 *
 * @param comparator The comparator to use to sort the array
 * @param options The options to use when sorting the array.@n
 *		  Possible values are:
 *		  Value                      | Description
 *		  ---------------------------|-------------------------
 *		  `OF_ARRAY_SORT_DESCENDING` | Sort in descending order
 * @return A sorted copy of the array
 */
- (OFArray OF_GENERIC(ObjectType) *)
    sortedArrayUsingComparator: (of_comparator_t)comparator
		       options: (int)options;
#endif

/**
 * @brief Creates a new array with the specified object added.
 *
 * @param object The object to add
 * @return A new array with the specified object added
 */
- (OFArray OF_GENERIC(ObjectType) *)arrayByAddingObject: (ObjectType)object;

/**
 * @brief Creates a new array with the objects from the specified array added.
 *
 * @param array The array with objects to add
 * @return A new array with the objects from the specified array added
 */
- (OFArray OF_GENERIC(ObjectType) *)arrayByAddingObjectsFromArray:
    (OFArray OF_GENERIC(ObjectType) *)array;

/**
 * @brief Creates a new array with the specified object removed.
 *
 * @param object The object to remove
 * @return A new array with the specified object removed
 */
- (OFArray OF_GENERIC(ObjectType) *)arrayByRemovingObject: (ObjectType)object;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each object.
 *
 * @param block The block to execute for each object
 */
- (void)enumerateObjectsUsingBlock: (of_array_enumeration_block_t)block;

/**
 * @brief Creates a new array, mapping each object using the specified block.
 *
 * @param block A block which maps an object for each object
 * @return A new, autoreleased OFArray
 */
- (OFArray *)mappedArrayUsingBlock: (of_array_map_block_t)block;

/**
 * @brief Creates a new array, only containing the objects for which the block
 *	  returns true.
 *
 * @param block A block which determines if the object should be in the new
 *		array
 * @return A new, autoreleased OFArray
 */
- (OFArray OF_GENERIC(ObjectType) *)filteredArrayUsingBlock:
    (of_array_filter_block_t)block;

/**
 * @brief Folds the array to a single object using the specified block.
 *
 * If the array is empty, it will return `nil`.
 *
 * If there is only one object in the array, that object will be returned and
 * the block will not be invoked.
 *
 * If there are at least two objects, the block is invoked for each object
 * except the first, where left is always to what the array has already been
 * folded and right what should be added to left.
 *
 * @param block A block which folds two objects into one, which is called for
 *		all objects except the first
 * @return The array folded to a single object
 */
- (nullable id)foldUsingBlock: (of_array_fold_block_t)block;
#endif
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END







|
<
<
<
<


|
>
|







|
<
<
<
<



|
|



















<
<
<
<
<
<
<
<






|







|










|

















|







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

/**
 * @brief Returns a copy of the array sorted using the specified selector and
 *	  options.
 *
 * @param selector The selector to use to sort the array. It's signature
 *		   should be the same as that of -[compare:].
 * @param options The options to use when sorting the array




 * @return A sorted copy of the array
 */
- (OFArray OF_GENERIC(ObjectType) *)
    sortedArrayUsingSelector: (SEL)selector
		     options: (OFArraySortOptions)options;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Returns a copy of the array sorted using the specified selector and
 *	  options.
 *
 * @param comparator The comparator to use to sort the array
 * @param options The options to use when sorting the array




 * @return A sorted copy of the array
 */
- (OFArray OF_GENERIC(ObjectType) *)
    sortedArrayUsingComparator: (OFComparator)comparator
		       options: (OFArraySortOptions)options;
#endif

/**
 * @brief Creates a new array with the specified object added.
 *
 * @param object The object to add
 * @return A new array with the specified object added
 */
- (OFArray OF_GENERIC(ObjectType) *)arrayByAddingObject: (ObjectType)object;

/**
 * @brief Creates a new array with the objects from the specified array added.
 *
 * @param array The array with objects to add
 * @return A new array with the objects from the specified array added
 */
- (OFArray OF_GENERIC(ObjectType) *)arrayByAddingObjectsFromArray:
    (OFArray OF_GENERIC(ObjectType) *)array;









#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each object.
 *
 * @param block The block to execute for each object
 */
- (void)enumerateObjectsUsingBlock: (OFArrayEnumerationBlock)block;

/**
 * @brief Creates a new array, mapping each object using the specified block.
 *
 * @param block A block which maps an object for each object
 * @return A new, autoreleased OFArray
 */
- (OFArray *)mappedArrayUsingBlock: (OFArrayMapBlock)block;

/**
 * @brief Creates a new array, only containing the objects for which the block
 *	  returns true.
 *
 * @param block A block which determines if the object should be in the new
 *		array
 * @return A new, autoreleased OFArray
 */
- (OFArray OF_GENERIC(ObjectType) *)filteredArrayUsingBlock:
    (OFArrayFilterBlock)block;

/**
 * @brief Folds the array to a single object using the specified block.
 *
 * If the array is empty, it will return `nil`.
 *
 * If there is only one object in the array, that object will be returned and
 * the block will not be invoked.
 *
 * If there are at least two objects, the block is invoked for each object
 * except the first, where left is always to what the array has already been
 * folded and right what should be added to left.
 *
 * @param block A block which folds two objects into one, which is called for
 *		all objects except the first
 * @return The array folded to a single object
 */
- (nullable id)foldUsingBlock: (OFArrayFoldBlock)block;
#endif
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END

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

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
#import "OFOutOfRangeException.h"

static struct {
	Class isa;
} placeholder;

@interface OFArray ()
- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth;
@end

@interface OFPlaceholderArray: OFArray
@end

@implementation OFPlaceholderArray
- (instancetype)init







|
>
|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#import "OFOutOfRangeException.h"

static struct {
	Class isa;
} placeholder;

@interface OFArray ()
- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth;
@end

@interface OFPlaceholderArray: OFArray
@end

@implementation OFPlaceholderArray
- (instancetype)init
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

- (instancetype)initWithObjects: (id)firstObject, ...
{
	id ret;
	va_list arguments;

	va_start(arguments, firstObject);
	ret = [self initWithObject: firstObject
			 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments







|
<







192
193
194
195
196
197
198
199

200
201
202
203
204
205
206

- (instancetype)initWithObjects: (id)firstObject, ...
{
	id ret;
	va_list arguments;

	va_start(arguments, firstObject);
	ret = [self initWithObject: firstObject arguments: arguments];

	va_end(arguments);

	return ret;
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
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
}

- (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)getObjects: (id *)buffer
	   inRange: (of_range_t)range
{
	for (size_t i = 0; i < range.length; i++)
		buffer[i] = [self objectAtIndex: range.location + i];
}

- (id const *)objects
{
	OFObject *container;
	size_t count;
	id *buffer;



	container = [[[OFObject alloc] init] autorelease];
	count = self.count;
	buffer = [container allocMemoryWithSize: sizeof(*buffer)
					  count: count];






	[self getObjects: buffer
		 inRange: of_range(0, count)];

	return buffer;
}

- (id)copy
{
	return [self retain];
}








|
<







<
|
|
>

>
|
|
|
|
>
>
>
>
>
|
<
<

|







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
}

- (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)getObjects: (id *)buffer inRange: (OFRange)range

{
	for (size_t i = 0; i < range.length; i++)
		buffer[i] = [self objectAtIndex: range.location + i];
}

- (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);
		@throw e;
	}



	return ret;
}

- (id)copy
{
	return [self retain];
}

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
	}

	[ret makeImmutable];

	return ret;
}

- (void)setValue: (id)value
	  forKey: (OFString *)key
{
	for (id object in self)
		[object setValue: value
			  forKey: key];
}

- (size_t)indexOfObject: (id)object
{
	size_t i = 0;

	if (object == nil)
		return OF_NOT_FOUND;

	for (id objectIter in self) {
		if ([objectIter isEqual: object])
			return i;

		i++;
	}

	return OF_NOT_FOUND;
}

- (size_t)indexOfObjectIdenticalTo: (id)object
{
	size_t i = 0;

	if (object == nil)
		return OF_NOT_FOUND;

	for (id objectIter in self) {
		if (objectIter == object)
			return i;

		i++;
	}

	return OF_NOT_FOUND;
}

- (bool)containsObject: (id)object
{
	return ([self indexOfObject: object] != OF_NOT_FOUND);
}

- (bool)containsObjectIdenticalTo: (id)object
{
	return ([self indexOfObjectIdenticalTo: object] != OF_NOT_FOUND);
}

- (id)firstObject
{
	if (self.count > 0)
		return [self objectAtIndex: 0];

	return nil;
}

- (id)lastObject
{
	size_t count = self.count;

	if (count > 0)
		return [self objectAtIndex: count - 1];

	return nil;
}

- (OFArray *)objectsInRange: (of_range_t)range
{
	OFArray *ret;
	id *buffer;

	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length < self.count)
		@throw [OFOutOfRangeException exception];

	if (![self isKindOfClass: [OFMutableArray class]])
		return [OFSubarray arrayWithArray: self
					    range: range];

	buffer = [self allocMemoryWithSize: sizeof(*buffer)
				     count: range.length];

	@try {
		[self getObjects: buffer
			 inRange: range];

		ret = [OFArray arrayWithObjects: buffer
					  count: range.length];
	} @finally {
		[self freeMemory: buffer];
	}

	return ret;
}

- (OFString *)componentsJoinedByString: (OFString *)separator
{
	return [self componentsJoinedByString: separator
				usingSelector: @selector(description)
				      options: 0];
}

- (OFString *)componentsJoinedByString: (OFString *)separator
			       options: (int)options
{
	return [self componentsJoinedByString: separator
				usingSelector: @selector(description)
				      options: options];
}

- (OFString *)componentsJoinedByString: (OFString *)separator
			 usingSelector: (SEL)selector
{
	return [self componentsJoinedByString: separator
				usingSelector: selector
				      options: 0];
}

- (OFString *)componentsJoinedByString: (OFString *)separator
			 usingSelector: (SEL)selector
			       options: (int)options
{
	OFMutableString *ret;

	if (separator == nil)
		@throw [OFInvalidArgumentException exception];

	if (self.count == 0)
		return @"";

	if (self.count == 1) {
		OFString *component =
		    [[self firstObject] performSelector: selector];

		if (component == nil)
			@throw [OFInvalidArgumentException exception];

		return component;
	}

	ret = [OFMutableString string];

	if (options & OF_ARRAY_SKIP_EMPTY) {
		for (id object in self) {
			void *pool = objc_autoreleasePoolPush();
			OFString *component =
			    [object performSelector: selector];

			if (component == nil)
				@throw [OFInvalidArgumentException exception];







|
<


|
<







|








|







|








|




|




|




















|









|
<

<
<
|

|
<

|
<

|













|
















|











|









|







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
	}

	[ret makeImmutable];

	return ret;
}

- (void)setValue: (id)value forKey: (OFString *)key

{
	for (id object in self)
		[object setValue: value forKey: key];

}

- (size_t)indexOfObject: (id)object
{
	size_t i = 0;

	if (object == nil)
		return OFNotFound;

	for (id objectIter in self) {
		if ([objectIter isEqual: object])
			return i;

		i++;
	}

	return OFNotFound;
}

- (size_t)indexOfObjectIdenticalTo: (id)object
{
	size_t i = 0;

	if (object == nil)
		return OFNotFound;

	for (id objectIter in self) {
		if (objectIter == object)
			return i;

		i++;
	}

	return OFNotFound;
}

- (bool)containsObject: (id)object
{
	return ([self indexOfObject: object] != OFNotFound);
}

- (bool)containsObjectIdenticalTo: (id)object
{
	return ([self indexOfObjectIdenticalTo: object] != OFNotFound);
}

- (id)firstObject
{
	if (self.count > 0)
		return [self objectAtIndex: 0];

	return nil;
}

- (id)lastObject
{
	size_t count = self.count;

	if (count > 0)
		return [self objectAtIndex: count - 1];

	return nil;
}

- (OFArray *)objectsInRange: (OFRange)range
{
	OFArray *ret;
	id *buffer;

	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length < self.count)
		@throw [OFOutOfRangeException exception];

	if (![self isKindOfClass: [OFMutableArray class]])
		return [OFSubarray arrayWithArray: self range: range];




	buffer = OFAllocMemory(range.length, sizeof(*buffer));
	@try {
		[self getObjects: buffer inRange: range];


		ret = [OFArray arrayWithObjects: buffer count: range.length];

	} @finally {
		OFFreeMemory(buffer);
	}

	return ret;
}

- (OFString *)componentsJoinedByString: (OFString *)separator
{
	return [self componentsJoinedByString: separator
				usingSelector: @selector(description)
				      options: 0];
}

- (OFString *)componentsJoinedByString: (OFString *)separator
			       options: (OFArrayJoinOptions)options
{
	return [self componentsJoinedByString: separator
				usingSelector: @selector(description)
				      options: options];
}

- (OFString *)componentsJoinedByString: (OFString *)separator
			 usingSelector: (SEL)selector
{
	return [self componentsJoinedByString: separator
				usingSelector: selector
				      options: 0];
}

- (OFString *)componentsJoinedByString: (OFString *)separator
			 usingSelector: (SEL)selector
			       options: (OFArrayJoinOptions)options
{
	OFMutableString *ret;

	if (separator == nil)
		@throw [OFInvalidArgumentException exception];

	if (self.count == 0)
		return @"";

	if (self.count == 1) {
		OFString *component =
		    [[self objectAtIndex: 0] performSelector: selector];

		if (component == nil)
			@throw [OFInvalidArgumentException exception];

		return component;
	}

	ret = [OFMutableString string];

	if (options & OFArraySkipEmptyComponents) {
		for (id object in self) {
			void *pool = objc_autoreleasePoolPush();
			OFString *component =
			    [object performSelector: selector];

			if (component == nil)
				@throw [OFInvalidArgumentException exception];
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
			return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	for (id object in self)
		OF_HASH_ADD_HASH(hash, [object hash]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	void *pool;
	OFMutableString *ret;

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

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return [ret autorelease];
}

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

	if ([self isKindOfClass: [OFMutableArray class]])
		element = [OFXMLElement elementWithName: @"OFMutableArray"
					      namespace: OF_SERIALIZATION_NS];
	else
		element = [OFXMLElement elementWithName: @"OFArray"
					      namespace: OF_SERIALIZATION_NS];

	for (id <OFSerialization> object in self) {
		void *pool2 = objc_autoreleasePoolPush();

		[element addChild: object.XMLElementBySerializing];

		objc_autoreleasePoolPop(pool2);
	}

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0
						depth: 0];
}

- (OFString *)JSONRepresentationWithOptions: (int)options

{
	return [self of_JSONRepresentationWithOptions: options
						depth: 0];
}

- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth
{
	OFMutableString *JSON = [OFMutableString stringWithString: @"["];
	void *pool = objc_autoreleasePoolPush();
	size_t i, count = self.count;

	if (options & OF_JSON_REPRESENTATION_PRETTY) {
		OFMutableString *indentation = [OFMutableString string];

		for (i = 0; i < depth; i++)
			[indentation appendString: @"\t"];

		[JSON appendString: @"\n"];








|

|


|

|

















|
<




















|


|


















|
<


|
>

|
<


|
>
|





|







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
			return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	for (id object in self)
		OFHashAddHash(&hash, [object hash]);

	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)description
{
	void *pool;
	OFMutableString *ret;

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

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return [ret autorelease];
}

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

	if ([self isKindOfClass: [OFMutableArray class]])
		element = [OFXMLElement elementWithName: @"OFMutableArray"
					      namespace: OFSerializationNS];
	else
		element = [OFXMLElement elementWithName: @"OFArray"
					      namespace: OFSerializationNS];

	for (id <OFSerialization> object in self) {
		void *pool2 = objc_autoreleasePoolPush();

		[element addChild: object.XMLElementBySerializing];

		objc_autoreleasePoolPop(pool2);
	}

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0 depth: 0];

}

- (OFString *)JSONRepresentationWithOptions:
    (OFJSONRepresentationOptions)options
{
	return [self of_JSONRepresentationWithOptions: options depth: 0];

}

- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth
{
	OFMutableString *JSON = [OFMutableString stringWithString: @"["];
	void *pool = objc_autoreleasePoolPush();
	size_t i, count = self.count;

	if (options & OFJSONRepresentationOptionPretty) {
		OFMutableString *indentation = [OFMutableString string];

		for (i = 0; i < depth; i++)
			[indentation appendString: @"\t"];

		[JSON appendString: @"\n"];

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
	count = self.count;

	if (count <= 15) {
		uint8_t tmp = 0x90 | ((uint8_t)count & 0xF);
		[data addItem: &tmp];
	} else if (count <= UINT16_MAX) {
		uint8_t type = 0xDC;
		uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)count);

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else if (count <= UINT32_MAX) {
		uint8_t type = 0xDD;
		uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)count);

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else
		@throw [OFOutOfRangeException exception];

	pool = objc_autoreleasePoolPush();

	i = 0;
	for (id object in self) {
		void *pool2 = objc_autoreleasePoolPush();
		OFData *child;

		i++;

		child = [object messagePackRepresentation];
		[data addItems: child.items
			 count: child.count];

		objc_autoreleasePoolPop(pool2);
	}

	assert(i == count);

	[data makeImmutable];







|


|
<


|


|
<













|
<







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
	count = self.count;

	if (count <= 15) {
		uint8_t tmp = 0x90 | ((uint8_t)count & 0xF);
		[data addItem: &tmp];
	} else if (count <= UINT16_MAX) {
		uint8_t type = 0xDC;
		uint16_t tmp = OFToBigEndian16((uint16_t)count);

		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else if (count <= UINT32_MAX) {
		uint8_t type = 0xDD;
		uint32_t tmp = OFToBigEndian32((uint32_t)count);

		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else
		@throw [OFOutOfRangeException exception];

	pool = objc_autoreleasePoolPush();

	i = 0;
	for (id object in self) {
		void *pool2 = objc_autoreleasePoolPush();
		OFData *child;

		i++;

		child = [object messagePackRepresentation];
		[data addItems: child.items count: child.count];


		objc_autoreleasePoolPop(pool2);
	}

	assert(i == count);

	[data makeImmutable];
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
		[object performSelector: selector];
}

- (void)makeObjectsPerformSelector: (SEL)selector
			withObject: (id)object
{
	for (id objectIter in self)
		[objectIter performSelector: selector
				 withObject: object];
}

- (OFArray *)sortedArray
{
	OFMutableArray *new = [[self mutableCopy] autorelease];

	[new sort];

	[new makeImmutable];

	return new;
}

- (OFArray *)sortedArrayUsingSelector: (SEL)selector
			      options: (int)options
{
	OFMutableArray *new = [[self mutableCopy] autorelease];

	[new sortUsingSelector: selector
		       options: options];

	[new makeImmutable];

	return new;
}

#ifdef OF_HAVE_BLOCKS
- (OFArray *)sortedArrayUsingComparator: (of_comparator_t)comparator
				options: (int)options
{
	OFMutableArray *new = [[self mutableCopy] autorelease];

	[new sortUsingComparator: comparator
			 options: options];

	[new makeImmutable];

	return new;
}
#endif

- (OFArray *)reversedArray
{
	OFMutableArray *new = [[self mutableCopy] autorelease];

	[new reverse];

	[new makeImmutable];

	return new;
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	of_range_t range = of_range(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;

	[self getObjects: objects
		 inRange: range];

	if (range.location + range.length > ULONG_MAX)
		@throw [OFOutOfRangeException exception];

	state->state = (unsigned long)(range.location + range.length);
	state->itemsPtr = objects;
	state->mutationsPtr = (unsigned long *)self;

	return (int)range.length;
}

- (OFEnumerator *)objectEnumerator
{
	return [[[OFArrayEnumerator alloc] initWithArray: self
					    mutationsPtr: NULL] autorelease];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (of_array_enumeration_block_t)block
{
	size_t i = 0;
	bool stop = false;

	for (id object in self) {
		block(object, i++, &stop);








|
<





<

<

<




|


<
|
<
<

<




|
|


<
|
<
<

<







<

<

<



|



|







|
<


















|







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
		[object performSelector: selector];
}

- (void)makeObjectsPerformSelector: (SEL)selector
			withObject: (id)object
{
	for (id objectIter in self)
		[objectIter performSelector: selector withObject: object];

}

- (OFArray *)sortedArray
{
	OFMutableArray *new = [[self mutableCopy] autorelease];

	[new sort];

	[new makeImmutable];

	return new;
}

- (OFArray *)sortedArrayUsingSelector: (SEL)selector
			      options: (OFArraySortOptions)options
{
	OFMutableArray *new = [[self mutableCopy] autorelease];

	[new sortUsingSelector: selector options: options];


	[new makeImmutable];

	return new;
}

#ifdef OF_HAVE_BLOCKS
- (OFArray *)sortedArrayUsingComparator: (OFComparator)comparator
				options: (OFArraySortOptions)options
{
	OFMutableArray *new = [[self mutableCopy] autorelease];

	[new sortUsingComparator: comparator options: options];


	[new makeImmutable];

	return new;
}
#endif

- (OFArray *)reversedArray
{
	OFMutableArray *new = [[self mutableCopy] autorelease];

	[new reverse];

	[new makeImmutable];

	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;

	[self getObjects: objects inRange: range];


	if (range.location + range.length > ULONG_MAX)
		@throw [OFOutOfRangeException exception];

	state->state = (unsigned long)(range.location + range.length);
	state->itemsPtr = objects;
	state->mutationsPtr = (unsigned long *)self;

	return (int)range.length;
}

- (OFEnumerator *)objectEnumerator
{
	return [[[OFArrayEnumerator alloc] initWithArray: self
					    mutationsPtr: NULL] autorelease];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (OFArrayEnumerationBlock)block
{
	size_t i = 0;
	bool stop = false;

	for (id object in self) {
		block(object, i++, &stop);

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
{
	OFMutableArray *ret;

	if (object == nil)
		@throw [OFInvalidArgumentException exception];

	ret = [[self mutableCopy] autorelease];

	[ret addObject: object];
	[ret makeImmutable];

	return ret;
}

- (OFArray *)arrayByAddingObjectsFromArray: (OFArray *)array
{
	OFMutableArray *ret = [[self mutableCopy] autorelease];

	[ret addObjectsFromArray: array];
	[ret makeImmutable];

	return ret;
}

- (OFArray *)arrayByRemovingObject: (id)object
{
	OFMutableArray *ret = [[self mutableCopy] autorelease];

	[ret removeObject: object];
	[ret makeImmutable];

	return ret;
}

#ifdef OF_HAVE_BLOCKS
- (OFArray *)mappedArrayUsingBlock: (of_array_map_block_t)block
{
	OFArray *ret;
	size_t count = self.count;
	id *tmp = [self allocMemoryWithSize: sizeof(id)
				      count: count];

	@try {
		[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
		    bool *stop) {
			tmp[idx] = block(object, idx);
		}];

		ret = [OFArray arrayWithObjects: tmp
					  count: count];
	} @finally {
		[self freeMemory: tmp];
	}

	return ret;
}

- (OFArray *)filteredArrayUsingBlock: (of_array_filter_block_t)block
{
	OFArray *ret;
	size_t count = self.count;
	id *tmp = [self allocMemoryWithSize: sizeof(id)
				      count: count];

	@try {
		__block size_t i = 0;

		[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
		    bool *stop) {
			if (block(object, idx))
				tmp[i++] = object;
		}];

		ret = [OFArray arrayWithObjects: tmp
					  count: i];
	} @finally {
		[self freeMemory: tmp];
	}

	return ret;
}

- (id)foldUsingBlock: (of_array_fold_block_t)block
{
	size_t count = self.count;
	__block id current;

	if (count == 0)
		return nil;
	if (count == 1)
		return [[[self firstObject] retain] autorelease];

	[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
	    bool *stop) {
		id new;

		if (idx == 0) {
			current = [object retain];







<









<






<
<
<
<
<
<
<
<
<
<

|



|
<







|
<

|





|



|
<










|
<

|





|







|







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
{
	OFMutableArray *ret;

	if (object == nil)
		@throw [OFInvalidArgumentException exception];

	ret = [[self mutableCopy] autorelease];

	[ret addObject: object];
	[ret makeImmutable];

	return ret;
}

- (OFArray *)arrayByAddingObjectsFromArray: (OFArray *)array
{
	OFMutableArray *ret = [[self mutableCopy] autorelease];

	[ret addObjectsFromArray: array];
	[ret makeImmutable];

	return ret;
}











#ifdef OF_HAVE_BLOCKS
- (OFArray *)mappedArrayUsingBlock: (OFArrayMapBlock)block
{
	OFArray *ret;
	size_t count = self.count;
	id *tmp = OFAllocMemory(count, sizeof(id));


	@try {
		[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
		    bool *stop) {
			tmp[idx] = block(object, idx);
		}];

		ret = [OFArray arrayWithObjects: tmp count: count];

	} @finally {
		OFFreeMemory(tmp);
	}

	return ret;
}

- (OFArray *)filteredArrayUsingBlock: (OFArrayFilterBlock)block
{
	OFArray *ret;
	size_t count = self.count;
	id *tmp = OFAllocMemory(count, sizeof(id));


	@try {
		__block size_t i = 0;

		[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
		    bool *stop) {
			if (block(object, idx))
				tmp[i++] = object;
		}];

		ret = [OFArray arrayWithObjects: tmp count: i];

	} @finally {
		OFFreeMemory(tmp);
	}

	return ret;
}

- (id)foldUsingBlock: (OFArrayFoldBlock)block
{
	size_t count = self.count;
	__block id current;

	if (count == 0)
		return nil;
	if (count == 1)
		return [[[self objectAtIndex: 0] retain] autorelease];

	[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
	    bool *stop) {
		id new;

		if (idx == 0) {
			current = [object retain];

Renamed and modified src/atomic.h [7db74776cf] to src/OFAtomic.h [25c89d25b5].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
20
21
22
23
24
25
26





27





28








































































































































29

30
31
32
33
34
35
36
37
38
39
40
#import "macros.h"

#ifndef OF_HAVE_ATOMIC_OPS
# error No atomic operations available!
#endif

#if !defined(OF_HAVE_THREADS)





# import "atomic_no_threads.h"





#elif defined(OF_X86_64_ASM) || defined(OF_X86_ASM)








































































































































# import "atomic_x86.h"

#elif defined(OF_POWERPC_ASM) && !defined(__APPLE_CC__) && !defined(OF_AIX)
# import "atomic_powerpc.h"
#elif defined(OF_HAVE_ATOMIC_BUILTINS)
# import "atomic_builtins.h"
#elif defined(OF_HAVE_SYNC_BUILTINS)
# import "atomic_sync_builtins.h"
#elif defined(OF_HAVE_OSATOMIC)
# import "atomic_osatomic.h"
#else
# error No atomic operations available!
#endif







>
>
>
>
>
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
|

|

|

|



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
#import "macros.h"

#ifndef OF_HAVE_ATOMIC_OPS
# error No atomic operations available!
#endif

#if !defined(OF_HAVE_THREADS)
static OF_INLINE int
OFAtomicIntAdd(volatile int *_Nonnull p, int i)
{
	return (*p += i);
}

static OF_INLINE int32_t
OFAtomicInt32Add(volatile int32_t *_Nonnull p, int32_t i)
{
	return (*p += i);
}

static OF_INLINE void *_Nullable
OFAtomicPointerAdd(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return (*(char *volatile *)p += i);
}

static OF_INLINE int
OFAtomicIntSubtract(volatile int *_Nonnull p, int i)
{
	return (*p -= i);
}

static OF_INLINE int32_t
OFAtomicInt32Subtract(volatile int32_t *_Nonnull p, int32_t i)
{
	return (*p -= i);
}

static OF_INLINE void *_Nullable
OFAtomicPointerSubtract(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return (*(char *volatile *)p -= i);
}

static OF_INLINE int
OFAtomicIntIncrease(volatile int *_Nonnull p)
{
	return ++*p;
}

static OF_INLINE int32_t
OFAtomicInt32Increase(volatile int32_t *_Nonnull p)
{
	return ++*p;
}

static OF_INLINE int
OFAtomicIntDecrease(volatile int *_Nonnull p)
{
	return --*p;
}

static OF_INLINE int32_t
OFAtomicInt32Decrease(volatile int32_t *_Nonnull p)
{
	return --*p;
}

static OF_INLINE unsigned int
OFAtomicIntOr(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return (*p |= i);
}

static OF_INLINE uint32_t
OFAtomicInt32Or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return (*p |= i);
}

static OF_INLINE unsigned int
OFAtomicIntAnd(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return (*p &= i);
}

static OF_INLINE uint32_t
OFAtomicInt32And(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return (*p &= i);
}

static OF_INLINE unsigned int
OFAtomicIntXor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return (*p ^= i);
}

static OF_INLINE uint32_t
OFAtomicInt32Xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return (*p ^= i);
}

static OF_INLINE bool
OFAtomicIntCompareAndSwap(volatile int *_Nonnull p, int o, int n)
{
	if (*p == o) {
		*p = n;
		return true;
	}

	return false;
}

static OF_INLINE bool
OFAtomicInt32CompareAndSwap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	if (*p == o) {
		*p = n;
		return true;
	}

	return false;
}

static OF_INLINE bool
OFAtomicPointerCompareAndSwap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	if (*p == o) {
		*p = n;
		return true;
	}

	return false;
}

static OF_INLINE void
OFMemoryBarrier(void)
{
	/* nop */
}

static OF_INLINE void
OFAcquireMemoryBarrier(void)
{
	/* nop */
}

static OF_INLINE void
OFReleaseMemoryBarrier(void)
{
	/* nop */
}
#elif (defined(OF_X86_64) || defined(OF_X86)) && defined(__GNUC__)
# import "platform/x86/OFAtomic.h"
#elif defined(OF_POWERPC) && defined(__GNUC__) && !defined(__APPLE_CC__) && \
    !defined(OF_AIX)
# import "platform/PowerPC/OFAtomic.h"
#elif defined(OF_HAVE_ATOMIC_BUILTINS)
# import "platform/GCC4.7/OFAtomic.h"
#elif defined(OF_HAVE_SYNC_BUILTINS)
# import "platform/GCC4/OFAtomic.h"
#elif defined(OF_HAVE_OSATOMIC)
# import "platform/macOS/OFAtomic.h"
#else
# error No atomic operations available!
#endif

Renamed and modified src/base64.h [38c87986ea] to src/OFBase64.h [14838b60d0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
28
29
30
31
32
33
34
35
36
37
38
39
40
41

@class OFString;
@class OFMutableData;

#ifdef __cplusplus
extern "C" {
#endif
extern OFString *of_base64_encode(const void *, size_t);
extern bool of_base64_decode(OFMutableData *, const char *, size_t);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|
|





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

@class OFString;
@class OFMutableData;

#ifdef __cplusplus
extern "C" {
#endif
extern OFString *OFBase64Encode(const void *, size_t);
extern bool OFBase64Decode(OFMutableData *, const char *, size_t);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Renamed and modified src/base64.m [91e8757b0a] to src/OFBase64.m [7cc5f21d9d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"
#import "OFData.h"
#import "base64.h"

const uint8_t of_base64_encode_table[64] = {
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
	'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
	'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
	'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
	'4', '5', '6', '7', '8', '9', '+', '/'
};

const int8_t of_base64_decode_table[128] = {
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
	55, 56, 57, 58, 59, 60, 61, -1, -1, -1,  0, -1, -1, -1,  0,  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, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1
};

OFString *
of_base64_encode(const void *data, size_t length)
{
	OFMutableString *ret = [OFMutableString string];
	uint8_t *buffer = (uint8_t *)data;
	size_t i;
	uint8_t rest;
	char tb[4];
	uint32_t sb;

	rest = length % 3;

	for (i = 0; i < length - rest; i += 3) {
		sb = (buffer[i] << 16) | (buffer[i + 1] << 8) | buffer[i + 2];

		tb[0] = of_base64_encode_table[(sb & 0xFC0000) >> 18];
		tb[1] = of_base64_encode_table[(sb & 0x03F000) >> 12];
		tb[2] = of_base64_encode_table[(sb & 0x000FC0) >> 6];
		tb[3] = of_base64_encode_table[sb & 0x00003F];

		[ret appendCString: tb
			  encoding: OF_STRING_ENCODING_ASCII
			    length: 4];
	}

	switch (rest) {
	case 1:
		tb[0] = of_base64_encode_table[buffer[i] >> 2];
		tb[1] = of_base64_encode_table[(buffer[i] & 3) << 4];
		tb[2] = tb[3] = '=';

		[ret appendCString: tb
			  encoding: OF_STRING_ENCODING_ASCII
			    length: 4];

		break;
	case 2:
		sb = (buffer[i] << 16) | (buffer[i + 1] << 8);

		tb[0] = of_base64_encode_table[(sb & 0xFC0000) >> 18];
		tb[1] = of_base64_encode_table[(sb & 0x03F000) >> 12];
		tb[2] = of_base64_encode_table[(sb & 0x000FC0) >> 6];
		tb[3] = '=';

		[ret appendCString: tb
			  encoding: OF_STRING_ENCODING_ASCII
			    length: 4];

		break;
	}

	[ret makeImmutable];

	return ret;
}

bool
of_base64_decode(OFMutableData *data, const char *string, size_t length)
{
	const uint8_t *buffer = (const uint8_t *)string;
	size_t i;

	if ((length & 3) != 0)
		return false;


<
<
|















|
|
|

|







|











|













|
|
|
|


|





|
|



|






|
|
|



|











|







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


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

#import "OFBase64.h"
#import "OFString.h"
#import "OFData.h"

static const unsigned char encodeTable[64] = {
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
	'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
	'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
	'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
	'4', '5', '6', '7', '8', '9', '+', '/'
};

static const signed char decodeTable[128] = {
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
	55, 56, 57, 58, 59, 60, 61, -1, -1, -1,  0, -1, -1, -1,  0,  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, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1
};

OFString *
OFBase64Encode(const void *data, size_t length)
{
	OFMutableString *ret = [OFMutableString string];
	uint8_t *buffer = (uint8_t *)data;
	size_t i;
	uint8_t rest;
	char tb[4];
	uint32_t sb;

	rest = length % 3;

	for (i = 0; i < length - rest; i += 3) {
		sb = (buffer[i] << 16) | (buffer[i + 1] << 8) | buffer[i + 2];

		tb[0] = encodeTable[(sb & 0xFC0000) >> 18];
		tb[1] = encodeTable[(sb & 0x03F000) >> 12];
		tb[2] = encodeTable[(sb & 0x000FC0) >> 6];
		tb[3] = encodeTable[sb & 0x00003F];

		[ret appendCString: tb
			  encoding: OFStringEncodingASCII
			    length: 4];
	}

	switch (rest) {
	case 1:
		tb[0] = encodeTable[buffer[i] >> 2];
		tb[1] = encodeTable[(buffer[i] & 3) << 4];
		tb[2] = tb[3] = '=';

		[ret appendCString: tb
			  encoding: OFStringEncodingASCII
			    length: 4];

		break;
	case 2:
		sb = (buffer[i] << 16) | (buffer[i + 1] << 8);

		tb[0] = encodeTable[(sb & 0xFC0000) >> 18];
		tb[1] = encodeTable[(sb & 0x03F000) >> 12];
		tb[2] = encodeTable[(sb & 0x000FC0) >> 6];
		tb[3] = '=';

		[ret appendCString: tb
			  encoding: OFStringEncodingASCII
			    length: 4];

		break;
	}

	[ret makeImmutable];

	return ret;
}

bool
OFBase64Decode(OFMutableData *data, const char *string, size_t length)
{
	const uint8_t *buffer = (const uint8_t *)string;
	size_t i;

	if ((length & 3) != 0)
		return false;

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
			return false;

		if (buffer[i + 2] == '=')
			count--;
		if (buffer[i + 3] == '=')
			count--;

		if ((tmp = of_base64_decode_table[buffer[i]]) == -1)
			return false;

		sb |= tmp << 18;

		if ((tmp = of_base64_decode_table[buffer[i + 1]]) == -1)
			return false;

		sb |= tmp << 12;

		if ((tmp = of_base64_decode_table[buffer[i + 2]]) == -1)
			return false;

		sb |= tmp << 6;

		if ((tmp = of_base64_decode_table[buffer[i + 3]]) == -1)
			return false;

		sb |= tmp;

		db[0] = (sb & 0xFF0000) >> 16;
		db[1] = (sb & 0x00FF00) >> 8;
		db[2] = sb & 0x0000FF;

		[data addItems: db
			 count: count];
	}

	return true;
}







|




|




|




|








|
<




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
			return false;

		if (buffer[i + 2] == '=')
			count--;
		if (buffer[i + 3] == '=')
			count--;

		if ((tmp = decodeTable[buffer[i]]) == -1)
			return false;

		sb |= tmp << 18;

		if ((tmp = decodeTable[buffer[i + 1]]) == -1)
			return false;

		sb |= tmp << 12;

		if ((tmp = decodeTable[buffer[i + 2]]) == -1)
			return false;

		sb |= tmp << 6;

		if ((tmp = decodeTable[buffer[i + 3]]) == -1)
			return false;

		sb |= tmp;

		db[0] = (sb & 0xFF0000) >> 16;
		db[1] = (sb & 0x00FF00) >> 8;
		db[2] = sb & 0x0000FF;

		[data addItems: db count: count];

	}

	return true;
}

Modified src/OFBitSetCharacterSet.h from [4ecbca5797] to [6dd7022dff].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFBitSetCharacterSet.m from [a3efdbfddb] to [d65aed83c7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

- (instancetype)initWithCharactersInString: (OFString *)string
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		const of_unichar_t *characters = string.characters;
		size_t length = string.length;

		for (size_t i = 0; i < length; i++) {
			of_unichar_t c = characters[i];

			if (c / 8 >= _size) {
				size_t newSize;

				if (UINT32_MAX - c < 1)
					@throw [OFOutOfRangeException
					    exception];

				newSize = OF_ROUND_UP_POW2(8, c + 1) / 8;


				_bitset = [self resizeMemory: _bitset
							size: newSize];
				memset(_bitset + _size, '\0', newSize - _size);

				_size = newSize;
			}

			of_bitset_set(_bitset, c);
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}








- (bool)characterIsMember: (of_unichar_t)character
{
	if (character / 8 >= _size)
		return false;

	return of_bitset_isset(_bitset, character);
}
@end







|



|

|






|
>

|
<





|











>
>
>
>
>
>
>
|

|


|


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

- (instancetype)initWithCharactersInString: (OFString *)string
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		const OFUnichar *characters = string.characters;
		size_t length = string.length;

		for (size_t i = 0; i < length; i++) {
			OFUnichar c = characters[i];

			if (c / CHAR_BIT >= _size) {
				size_t newSize;

				if (UINT32_MAX - c < 1)
					@throw [OFOutOfRangeException
					    exception];

				newSize = OFRoundUpToPowerOf2(CHAR_BIT, c + 1) /
				    CHAR_BIT;

				_bitset = OFResizeMemory(_bitset, newSize, 1);

				memset(_bitset + _size, '\0', newSize - _size);

				_size = newSize;
			}

			OFBitsetSet(_bitset, c);
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	OFFreeMemory(_bitset);

	[super dealloc];
}

- (bool)characterIsMember: (OFUnichar)character
{
	if (character / CHAR_BIT >= _size)
		return false;

	return OFBitsetIsSet(_bitset, character);
}
@end

Modified src/OFBlock.h from [f83ccaa46b] to [ae09bf0d6b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "block.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFBlock OFBlock.h ObjFW/OFBlock.h
 *
 * @brief The class for all blocks, since all blocks are also objects.
 */

<
<
|















<
<







1


2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


18
19
20
21
22
23
24
/*


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



OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFBlock OFBlock.h ObjFW/OFBlock.h
 *
 * @brief The class for all blocks, since all blocks are also objects.
 */
38
39
40
41
42
43
44
45
































46
OF_SUBCLASSING_RESTRICTED
@interface OFGlobalBlock: OFBlock
@end

OF_SUBCLASSING_RESTRICTED
@interface OFMallocBlock: OFBlock
@end

































OF_ASSUME_NONNULL_END








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

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
OF_SUBCLASSING_RESTRICTED
@interface OFGlobalBlock: OFBlock
@end

OF_SUBCLASSING_RESTRICTED
@interface OFMallocBlock: OFBlock
@end

#ifdef __cplusplus
extern "C" {
#endif
extern void *_Nullable _Block_copy(const void *_Nullable);
extern void _Block_release(const void *_Nullable);

# if defined(OF_WINDOWS) && \
    (defined(OF_NO_SHARED) || defined(OF_COMPILING_OBJFW))
/*
 * Clang has implicit declarations for these, but they are dllimport. When
 * compiling ObjFW itself or using it as a static library, these need to be
 * dllexport. Interestingly, this still works when using it as a shared library.
 */
extern __declspec(dllexport) struct objc_class _NSConcreteStackBlock;
extern __declspec(dllexport) struct objc_class _NSConcreteGlobalBlock;
extern __declspec(dllexport) void _Block_object_assign(void *, const void *,
    const int);
extern __declspec(dllexport) void _Block_object_dispose(const void *,
    const int);
# endif
#ifdef __cplusplus
}
#endif

#ifndef Block_copy
# define Block_copy(...) \
    ((__typeof__(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
#endif
#ifndef Block_release
# define Block_release(...) _Block_release((const void *)(__VA_ARGS__))
#endif

OF_ASSUME_NONNULL_END

Modified src/OFBlock.m from [a597363d01] to [0c18ed18b9].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdint.h>
#include <stdlib.h>
#include <string.h>

#import "OFBlock.h"







#import "OFAllocFailedException.h"
#import "OFInitializationFailedException.h"

#if defined(OF_OBJFW_RUNTIME)
# import "runtime/private.h"
#endif

#ifdef OF_HAVE_ATOMIC_OPS
# import "atomic.h"
#endif
#ifdef OF_HAVE_THREADS

# import "mutex.h"





#endif


typedef struct of_block_byref_t of_block_byref_t;
struct of_block_byref_t {
	Class isa;
	of_block_byref_t *forwarding;
	int flags;
	int size;
	void (*byref_keep)(void *dest, void *src);
	void (*byref_dispose)(void *);
};

enum {
	OF_BLOCK_HAS_COPY_DISPOSE = (1 << 25),
	OF_BLOCK_HAS_CTOR	  = (1 << 26),
	OF_BLOCK_IS_GLOBAL	  = (1 << 28),
	OF_BLOCK_HAS_STRET	  = (1 << 29),
	OF_BLOCK_HAS_SIGNATURE	  = (1 << 30)
};
#define OF_BLOCK_REFCOUNT_MASK 0xFFFF

enum {
	OF_BLOCK_FIELD_IS_OBJECT =   3,
	OF_BLOCK_FIELD_IS_BLOCK	 =   7,
	OF_BLOCK_FIELD_IS_BYREF	 =   8,
	OF_BLOCK_FIELD_IS_WEAK	 =  16,
	OF_BLOCK_BYREF_CALLER	 = 128
};

@protocol RetainRelease
- (instancetype)retain;
- (void)release;
@end

#ifdef OF_OBJFW_RUNTIME
/* Begin of ObjC module */
static struct objc_class _NSConcreteStackBlock_metaclass = {
	Nil, Nil, "OFStackBlock", 8, OBJC_CLASS_INFO_METACLASS,
	sizeof(_NSConcreteStackBlock_metaclass), NULL, NULL
};

struct objc_class _NSConcreteStackBlock = {
	&_NSConcreteStackBlock_metaclass, (Class)(void *)"OFBlock",
	"OFStackBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(of_block_literal_t),
	NULL, NULL
};

static struct objc_class _NSConcreteGlobalBlock_metaclass = {
	Nil, Nil, "OFGlobalBlock", 8, OBJC_CLASS_INFO_METACLASS,
	sizeof(_NSConcreteGlobalBlock_metaclass), NULL, NULL
};

struct objc_class _NSConcreteGlobalBlock = {
	&_NSConcreteGlobalBlock_metaclass, (Class)(void *)"OFBlock",
	"OFGlobalBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(of_block_literal_t),
	NULL, NULL
};

static struct objc_class _NSConcreteMallocBlock_metaclass = {
	Nil, Nil, "OFMallocBlock", 8, OBJC_CLASS_INFO_METACLASS,
	sizeof(_NSConcreteMallocBlock_metaclass), NULL, NULL
};

struct objc_class _NSConcreteMallocBlock = {
	&_NSConcreteMallocBlock_metaclass, (Class)(void *)"OFBlock",
	"OFMallocBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(of_block_literal_t),
	NULL, NULL
};

static struct {
	unsigned long unknown;
	struct objc_selector *selectorRefs;
	uint16_t classDefsCount, categoryDefsCount;

<
<
|




















>
>
>
>
>
>








|
|
|
|
>
|
>
>
>
>
>
|
>

<
|

|


|
|



|
|
|
|
|

|


|
|
|
|
|
















|










|










|







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


 * 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 <stdint.h>
#include <stdlib.h>
#include <string.h>

#import "OFBlock.h"
#ifdef OF_HAVE_ATOMIC_OPS
# import "OFAtomic.h"
#endif
#ifdef OF_HAVE_THREADS
# import "OFPlainMutex.h"
#endif

#import "OFAllocFailedException.h"
#import "OFInitializationFailedException.h"

#if defined(OF_OBJFW_RUNTIME)
# import "runtime/private.h"
#endif

struct Block {
	Class isa;
	int flags;
	int reserved;
	void (*invoke)(void *block, ...);
	struct {
		unsigned long reserved;
		unsigned long size;
		void (*_Nullable copyHelper)(void *dest, void *src);
		void (*_Nullable disposeHelper)(void *src);
		const char *signature;
	} *descriptor;
};


struct Byref {
	Class isa;
	struct Byref *forwarding;
	int flags;
	int size;
	void (*keepByref)(void *dest, void *src);
	void (*disposeByref)(void *);
};

enum {
	OFBlockHasCopyDispose = (1 << 25),
	OFBlockHasCtor	      = (1 << 26),
	OFBlockIsGlobal	      = (1 << 28),
	OFBlockHasStret	      = (1 << 29),
	OFBlockHasSignature   = (1 << 30)
};
#define OFBlockRefCountMask 0xFFFF

enum {
	OFBlockFieldIsObject =   3,
	OFBlockFieldIsBlock  =   7,
	OFBlockFieldIsByref  =   8,
	OFBlockFieldIsWeak   =  16,
	OFBlockByrefCaller   = 128
};

@protocol RetainRelease
- (instancetype)retain;
- (void)release;
@end

#ifdef OF_OBJFW_RUNTIME
/* Begin of ObjC module */
static struct objc_class _NSConcreteStackBlock_metaclass = {
	Nil, Nil, "OFStackBlock", 8, OBJC_CLASS_INFO_METACLASS,
	sizeof(_NSConcreteStackBlock_metaclass), NULL, NULL
};

struct objc_class _NSConcreteStackBlock = {
	&_NSConcreteStackBlock_metaclass, (Class)(void *)"OFBlock",
	"OFStackBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(struct Block),
	NULL, NULL
};

static struct objc_class _NSConcreteGlobalBlock_metaclass = {
	Nil, Nil, "OFGlobalBlock", 8, OBJC_CLASS_INFO_METACLASS,
	sizeof(_NSConcreteGlobalBlock_metaclass), NULL, NULL
};

struct objc_class _NSConcreteGlobalBlock = {
	&_NSConcreteGlobalBlock_metaclass, (Class)(void *)"OFBlock",
	"OFGlobalBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(struct Block),
	NULL, NULL
};

static struct objc_class _NSConcreteMallocBlock_metaclass = {
	Nil, Nil, "OFMallocBlock", 8, OBJC_CLASS_INFO_METACLASS,
	sizeof(_NSConcreteMallocBlock_metaclass), NULL, NULL
};

struct objc_class _NSConcreteMallocBlock = {
	&_NSConcreteMallocBlock_metaclass, (Class)(void *)"OFBlock",
	"OFMallocBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(struct Block),
	NULL, NULL
};

static struct {
	unsigned long unknown;
	struct objc_selector *selectorRefs;
	uint16_t classDefsCount, categoryDefsCount;
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
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
#endif

static struct {
	Class isa;
} alloc_failed_exception;

#ifndef OF_HAVE_ATOMIC_OPS
# define NUM_SPINLOCKS 8	/* needs to be a power of 2 */
# define SPINLOCK_HASH(p) ((uintptr_t)p >> 4) & (NUM_SPINLOCKS - 1)
static of_spinlock_t blockSpinlocks[NUM_SPINLOCKS];
static of_spinlock_t byrefSpinlocks[NUM_SPINLOCKS];
#endif

void *
_Block_copy(const void *block_)
{
	of_block_literal_t *block = (of_block_literal_t *)block_;

	if ([(id)block isMemberOfClass: (Class)&_NSConcreteStackBlock]) {
		of_block_literal_t *copy;

		if ((copy = malloc(block->descriptor->size)) == NULL) {
			alloc_failed_exception.isa =
			    [OFAllocFailedException class];
			@throw (OFAllocFailedException *)
			    &alloc_failed_exception;
		}
		memcpy(copy, block, block->descriptor->size);

		object_setClass((id)copy, (Class)&_NSConcreteMallocBlock);
		copy->flags++;

		if (block->flags & OF_BLOCK_HAS_COPY_DISPOSE)
			block->descriptor->copy_helper(copy, block);

		return copy;
	}

	if ([(id)block isMemberOfClass: (Class)&_NSConcreteMallocBlock]) {
#ifdef OF_HAVE_ATOMIC_OPS
		of_atomic_int_inc(&block->flags);
#else
		unsigned hash = SPINLOCK_HASH(block);

		OF_ENSURE(of_spinlock_lock(&blockSpinlocks[hash]));
		block->flags++;
		OF_ENSURE(of_spinlock_unlock(&blockSpinlocks[hash]));
#endif
	}

	return block;
}

void
_Block_release(const void *block_)
{
	of_block_literal_t *block = (of_block_literal_t *)block_;

	if (object_getClass((id)block) != (Class)&_NSConcreteMallocBlock)
		return;

#ifdef OF_HAVE_ATOMIC_OPS
	if ((of_atomic_int_dec(&block->flags) & OF_BLOCK_REFCOUNT_MASK) == 0) {
		if (block->flags & OF_BLOCK_HAS_COPY_DISPOSE)
			block->descriptor->dispose_helper(block);

		free(block);
	}
#else
	unsigned hash = SPINLOCK_HASH(block);

	OF_ENSURE(of_spinlock_lock(&blockSpinlocks[hash]));
	if ((--block->flags & OF_BLOCK_REFCOUNT_MASK) == 0) {
		OF_ENSURE(of_spinlock_unlock(&blockSpinlocks[hash]));

		if (block->flags & OF_BLOCK_HAS_COPY_DISPOSE)
			block->descriptor->dispose_helper(block);

		free(block);

		return;
	}
	OF_ENSURE(of_spinlock_unlock(&blockSpinlocks[hash]));
#endif
}

void
_Block_object_assign(void *dst_, const void *src_, const int flags_)
{
	int flags = flags_ & (OF_BLOCK_FIELD_IS_BLOCK |
	    OF_BLOCK_FIELD_IS_OBJECT | OF_BLOCK_FIELD_IS_BYREF);

	if (src_ == NULL)
		return;

	switch (flags) {
	case OF_BLOCK_FIELD_IS_BLOCK:
		*(of_block_literal_t **)dst_ = _Block_copy(src_);
		break;
	case OF_BLOCK_FIELD_IS_OBJECT:
		if (!(flags_ & OF_BLOCK_BYREF_CALLER))
			*(id *)dst_ = [(id)src_ retain];
		break;
	case OF_BLOCK_FIELD_IS_BYREF:;
		of_block_byref_t *src = (of_block_byref_t *)src_;
		of_block_byref_t **dst = (of_block_byref_t **)dst_;

		src = src->forwarding;

		if ((src->flags & OF_BLOCK_REFCOUNT_MASK) == 0) {
			if ((*dst = malloc(src->size)) == NULL) {
				alloc_failed_exception.isa =
				    [OFAllocFailedException class];
				@throw (OFAllocFailedException *)
				    &alloc_failed_exception;
			}

			memcpy(*dst, src, src->size);
			(*dst)->flags =
			    ((*dst)->flags & ~OF_BLOCK_REFCOUNT_MASK) | 1;
			(*dst)->forwarding = *dst;

			if (src->flags & OF_BLOCK_HAS_COPY_DISPOSE)
				src->byref_keep(*dst, src);

#ifdef OF_HAVE_ATOMIC_OPS
			if (!of_atomic_ptr_cmpswap((void **)&src->forwarding,
			    src, *dst)) {
				src->byref_dispose(*dst);
				free(*dst);

				*dst = src->forwarding;
			}
#else
			unsigned hash = SPINLOCK_HASH(src);

			OF_ENSURE(of_spinlock_lock(&byrefSpinlocks[hash]));
			if (src->forwarding == src)
				src->forwarding = *dst;
			else {
				src->byref_dispose(*dst);
				free(*dst);

				*dst = src->forwarding;
			}
			OF_ENSURE(of_spinlock_unlock(&byrefSpinlocks[hash]));
#endif
		} else
			*dst = src;

#ifdef OF_HAVE_ATOMIC_OPS
		of_atomic_int_inc(&(*dst)->flags);
#else
		unsigned hash = SPINLOCK_HASH(*dst);

		OF_ENSURE(of_spinlock_lock(&byrefSpinlocks[hash]));
		(*dst)->flags++;
		OF_ENSURE(of_spinlock_unlock(&byrefSpinlocks[hash]));
#endif
		break;
	}
}

void
_Block_object_dispose(const void *object_, const int flags_)
{
	const int flags = flags_ & (OF_BLOCK_FIELD_IS_BLOCK |
	    OF_BLOCK_FIELD_IS_OBJECT | OF_BLOCK_FIELD_IS_BYREF);

	if (object_ == NULL)
		return;

	switch (flags) {
	case OF_BLOCK_FIELD_IS_BLOCK:
		_Block_release(object_);
		break;
	case OF_BLOCK_FIELD_IS_OBJECT:
		if (!(flags_ & OF_BLOCK_BYREF_CALLER))
			[(id)object_ release];
		break;
	case OF_BLOCK_FIELD_IS_BYREF:;
		of_block_byref_t *object = (of_block_byref_t *)object_;

		object = object->forwarding;

#ifdef OF_HAVE_ATOMIC_OPS
		if ((of_atomic_int_dec(&object->flags) &
		    OF_BLOCK_REFCOUNT_MASK) == 0) {
			if (object->flags & OF_BLOCK_HAS_COPY_DISPOSE)
				object->byref_dispose(object);

			free(object);
		}
#else
		unsigned hash = SPINLOCK_HASH(object);

		OF_ENSURE(of_spinlock_lock(&byrefSpinlocks[hash]));
		if ((--object->flags & OF_BLOCK_REFCOUNT_MASK) == 0) {
			OF_ENSURE(of_spinlock_unlock(&byrefSpinlocks[hash]));

			if (object->flags & OF_BLOCK_HAS_COPY_DISPOSE)
				object->byref_dispose(object);

			free(object);
		}
		OF_ENSURE(of_spinlock_unlock(&byrefSpinlocks[hash]));
#endif
		break;
	}
}

@implementation OFBlock
+ (void)load
{
#ifndef OF_HAVE_ATOMIC_OPS
	for (size_t i = 0; i < NUM_SPINLOCKS; i++)
		if (!of_spinlock_new(&blockSpinlocks[i]) ||
		    !of_spinlock_new(&byrefSpinlocks[i]))
			@throw [OFInitializationFailedException
			    exceptionWithClass: self];
#endif

#ifdef OF_APPLE_RUNTIME
	Class tmp;








|
|
|
|





|


|












|
|






|



|

|









|





|
|
|






|
|
|

|
|





|






|
|





|
|

|
|


|
|
|



|









|


|
|


|
|
|







|



|




|





|



|

|








|
|





|


|
|


|
|




|
|
|
|






|
|
|

|
|



|









|
|
|







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
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
#endif

static struct {
	Class isa;
} alloc_failed_exception;

#ifndef OF_HAVE_ATOMIC_OPS
# define numSpinlocks 8	/* needs to be a power of 2 */
# define SPINLOCK_HASH(p) ((uintptr_t)p >> 4) & (numSpinlocks - 1)
static OFSpinlock blockSpinlocks[numSpinlocks];
static OFSpinlock byrefSpinlocks[numSpinlocks];
#endif

void *
_Block_copy(const void *block_)
{
	struct Block *block = (struct Block *)block_;

	if ([(id)block isMemberOfClass: (Class)&_NSConcreteStackBlock]) {
		struct Block *copy;

		if ((copy = malloc(block->descriptor->size)) == NULL) {
			alloc_failed_exception.isa =
			    [OFAllocFailedException class];
			@throw (OFAllocFailedException *)
			    &alloc_failed_exception;
		}
		memcpy(copy, block, block->descriptor->size);

		object_setClass((id)copy, (Class)&_NSConcreteMallocBlock);
		copy->flags++;

		if (block->flags & OFBlockHasCopyDispose)
			block->descriptor->copyHelper(copy, block);

		return copy;
	}

	if ([(id)block isMemberOfClass: (Class)&_NSConcreteMallocBlock]) {
#ifdef OF_HAVE_ATOMIC_OPS
		OFAtomicIntIncrease(&block->flags);
#else
		unsigned hash = SPINLOCK_HASH(block);

		OFEnsure(OFSpinlockLock(&blockSpinlocks[hash]) == 0);
		block->flags++;
		OFEnsure(OFSpinlockUnlock(&blockSpinlocks[hash]) == 0);
#endif
	}

	return block;
}

void
_Block_release(const void *block_)
{
	struct Block *block = (struct Block *)block_;

	if (object_getClass((id)block) != (Class)&_NSConcreteMallocBlock)
		return;

#ifdef OF_HAVE_ATOMIC_OPS
	if ((OFAtomicIntDecrease(&block->flags) & OFBlockRefCountMask) == 0) {
		if (block->flags & OFBlockHasCopyDispose)
			block->descriptor->disposeHelper(block);

		free(block);
	}
#else
	unsigned hash = SPINLOCK_HASH(block);

	OFEnsure(OFSpinlockLock(&blockSpinlocks[hash]) == 0);
	if ((--block->flags & OFBlockRefCountMask) == 0) {
		OFEnsure(OFSpinlockUnlock(&blockSpinlocks[hash]) == 0);

		if (block->flags & OFBlockHasCopyDispose)
			block->descriptor->disposeHelper(block);

		free(block);

		return;
	}
	OFEnsure(OFSpinlockUnlock(&blockSpinlocks[hash]) == 0);
#endif
}

void
_Block_object_assign(void *dst_, const void *src_, const int flags_)
{
	int flags = flags_ & (OFBlockFieldIsBlock | OFBlockFieldIsObject |
	    OFBlockFieldIsByref);

	if (src_ == NULL)
		return;

	switch (flags) {
	case OFBlockFieldIsBlock:
		*(struct Block **)dst_ = _Block_copy(src_);
		break;
	case OFBlockFieldIsObject:
		if (!(flags_ & OFBlockByrefCaller))
			*(id *)dst_ = [(id)src_ retain];
		break;
	case OFBlockFieldIsByref:;
		struct Byref *src = (struct Byref *)src_;
		struct Byref **dst = (struct Byref **)dst_;

		src = src->forwarding;

		if ((src->flags & OFBlockRefCountMask) == 0) {
			if ((*dst = malloc(src->size)) == NULL) {
				alloc_failed_exception.isa =
				    [OFAllocFailedException class];
				@throw (OFAllocFailedException *)
				    &alloc_failed_exception;
			}

			memcpy(*dst, src, src->size);
			(*dst)->flags =
			    ((*dst)->flags & ~OFBlockRefCountMask) | 1;
			(*dst)->forwarding = *dst;

			if (src->flags & OFBlockHasCopyDispose)
				src->keepByref(*dst, src);

#ifdef OF_HAVE_ATOMIC_OPS
			if (!OFAtomicPointerCompareAndSwap(
			    (void **)&src->forwarding, src, *dst)) {
				src->disposeByref(*dst);
				free(*dst);

				*dst = src->forwarding;
			}
#else
			unsigned hash = SPINLOCK_HASH(src);

			OFEnsure(OFSpinlockLock(&byrefSpinlocks[hash]) == 0);
			if (src->forwarding == src)
				src->forwarding = *dst;
			else {
				src->disposeByref(*dst);
				free(*dst);

				*dst = src->forwarding;
			}
			OFEnsure(OFSpinlockUnlock(&byrefSpinlocks[hash]) == 0);
#endif
		} else
			*dst = src;

#ifdef OF_HAVE_ATOMIC_OPS
		OFAtomicIntIncrease(&(*dst)->flags);
#else
		unsigned hash = SPINLOCK_HASH(*dst);

		OFEnsure(OFSpinlockLock(&byrefSpinlocks[hash]) == 0);
		(*dst)->flags++;
		OFEnsure(OFSpinlockUnlock(&byrefSpinlocks[hash]) == 0);
#endif
		break;
	}
}

void
_Block_object_dispose(const void *object_, const int flags_)
{
	const int flags = flags_ & (OFBlockFieldIsBlock | OFBlockFieldIsObject |
	    OFBlockFieldIsByref);

	if (object_ == NULL)
		return;

	switch (flags) {
	case OFBlockFieldIsBlock:
		_Block_release(object_);
		break;
	case OFBlockFieldIsObject:
		if (!(flags_ & OFBlockByrefCaller))
			[(id)object_ release];
		break;
	case OFBlockFieldIsByref:;
		struct Byref *object = (struct Byref *)object_;

		object = object->forwarding;

#ifdef OF_HAVE_ATOMIC_OPS
		if ((OFAtomicIntDecrease(&object->flags) &
		    OFBlockRefCountMask) == 0) {
			if (object->flags & OFBlockHasCopyDispose)
				object->disposeByref(object);

			free(object);
		}
#else
		unsigned hash = SPINLOCK_HASH(object);

		OFEnsure(OFSpinlockLock(&byrefSpinlocks[hash]) == 0);
		if ((--object->flags & OFBlockRefCountMask) == 0) {
			OFEnsure(OFSpinlockUnlock(&byrefSpinlocks[hash]) == 0);

			if (object->flags & OFBlockHasCopyDispose)
				object->disposeByref(object);

			free(object);
		}
		OFEnsure(OFSpinlockUnlock(&byrefSpinlocks[hash]) == 0);
#endif
		break;
	}
}

@implementation OFBlock
+ (void)load
{
#ifndef OF_HAVE_ATOMIC_OPS
	for (size_t i = 0; i < numSpinlocks; i++)
		if (OFSpinlockNew(&blockSpinlocks[i]) != 0 ||
		    OFSpinlockNew(&byrefSpinlocks[i]) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self];
#endif

#ifdef OF_APPLE_RUNTIME
	Class tmp;

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
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void *)allocMemoryWithSize: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)allocMemoryWithSize: (size_t)size
			count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)resizeMemory: (void *)ptr
		  size: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)resizeMemory: (void *)ptr
		  size: (size_t)size
		 count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)freeMemory: (void *)ptr
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)retain
{
	if ([self isMemberOfClass: (Class)&_NSConcreteMallocBlock])
		return Block_copy(self);

	return self;
}







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







445
446
447
448
449
450
451





























452
453
454
455
456
457
458
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}






























- (instancetype)retain
{
	if ([self isMemberOfClass: (Class)&_NSConcreteMallocBlock])
		return Block_copy(self);

	return self;
}
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511

	return self;
}

- (unsigned int)retainCount
{
	if ([self isMemberOfClass: (Class)&_NSConcreteMallocBlock])
		return ((of_block_literal_t *)self)->flags &
		    OF_BLOCK_REFCOUNT_MASK;

	return OF_RETAIN_COUNT_MAX;
}

- (void)release
{
	if ([self isMemberOfClass: (Class)&_NSConcreteMallocBlock])
		Block_release(self);
}

- (void)dealloc
{
	OF_DEALLOC_UNSUPPORTED
}
@end







|
<

|













469
470
471
472
473
474
475
476

477
478
479
480
481
482
483
484
485
486
487
488
489
490
491

	return self;
}

- (unsigned int)retainCount
{
	if ([self isMemberOfClass: (Class)&_NSConcreteMallocBlock])
		return ((struct Block *)self)->flags & OFBlockRefCountMask;


	return OFMaxRetainCount;
}

- (void)release
{
	if ([self isMemberOfClass: (Class)&_NSConcreteMallocBlock])
		Block_release(self);
}

- (void)dealloc
{
	OF_DEALLOC_UNSUPPORTED
}
@end

Modified src/OFBytesValue.h from [cfc1c393f0] to [ca1dbf0669].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFBytesValue.m from [812d7bedad] to [b331442837].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType
{
	self = [super init];

	@try {
		_size = of_sizeof_type_encoding(objCType);
		_objCType = objCType;
		_bytes = [self allocMemoryWithSize: _size];

		memcpy(_bytes, bytes, _size);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)getValue: (void *)value






	    size: (size_t)size
{
	if (size != _size)
		@throw [OFOutOfRangeException exception];

	memcpy(value, _bytes, _size);
}
@end







|

|










|
>
>
>
>
>
>
|







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

- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType
{
	self = [super init];

	@try {
		_size = OFSizeOfTypeEncoding(objCType);
		_objCType = objCType;
		_bytes = OFAllocMemory(1, _size);

		memcpy(_bytes, bytes, _size);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	OFFreeMemory(_bytes);

	[super dealloc];
}

- (void)getValue: (void *)value size: (size_t)size
{
	if (size != _size)
		@throw [OFOutOfRangeException exception];

	memcpy(value, _bytes, _size);
}
@end

Renamed and modified src/crc16.h [4ab5578211] to src/OFCRC16.h [6bd74791bc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
23
24
25
26
27
28
29
30
31
32
33
34
#endif

#import "macros.h"

#ifdef __cplusplus
extern "C" {
#endif
extern uint16_t of_crc16(uint16_t crc, const void *_Nonnull bytes,
    size_t length);
#ifdef __cplusplus
}
#endif







|




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

#import "macros.h"

#ifdef __cplusplus
extern "C" {
#endif
extern uint16_t OFCRC16(uint16_t crc, const void *_Nonnull bytes,
    size_t length);
#ifdef __cplusplus
}
#endif

Renamed and modified src/crc16.m [8d831852e3] to src/OFCRC16.m [ba4e599142].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "crc16.h"

#define CRC16_MAGIC 0xA001

uint16_t
of_crc16(uint16_t crc, const void *bytes_, size_t length)
{
	const unsigned char *bytes = bytes_;

	for (size_t i = 0; i < length; i++) {
		crc ^= bytes[i];

		for (uint8_t j = 0; j < 8; j++)
			crc = (crc >> 1) ^ (CRC16_MAGIC & (~(crc & 1) + 1));
	}

	return crc;
}

<
<
|















|

|


|




|


|


|

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


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

#import "OFCRC16.h"

static const uint16_t CRC16Magic = 0xA001;

uint16_t
OFCRC16(uint16_t CRC, const void *bytes_, size_t length)
{
	const unsigned char *bytes = bytes_;

	for (size_t i = 0; i < length; i++) {
		CRC ^= bytes[i];

		for (uint8_t j = 0; j < 8; j++)
			CRC = (CRC >> 1) ^ (CRC16Magic & (~(CRC & 1) + 1));
	}

	return CRC;
}

Renamed and modified src/crc32.h [8683988c03] to src/OFCRC32.h [139d8dccac].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
23
24
25
26
27
28
29
30
31
32
33
34
#endif

#import "macros.h"

#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t of_crc32(uint32_t crc, const void *_Nonnull bytes,
    size_t length);
#ifdef __cplusplus
}
#endif







|




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

#import "macros.h"

#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t OFCRC32(uint32_t crc, const void *_Nonnull bytes,
    size_t length);
#ifdef __cplusplus
}
#endif

Renamed and modified src/crc32.m [04306be63c] to src/OFCRC32.m [b50ad38210].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "crc32.h"

#define CRC32_MAGIC 0xEDB88320

uint32_t
of_crc32(uint32_t crc, const void *bytes_, size_t length)
{
	const unsigned char *bytes = bytes_;

	for (size_t i = 0; i < length; i++) {
		crc ^= bytes[i];

		for (uint8_t j = 0; j < 8; j++)
			crc = (crc >> 1) ^ (CRC32_MAGIC & (~(crc & 1) + 1));
	}

	return crc;
}

<
<
|















|

|


|




|


|


|

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


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

#import "OFCRC32.h"

static const uint32_t CRC32Magic = 0xEDB88320;

uint32_t
OFCRC32(uint32_t CRC, const void *bytes_, size_t length)
{
	const unsigned char *bytes = bytes_;

	for (size_t i = 0; i < length; i++) {
		CRC ^= bytes[i];

		for (uint8_t j = 0; j < 8; j++)
			CRC = (CRC >> 1) ^ (CRC32Magic & (~(CRC & 1) + 1));
	}

	return CRC;
}

Modified src/OFCharacterSet.h from [c111b41606] to [20dd965c47].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
 * @brief Creates a new character set containing the characters in the specified
 *	  range.
 *
 * @param range The range of characters for the character set
 * @return A new OFCharacterSet
 */
+ (instancetype)characterSetWithRange: (of_range_t)range;

/**
 * @brief A character set containing all Unicode characters in the category
 *	  `Zs` plus CHARACTER TABULATION (U+0009).
 */
+ (OFCharacterSet *)whitespaceCharacterSet;








|







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
 * @brief Creates a new character set containing the characters in the specified
 *	  range.
 *
 * @param range The range of characters for the character set
 * @return A new OFCharacterSet
 */
+ (instancetype)characterSetWithRange: (OFRange)range;

/**
 * @brief A character set containing all Unicode characters in the category
 *	  `Zs` plus CHARACTER TABULATION (U+0009).
 */
+ (OFCharacterSet *)whitespaceCharacterSet;

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
 * @brief Initializes an already allocated character set with the characters in
 *	  the specified range.
 *
 * @param range The range of characters for the character set
 * @return An initialized OFCharacterSet
 */
- (instancetype)initWithRange: (of_range_t)range;

/**
 * @brief Returns whether the specified character is a member of the character
 *	  set.
 *
 * @param character The character that is checked for being a member of the
 *		    character set
 * @return Whether the specified character is a member of the character set.
 */
- (bool)characterIsMember: (of_unichar_t)character;
@end

OF_ASSUME_NONNULL_END







|









|



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
 * @brief Initializes an already allocated character set with the characters in
 *	  the specified range.
 *
 * @param range The range of characters for the character set
 * @return An initialized OFCharacterSet
 */
- (instancetype)initWithRange: (OFRange)range;

/**
 * @brief Returns whether the specified character is a member of the character
 *	  set.
 *
 * @param character The character that is checked for being a member of the
 *		    character set
 * @return Whether the specified character is a member of the character set.
 */
- (bool)characterIsMember: (OFUnichar)character;
@end

OF_ASSUME_NONNULL_END

Modified src/OFCharacterSet.m from [7a7a3d4551] to [76e8828364].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFCharacterSet.h"
#import "OFBitSetCharacterSet.h"
#import "OFInvertedCharacterSet.h"
#import "OFRangeCharacterSet.h"

#import "once.h"

@interface OFPlaceholderCharacterSet: OFCharacterSet
@end

@interface OFWhitespaceCharacterSet: OFCharacterSet
@end

static struct {

<
<
|


















|
<
|








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


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

#import "OFCharacterSet.h"
#import "OFBitSetCharacterSet.h"
#import "OFInvertedCharacterSet.h"
#import "OFOnce.h"

#import "OFRangeCharacterSet.h"

@interface OFPlaceholderCharacterSet: OFCharacterSet
@end

@interface OFWhitespaceCharacterSet: OFCharacterSet
@end

static struct {
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

- (instancetype)initWithCharactersInString: (OFString *)characters
{
	return (id)[[OFBitSetCharacterSet alloc]
	    initWithCharactersInString: characters];
}

- (instancetype)initWithRange: (of_range_t)range
{
	return (id)[[OFRangeCharacterSet alloc] initWithRange: range];
}

- (instancetype)retain
{
	return self;







|







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

- (instancetype)initWithCharactersInString: (OFString *)characters
{
	return (id)[[OFBitSetCharacterSet alloc]
	    initWithCharactersInString: characters];
}

- (instancetype)initWithRange: (OFRange)range
{
	return (id)[[OFRangeCharacterSet alloc] initWithRange: range];
}

- (instancetype)retain
{
	return self;
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

+ (instancetype)characterSetWithCharactersInString: (OFString *)characters
{
	return [[[self alloc] initWithCharactersInString: characters]
	    autorelease];
}

+ (instancetype)characterSetWithRange: (of_range_t)range
{
	return [[[self alloc] initWithRange: range] autorelease];
}

+ (OFCharacterSet *)whitespaceCharacterSet
{
	static of_once_t onceControl = OF_ONCE_INIT;
	of_once(&onceControl, initWhitespaceCharacterSet);

	return whitespaceCharacterSet;
}

- (instancetype)init
{
	if ([self isMemberOfClass: [OFCharacterSet class]]) {







|






|
|







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

+ (instancetype)characterSetWithCharactersInString: (OFString *)characters
{
	return [[[self alloc] initWithCharactersInString: characters]
	    autorelease];
}

+ (instancetype)characterSetWithRange: (OFRange)range
{
	return [[[self alloc] initWithRange: range] autorelease];
}

+ (OFCharacterSet *)whitespaceCharacterSet
{
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, initWhitespaceCharacterSet);

	return whitespaceCharacterSet;
}

- (instancetype)init
{
	if ([self isMemberOfClass: [OFCharacterSet class]]) {
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
}

- (instancetype)initWithCharactersInString: (OFString *)characters
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRange: (of_range_t)range
{
	OF_INVALID_INIT_METHOD
}

- (bool)characterIsMember: (of_unichar_t)character
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFCharacterSet *)invertedSet
{
	return [[[OFInvertedCharacterSet alloc]







|




|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
}

- (instancetype)initWithCharactersInString: (OFString *)characters
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRange: (OFRange)range
{
	OF_INVALID_INIT_METHOD
}

- (bool)characterIsMember: (OFUnichar)character
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFCharacterSet *)invertedSet
{
	return [[[OFInvertedCharacterSet alloc]
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

- (void)release
{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

- (bool)characterIsMember: (of_unichar_t)character
{
	switch (character) {
	case 0x0009:
	case 0x0020:
	case 0x00A0:
	case 0x1680:
	case 0x2000:







|


|







163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

- (bool)characterIsMember: (OFUnichar)character
{
	switch (character) {
	case 0x0009:
	case 0x0020:
	case 0x00A0:
	case 0x1680:
	case 0x2000:

Modified src/OFCollection.h from [045f1fdf5c] to [8d8461caff].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
OF_ASSUME_NONNULL_BEGIN

/**
 * @protocol OFCollection OFCollection.h ObjFW/OFCollection.h
 *
 * @brief A protocol with methods common for all collections.
 */
@protocol OFCollection <OFEnumerating, OFFastEnumeration>
/**
 * @brief The number of objects in the collection
 */
@property (readonly, nonatomic) size_t count;

/**
 * @brief Checks whether the collection contains an object equal to the







|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
OF_ASSUME_NONNULL_BEGIN

/**
 * @protocol OFCollection OFCollection.h ObjFW/OFCollection.h
 *
 * @brief A protocol with methods common for all collections.
 */
@protocol OFCollection <OFEnumeration, OFFastEnumeration>
/**
 * @brief The number of objects in the collection
 */
@property (readonly, nonatomic) size_t count;

/**
 * @brief Checks whether the collection contains an object equal to the

Modified src/OFColor.h from [b42dcb2b78] to [bce9432d45].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

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

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFColor.h"

#import "once.h"

#import "OFInvalidArgumentException.h"

@implementation OFColor
#define PREDEFINED_COLOR(name, r, g, b)					\
	static OFColor *name##Color = nil;				\
									\
	static void							\
	initPredefinedColor_##name(void)				\
	{								\
		name##Color = [[OFColor alloc] initWithRed: r		\
						     green: g		\
						      blue: b		\
						     alpha: 1];		\
	}								\
									\
	+ (OFColor *)name						\
	{								\
		static of_once_t onceControl = OF_ONCE_INIT;		\
		of_once(&onceControl, initPredefinedColor_##name);	\
									\
		return name##Color;					\
	}

PREDEFINED_COLOR(black,   0.00f, 0.00f, 0.00f)
PREDEFINED_COLOR(silver,  0.75f, 0.75f, 0.75f)
PREDEFINED_COLOR(grey,    0.50f, 0.50f, 0.50f)
PREDEFINED_COLOR(white,   1.00f, 1.00f, 1.00f)
PREDEFINED_COLOR(maroon,  0.50f, 0.00f, 0.00f)

<
<
|
















<
|




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







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


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

#import "OFColor.h"

#import "OFOnce.h"

#import "OFInvalidArgumentException.h"

@implementation OFColor
#define PREDEFINED_COLOR(name, redValue, greenValue, blueValue)		   \
	static OFColor *name##Color = nil;				   \
									   \
	static void							   \
	initPredefinedColor_##name(void)				   \
	{								   \
		name##Color = [[OFColor alloc] initWithRed: redValue	   \
						     green: greenValue	   \
						      blue: blueValue	   \
						     alpha: 1];		   \
	}								   \
									   \
	+ (OFColor *)name						   \
	{								   \
		static OFOnceControl onceControl = OFOnceControlInitValue; \
		OFOnce(&onceControl, initPredefinedColor_##name);	   \
									   \
		return name##Color;					   \
	}

PREDEFINED_COLOR(black,   0.00f, 0.00f, 0.00f)
PREDEFINED_COLOR(silver,  0.75f, 0.75f, 0.75f)
PREDEFINED_COLOR(grey,    0.50f, 0.50f, 0.50f)
PREDEFINED_COLOR(white,   1.00f, 1.00f, 1.00f)
PREDEFINED_COLOR(maroon,  0.50f, 0.00f, 0.00f)
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;
	float tmp;

	OF_HASH_INIT(hash);

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

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

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

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

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (void)getRed: (float *)red
	 green: (float *)green
	  blue: (float *)blue







|


|

|

|

|

|

|

|

|

|

|







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
		return false;

	return true;
}

- (unsigned long)hash
{
	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
	 green: (float *)green
	  blue: (float *)blue

Modified src/OFCondition.h from [bee8a8b425] to [b8eb2412a1].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFMutex.h"

#import "condition.h"

OF_ASSUME_NONNULL_BEGIN

@class OFDate;

/**
 * @class OFCondition OFCondition.h ObjFW/OFCondition.h
 *
 * @brief A class implementing a condition variable for thread synchronization.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFCondition: OFMutex
{
	of_condition_t _condition;
	bool _conditionInitialized;
}

/**
 * @brief Creates a new condition.
 *
 * @return A new, autoreleased OFCondition

<
<
|














<
|













|







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


 * 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 "OFMutex.h"

#import "OFPlainCondition.h"

OF_ASSUME_NONNULL_BEGIN

@class OFDate;

/**
 * @class OFCondition OFCondition.h ObjFW/OFCondition.h
 *
 * @brief A class implementing a condition variable for thread synchronization.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFCondition: OFMutex
{
	OFPlainCondition _condition;
	bool _conditionInitialized;
}

/**
 * @brief Creates a new condition.
 *
 * @return A new, autoreleased OFCondition
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
 *
 * @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: (of_time_interval_t)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: (of_time_interval_t)timeInterval
	       orExecSignal: (ULONG *)signalMask;
#endif

/**
 * @brief Blocks the current thread until another thread calls @ref signal,
 *	  @ref broadcast or the timeout is reached.
 *







|













|







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
 *
 * @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.
 *
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
 * @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;








|
<







109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
 * @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;

Modified src/OFCondition.m from [24791db544] to [8de6591c81].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	if (!of_condition_new(&_condition)) {
		Class c = self.class;
		[self release];
		@throw [OFInitializationFailedException exceptionWithClass: c];
	}

	_conditionInitialized = true;

	return self;
}

- (void)dealloc
{
	if (_conditionInitialized) {

		if (!of_condition_free(&_condition)) {

			OF_ENSURE(errno == EBUSY);

			@throw [OFConditionStillWaitingException
			    exceptionWithCondition: self];
		}
	}

	[super dealloc];
}

- (void)wait
{
	if (!of_condition_wait(&_condition, &_mutex))


		@throw [OFConditionWaitFailedException
		    exceptionWithCondition: self
				     errNo: errno];
}

#ifdef OF_AMIGAOS
- (void)waitForConditionOrExecSignal: (ULONG *)signalMask
{
	if (!of_condition_wait_or_signal(&_condition, &_mutex, signalMask))



		@throw [OFConditionWaitFailedException
		    exceptionWithCondition: self
				     errNo: errno];
}
#endif

- (bool)waitForTimeInterval: (of_time_interval_t)timeInterval
{
	if (!of_condition_timed_wait(&_condition, &_mutex, timeInterval)) {


		if (errno == ETIMEDOUT)
			return false;
		else

			@throw [OFConditionWaitFailedException
			    exceptionWithCondition: self
					     errNo: errno];
	}

	return true;
}

#ifdef OF_AMIGAOS
- (bool)waitForTimeInterval: (of_time_interval_t)timeInterval
	       orExecSignal: (ULONG *)signalMask
{
	if (!of_condition_timed_wait_or_signal(&_condition, &_mutex,
	    timeInterval, signalMask)) {

		if (errno == ETIMEDOUT)
			return false;
		else

			@throw [OFConditionWaitFailedException
			    exceptionWithCondition: self
					     errNo: errno];
	}

	return true;
}
#endif

- (bool)waitUntilDate: (OFDate *)date
{
	return [self waitForTimeInterval: date.timeIntervalSinceNow];
}

#ifdef OF_AMIGAOS
- (bool)waitUntilDate: (OFDate *)date
	 orExecSignal: (ULONG *)signalMask
{
	return [self waitForTimeInterval: date.timeIntervalSinceNow
			    orExecSignal: signalMask];
}
#endif

- (void)signal
{
	if (!of_condition_signal(&_condition))


		@throw [OFConditionSignalFailedException
		    exceptionWithCondition: self
				     errNo: errno];
}

- (void)broadcast
{
	if (!of_condition_broadcast(&_condition))


		@throw [OFConditionBroadcastFailedException
		    exceptionWithCondition: self
				     errNo: errno];
}
@end







|













>
|
>
|











|
>
>


|





|
>
>
>


|



|

|
>
>
|
|
|
>
|
|
|
<





|


|
|
>
|
|
|
>
|
|
|
<











|
<








|
>
>


|




|
>
>


|


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
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	if (OFPlainConditionNew(&_condition) != 0) {
		Class c = self.class;
		[self release];
		@throw [OFInitializationFailedException exceptionWithClass: c];
	}

	_conditionInitialized = true;

	return self;
}

- (void)dealloc
{
	if (_conditionInitialized) {
		int error = OFPlainConditionFree(&_condition);

		if (error != 0) {
			OFEnsure(error == EBUSY);

			@throw [OFConditionStillWaitingException
			    exceptionWithCondition: self];
		}
	}

	[super dealloc];
}

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

- (bool)waitUntilDate: (OFDate *)date
{
	return [self waitForTimeInterval: date.timeIntervalSinceNow];
}

#ifdef OF_AMIGAOS
- (bool)waitUntilDate: (OFDate *)date orExecSignal: (ULONG *)signalMask

{
	return [self waitForTimeInterval: date.timeIntervalSinceNow
			    orExecSignal: signalMask];
}
#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

Modified src/OFConstantString.h from [00399dbd51] to [4428a8bf71].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFConstantString.m from [a6f51dcbb3] to [069ffd9312].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

@implementation OFConstantUTF8String
+ (instancetype)alloc
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)allocMemoryWithSize: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)allocMemoryWithSize: (size_t)size
			count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)resizeMemory: (void *)pointer
		  size: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)resizeMemory: (void *)pointer
		  size: (size_t)size
		 count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)freeMemory: (void *)pointer
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)retain
{
	return self;
}

- (instancetype)autorelease
{
	return self;
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

- (void)release
{
}

- (void)dealloc







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












|







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

@implementation OFConstantUTF8String
+ (instancetype)alloc
{
	OF_UNRECOGNIZED_SELECTOR
}






























- (instancetype)retain
{
	return self;
}

- (instancetype)autorelease
{
	return self;
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

- (void)release
{
}

- (void)dealloc
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
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
	objc_registerClassPair((Class)&_OFConstantStringClassReference);
#endif
}

- (void)finishInitialization
{
	@synchronized (self) {
		struct of_string_utf8_ivars *ivars;

		if ([self isMemberOfClass: [OFConstantUTF8String class]])
			return;

		if ((ivars = calloc(1, sizeof(*ivars))) == NULL)
			@throw [OFOutOfMemoryException
			    exceptionWithRequestedSize: sizeof(*ivars)];

		ivars->cString = _cString;
		ivars->cStringLength = _cStringLength;

		switch (of_string_utf8_check(ivars->cString,
		    ivars->cStringLength,
			&ivars->length)) {
			case 1:
				ivars->isUTF8 = true;
				break;
			case -1:
				free(ivars);
				@throw [OFInvalidEncodingException exception];
		}

		_cString = (char *)ivars;
		object_setClass(self, [OFConstantUTF8String class]);
	}
}

+ (instancetype)alloc
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)allocMemoryWithSize: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)allocMemoryWithSize: (size_t)size
			count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)resizeMemory: (void *)pointer
		  size: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)resizeMemory: (void *)pointer
		  size: (size_t)size
		 count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)freeMemory: (void *)pointer
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)retain
{
	return self;
}

- (instancetype)autorelease
{
	return self;
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

- (void)release
{
}

- (void)dealloc
{
	OF_DEALLOC_UNSUPPORTED
}

/*
 * In all following methods, the constant string is converted to an
 * OFConstantUTF8String and the message sent again.
 */

/* From protocol OFCopying */
- (id)copy
{
	[self finishInitialization];

	return [self copy];
}

/* From protocol OFMutableCopying */
- (id)mutableCopy
{
	[self finishInitialization];

	return [self mutableCopy];
}

/* From protocol OFComparing */
- (of_comparison_result_t)compare: (id <OFComparing>)object
{
	[self finishInitialization];

	return [self compare: object];
}

/* From OFObject, but reimplemented in OFString */
- (bool)isEqual: (id)object
{
	[self finishInitialization];

	return [self isEqual: object];
}

- (unsigned long)hash
{
	[self finishInitialization];

	return self.hash;
}

- (OFString *)description
{
	[self finishInitialization];

	return self.description;
}

/* From OFString */
- (const char *)UTF8String
{
	[self finishInitialization];

	return self.UTF8String;
}

- (size_t)getCString: (char *)cString_
	   maxLength: (size_t)maxLength
	    encoding: (of_string_encoding_t)encoding
{
	[self finishInitialization];

	return [self getCString: cString_
		      maxLength: maxLength
		       encoding: encoding];
}

- (const char *)cStringWithEncoding: (of_string_encoding_t)encoding
{
	[self finishInitialization];

	return [self cStringWithEncoding: encoding];
}

- (size_t)length
{
	[self finishInitialization];

	return self.length;
}

- (size_t)UTF8StringLength
{
	[self finishInitialization];

	return self.UTF8StringLength;
}

- (size_t)cStringLengthWithEncoding: (of_string_encoding_t)encoding
{
	[self finishInitialization];

	return [self cStringLengthWithEncoding: encoding];
}

- (of_comparison_result_t)caseInsensitiveCompare: (OFString *)otherString
{
	[self finishInitialization];

	return [self caseInsensitiveCompare: otherString];
}

- (of_unichar_t)characterAtIndex: (size_t)idx
{
	[self finishInitialization];

	return [self characterAtIndex: idx];
}

- (void)getCharacters: (of_unichar_t *)buffer
	      inRange: (of_range_t)range
{
	[self finishInitialization];

	[self getCharacters: buffer
		    inRange: range];
}

- (of_range_t)rangeOfString: (OFString *)string
{
	[self finishInitialization];

	return [self rangeOfString: string];
}

- (of_range_t)rangeOfString: (OFString *)string
		    options: (int)options
{
	[self finishInitialization];

	return [self rangeOfString: string
			   options: options];
}

- (of_range_t)rangeOfString: (OFString *)string
		    options: (int)options
		      range: (of_range_t)range
{
	[self finishInitialization];

	return [self rangeOfString: string
			   options: options
			     range: range];
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
{
	[self finishInitialization];

	return [self indexOfCharacterFromSet: characterSet];
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (int)options
{
	[self finishInitialization];

	return [self indexOfCharacterFromSet: characterSet
				     options: options];
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (int)options
			    range: (of_range_t)range
{
	[self finishInitialization];

	return [self indexOfCharacterFromSet: characterSet
				     options: options
				       range: range];
}

- (bool)containsString: (OFString *)string
{
	[self finishInitialization];

	return [self containsString: string];
}

- (OFString *)substringFromIndex: (size_t)idx
{
	[self finishInitialization];

	return [self substringFromIndex: idx];
}

- (OFString *)substringToIndex: (size_t)idx
{
	[self finishInitialization];

	return [self substringToIndex: idx];
}

- (OFString *)substringWithRange: (of_range_t)range
{
	[self finishInitialization];

	return [self substringWithRange: range];
}

- (OFString *)stringByAppendingString: (OFString *)string
{
	[self finishInitialization];

	return [self stringByAppendingString: string];
}

- (OFString *)stringByAppendingFormat: (OFConstantString *)format
			    arguments: (va_list)arguments
{
	[self finishInitialization];

	return [self stringByAppendingFormat: format
				   arguments: arguments];
}

- (OFString *)stringByAppendingPathComponent: (OFString *)component
{
	[self finishInitialization];

	return [self stringByAppendingPathComponent: component];
}

- (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];
}

- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
					   options: (int)options
					     range: (of_range_t)range
{
	[self finishInitialization];

	return [self stringByReplacingOccurrencesOfString: string
					       withString: replacement
						  options: options
						    range: range];
}

- (OFString *)uppercaseString
{
	[self finishInitialization];

	return self.uppercaseString;
}

- (OFString *)lowercaseString
{
	[self finishInitialization];

	return self.lowercaseString;
}

- (OFString *)capitalizedString
{
	[self finishInitialization];

	return self.capitalizedString;
}

- (OFString *)stringByDeletingLeadingWhitespaces
{
	[self finishInitialization];

	return self.stringByDeletingLeadingWhitespaces;
}

- (OFString *)stringByDeletingTrailingWhitespaces
{
	[self finishInitialization];

	return self.stringByDeletingTrailingWhitespaces;
}

- (OFString *)stringByDeletingEnclosingWhitespaces
{
	[self finishInitialization];

	return self.stringByDeletingEnclosingWhitespaces;
}

- (bool)hasPrefix: (OFString *)prefix
{
	[self finishInitialization];

	return [self hasPrefix: prefix];
}

- (bool)hasSuffix: (OFString *)suffix
{
	[self finishInitialization];

	return [self hasSuffix: suffix];
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
{
	[self finishInitialization];

	return [self componentsSeparatedByString: delimiter];
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
				 options: (int)options
{
	[self finishInitialization];

	return [self componentsSeparatedByString: delimiter
					 options: options];
}

- (OFArray *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
{
	[self finishInitialization];

	return [self componentsSeparatedByCharactersInSet: characterSet];
}

- (OFArray *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
				 options: (int)options
{
	[self finishInitialization];

	return [self componentsSeparatedByCharactersInSet: characterSet
						  options: options];
}

- (OFArray *)pathComponents
{
	[self finishInitialization];

	return self.pathComponents;
}

- (OFString *)lastPathComponent
{
	[self finishInitialization];

	return self.lastPathComponent;
}

- (OFString *)stringByDeletingLastPathComponent
{
	[self finishInitialization];

	return self.stringByDeletingLastPathComponent;
}

- (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
{
	[self finishInitialization];

	return self.floatValue;
}

- (double)doubleValue
{
	[self finishInitialization];

	return self.doubleValue;
}

- (const of_unichar_t *)characters
{
	[self finishInitialization];

	return self.characters;
}

- (const of_char16_t *)UTF16String
{
	[self finishInitialization];

	return self.UTF16String;
}

- (const of_char16_t *)UTF16StringWithByteOrder: (of_byte_order_t)byteOrder
{
	[self finishInitialization];

	return [self UTF16StringWithByteOrder: byteOrder];
}

- (size_t)UTF16StringLength
{
	[self finishInitialization];

	return self.UTF16StringLength;
}

- (const of_char32_t *)UTF32String
{
	[self finishInitialization];

	return self.UTF32String;
}

- (const of_char32_t *)UTF32StringWithByteOrder: (of_byte_order_t)byteOrder
{
	[self finishInitialization];

	return [self UTF32StringWithByteOrder: byteOrder];
}

- (OFData *)dataWithEncoding: (of_string_encoding_t)encoding
{
	[self finishInitialization];

	return [self dataWithEncoding: encoding];
}

#ifdef OF_HAVE_UNICODE_TABLES
- (OFString *)decomposedStringWithCanonicalMapping
{
	[self finishInitialization];

	return self.decomposedStringWithCanonicalMapping;
}

- (OFString *)decomposedStringWithCompatibilityMapping
{
	[self finishInitialization];

	return self.decomposedStringWithCompatibilityMapping;
}
#endif

#ifdef OF_WINDOWS
- (OFString *)stringByExpandingWindowsEnvironmentStrings
{
	[self finishInitialization];

	return self.stringByExpandingWindowsEnvironmentStrings;
}
#endif

#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path
{
	[self finishInitialization];

	[self writeToFile: path];
}

- (void)writeToFile: (OFString *)path
	   encoding: (of_string_encoding_t)encoding
{
	[self finishInitialization];

	[self writeToFile: path
		 encoding: encoding];
}
#endif

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

	[self writeToURL: URL];
}

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

	[self writeToURL: URL
		encoding: encoding];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (of_string_line_enumeration_block_t)block
{
	[self finishInitialization];

	[self enumerateLinesUsingBlock: block];
}
#endif
@end







|




|
<
<
<



|
<
|
|
|
|
|
|
|









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















|




















<







<



|
|


<
|






<






<






<







<





|


<





|


<






<






<



|


<



|


<
|


|


<



|
<


<
|
<


|


<



|
|


<
|
<


|
|
|


<
|
<
<





<




|


<
|
<



|
|


<








<






<






<



|


<






<







<
|
<





<






<







<







|


<









<






<






<






<






<






<






<






<






<




|


<
|
<






<





|


<







<






<






<






<






<






<






<






<






<



|


<



|


<



|


<






<



|


<



|


<



|


<







<






<








<








<



|
<


<
|
<






<



|
<


<
|
<



|


<




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
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
	objc_registerClassPair((Class)&_OFConstantStringClassReference);
#endif
}

- (void)finishInitialization
{
	@synchronized (self) {
		struct OFUTF8StringIvars *ivars;

		if ([self isMemberOfClass: [OFConstantUTF8String class]])
			return;

		ivars = OFAllocZeroedMemory(1, sizeof(*ivars));



		ivars->cString = _cString;
		ivars->cStringLength = _cStringLength;

		switch (OFUTF8StringCheck(ivars->cString, ivars->cStringLength,

		    &ivars->length)) {
		case 1:
			ivars->isUTF8 = true;
			break;
		case -1:
			OFFreeMemory(ivars);
			@throw [OFInvalidEncodingException exception];
		}

		_cString = (char *)ivars;
		object_setClass(self, [OFConstantUTF8String class]);
	}
}

+ (instancetype)alloc
{





























	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)retain
{
	return self;
}

- (instancetype)autorelease
{
	return self;
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

- (void)release
{
}

- (void)dealloc
{
	OF_DEALLOC_UNSUPPORTED
}

/*
 * In all following methods, the constant string is converted to an
 * OFConstantUTF8String and the message sent again.
 */

/* From protocol OFCopying */
- (id)copy
{
	[self finishInitialization];

	return [self copy];
}

/* From protocol OFMutableCopying */
- (id)mutableCopy
{
	[self finishInitialization];

	return [self mutableCopy];
}

/* From protocol OFComparing,  but overridden in OFString */
- (OFComparisonResult)compare: (OFString *)string
{
	[self finishInitialization];

	return [self compare: string];
}

/* From OFObject, but reimplemented in OFString */
- (bool)isEqual: (id)object
{
	[self finishInitialization];

	return [self isEqual: object];
}

- (unsigned long)hash
{
	[self finishInitialization];

	return self.hash;
}

- (OFString *)description
{
	[self finishInitialization];

	return self.description;
}

/* From OFString */
- (const char *)UTF8String
{
	[self finishInitialization];

	return self.UTF8String;
}

- (size_t)getCString: (char *)cString_
	   maxLength: (size_t)maxLength
	    encoding: (OFStringEncoding)encoding
{
	[self finishInitialization];

	return [self getCString: cString_
		      maxLength: maxLength
		       encoding: encoding];
}

- (const char *)cStringWithEncoding: (OFStringEncoding)encoding
{
	[self finishInitialization];

	return [self cStringWithEncoding: encoding];
}

- (size_t)length
{
	[self finishInitialization];

	return self.length;
}

- (size_t)UTF8StringLength
{
	[self finishInitialization];

	return self.UTF8StringLength;
}

- (size_t)cStringLengthWithEncoding: (OFStringEncoding)encoding
{
	[self finishInitialization];

	return [self cStringLengthWithEncoding: encoding];
}

- (OFComparisonResult)caseInsensitiveCompare: (OFString *)string
{
	[self finishInitialization];

	return [self caseInsensitiveCompare: string];
}

- (OFUnichar)characterAtIndex: (size_t)idx
{
	[self finishInitialization];

	return [self characterAtIndex: idx];
}

- (void)getCharacters: (OFUnichar *)buffer inRange: (OFRange)range

{
	[self finishInitialization];

	[self getCharacters: buffer inRange: range];

}

- (OFRange)rangeOfString: (OFString *)string
{
	[self finishInitialization];

	return [self rangeOfString: string];
}

- (OFRange)rangeOfString: (OFString *)string
		 options: (OFStringSearchOptions)options
{
	[self finishInitialization];

	return [self rangeOfString: string options: options];

}

- (OFRange)rangeOfString: (OFString *)string
		 options: (OFStringSearchOptions)options
		   range: (OFRange)range
{
	[self finishInitialization];

	return [self rangeOfString: string options: options range: range];


}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
{
	[self finishInitialization];

	return [self indexOfCharacterFromSet: characterSet];
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (OFStringSearchOptions)options
{
	[self finishInitialization];

	return [self indexOfCharacterFromSet: characterSet options: options];

}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (OFStringSearchOptions)options
			    range: (OFRange)range
{
	[self finishInitialization];

	return [self indexOfCharacterFromSet: characterSet
				     options: options
				       range: range];
}

- (bool)containsString: (OFString *)string
{
	[self finishInitialization];

	return [self containsString: string];
}

- (OFString *)substringFromIndex: (size_t)idx
{
	[self finishInitialization];

	return [self substringFromIndex: idx];
}

- (OFString *)substringToIndex: (size_t)idx
{
	[self finishInitialization];

	return [self substringToIndex: idx];
}

- (OFString *)substringWithRange: (OFRange)range
{
	[self finishInitialization];

	return [self substringWithRange: range];
}

- (OFString *)stringByAppendingString: (OFString *)string
{
	[self finishInitialization];

	return [self stringByAppendingString: string];
}

- (OFString *)stringByAppendingFormat: (OFConstantString *)format
			    arguments: (va_list)arguments
{
	[self finishInitialization];

	return [self stringByAppendingFormat: format arguments: arguments];

}

- (OFString *)stringByAppendingPathComponent: (OFString *)component
{
	[self finishInitialization];

	return [self stringByAppendingPathComponent: component];
}

- (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];
}

- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
					   options: (int)options
					     range: (OFRange)range
{
	[self finishInitialization];

	return [self stringByReplacingOccurrencesOfString: string
					       withString: replacement
						  options: options
						    range: range];
}

- (OFString *)uppercaseString
{
	[self finishInitialization];

	return self.uppercaseString;
}

- (OFString *)lowercaseString
{
	[self finishInitialization];

	return self.lowercaseString;
}

- (OFString *)capitalizedString
{
	[self finishInitialization];

	return self.capitalizedString;
}

- (OFString *)stringByDeletingLeadingWhitespaces
{
	[self finishInitialization];

	return self.stringByDeletingLeadingWhitespaces;
}

- (OFString *)stringByDeletingTrailingWhitespaces
{
	[self finishInitialization];

	return self.stringByDeletingTrailingWhitespaces;
}

- (OFString *)stringByDeletingEnclosingWhitespaces
{
	[self finishInitialization];

	return self.stringByDeletingEnclosingWhitespaces;
}

- (bool)hasPrefix: (OFString *)prefix
{
	[self finishInitialization];

	return [self hasPrefix: prefix];
}

- (bool)hasSuffix: (OFString *)suffix
{
	[self finishInitialization];

	return [self hasSuffix: suffix];
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
{
	[self finishInitialization];

	return [self componentsSeparatedByString: delimiter];
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
				 options: (OFStringSeparationOptions)options
{
	[self finishInitialization];

	return [self componentsSeparatedByString: delimiter options: options];

}

- (OFArray *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
{
	[self finishInitialization];

	return [self componentsSeparatedByCharactersInSet: characterSet];
}

- (OFArray *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
				 options: (OFStringSeparationOptions)options
{
	[self finishInitialization];

	return [self componentsSeparatedByCharactersInSet: characterSet
						  options: options];
}

- (OFArray *)pathComponents
{
	[self finishInitialization];

	return self.pathComponents;
}

- (OFString *)lastPathComponent
{
	[self finishInitialization];

	return self.lastPathComponent;
}

- (OFString *)stringByDeletingLastPathComponent
{
	[self finishInitialization];

	return self.stringByDeletingLastPathComponent;
}

- (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
{
	[self finishInitialization];

	return self.floatValue;
}

- (double)doubleValue
{
	[self finishInitialization];

	return self.doubleValue;
}

- (const OFUnichar *)characters
{
	[self finishInitialization];

	return self.characters;
}

- (const OFChar16 *)UTF16String
{
	[self finishInitialization];

	return self.UTF16String;
}

- (const OFChar16 *)UTF16StringWithByteOrder: (OFByteOrder)byteOrder
{
	[self finishInitialization];

	return [self UTF16StringWithByteOrder: byteOrder];
}

- (size_t)UTF16StringLength
{
	[self finishInitialization];

	return self.UTF16StringLength;
}

- (const OFChar32 *)UTF32String
{
	[self finishInitialization];

	return self.UTF32String;
}

- (const OFChar32 *)UTF32StringWithByteOrder: (OFByteOrder)byteOrder
{
	[self finishInitialization];

	return [self UTF32StringWithByteOrder: byteOrder];
}

- (OFData *)dataWithEncoding: (OFStringEncoding)encoding
{
	[self finishInitialization];

	return [self dataWithEncoding: encoding];
}

#ifdef OF_HAVE_UNICODE_TABLES
- (OFString *)decomposedStringWithCanonicalMapping
{
	[self finishInitialization];

	return self.decomposedStringWithCanonicalMapping;
}

- (OFString *)decomposedStringWithCompatibilityMapping
{
	[self finishInitialization];

	return self.decomposedStringWithCompatibilityMapping;
}
#endif

#ifdef OF_WINDOWS
- (OFString *)stringByExpandingWindowsEnvironmentStrings
{
	[self finishInitialization];

	return self.stringByExpandingWindowsEnvironmentStrings;
}
#endif

#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path
{
	[self finishInitialization];

	[self writeToFile: path];
}

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

Modified src/OFCountedMapTableSet.h from [32ae18f6b2] to [1da95cb48f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFCountedMapTableSet.m from [72e0c09af3] to [d7ae224517].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if ([set isKindOfClass: [OFCountedSet class]]) {
			OFCountedSet *countedSet = (OFCountedSet *)countedSet;

			for (id object in countedSet) {
				size_t count =
				    [countedSet countForObject: object];

				for (size_t i = 0; i < count; i++)
					[self addObject: object];







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if ([set isKindOfClass: [OFCountedSet class]]) {
			OFCountedSet *countedSet = (OFCountedSet *)set;

			for (id object in countedSet) {
				size_t count =
				    [countedSet countForObject: object];

				for (size_t i = 0; i < count; i++)
					[self addObject: object];
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
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	self = [self init];

	@try {
		for (size_t i = 0; i < count; i++)
			[self addObject: objects[i]];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	self = [self init];

	@try {
		id object;

		[self addObject: firstObject];







|
<














|
<







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
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	self = [self init];

	@try {
		for (size_t i = 0; i < count; i++)
			[self addObject: objects[i]];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	self = [self init];

	@try {
		id object;

		[self addObject: firstObject];
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
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: @"OFCountedSet"] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		for (OFXMLElement *objectElement in
		    [element elementsForName: @"object"
				   namespace: OF_SERIALIZATION_NS]) {
			void *pool2 = objc_autoreleasePoolPush();
			OFXMLElement *object;
			OFXMLAttribute *countAttribute;
			unsigned long long count;

			object = [objectElement elementsForNamespace:
			    OF_SERIALIZATION_NS].firstObject;
			countAttribute =
			    [objectElement attributeForName: @"count"];

			if (object == nil || countAttribute == nil)
				@throw [OFInvalidFormatException exception];

			count = countAttribute.unsignedLongLongValue;







|




|






|







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
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: @"OFCountedSet"] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		for (OFXMLElement *objectElement in
		    [element elementsForName: @"object"
				   namespace: OFSerializationNS]) {
			void *pool2 = objc_autoreleasePoolPush();
			OFXMLElement *object;
			OFXMLAttribute *countAttribute;
			unsigned long long count;

			object = [objectElement elementsForNamespace:
			    OFSerializationNS].firstObject;
			countAttribute =
			    [objectElement attributeForName: @"count"];

			if (object == nil || countAttribute == nil)
				@throw [OFInvalidFormatException exception];

			count = countAttribute.unsignedLongLongValue;
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

- (size_t)countForObject: (id)object
{
	return (size_t)(uintptr_t)[_mapTable objectForKey: object];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsAndCountUsingBlock:
    (of_counted_set_enumeration_block_t)block
{
	@try {
		[_mapTable enumerateKeysAndObjectsUsingBlock:
		    ^ (void *key, void *object, bool *stop) {
			block(key, (size_t)(uintptr_t)object, stop);
		}];
	} @catch (OFEnumerationMutationException *e) {
		@throw [OFEnumerationMutationException
		    exceptionWithObject: self];
	}
}
#endif

- (void)addObject: (id)object
{
	size_t count = (size_t)(uintptr_t)[_mapTable objectForKey: object];

	if (SIZE_MAX - count < 1 || UINTPTR_MAX - count < 1)
		@throw [OFOutOfRangeException exception];

	[_mapTable setObject: (void *)(uintptr_t)(count + 1)
		      forKey: object];
}

- (void)removeObject: (id)object
{
	size_t count = (size_t)(uintptr_t)[_mapTable objectForKey: object];

	if (count == 0)
		return;

	count--;

	if (count > 0)
		[_mapTable setObject: (void *)(uintptr_t)count
			      forKey: object];
	else
		[_mapTable removeObjectForKey: object];
}

- (void)removeAllObjects
{
	[_mapTable removeAllObjects];
}

- (void)makeImmutable
{
}
@end







|
<




















|
<












|
<













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

- (size_t)countForObject: (id)object
{
	return (size_t)(uintptr_t)[_mapTable objectForKey: object];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsAndCountUsingBlock: (OFCountedSetEnumerationBlock)block

{
	@try {
		[_mapTable enumerateKeysAndObjectsUsingBlock:
		    ^ (void *key, void *object, bool *stop) {
			block(key, (size_t)(uintptr_t)object, stop);
		}];
	} @catch (OFEnumerationMutationException *e) {
		@throw [OFEnumerationMutationException
		    exceptionWithObject: self];
	}
}
#endif

- (void)addObject: (id)object
{
	size_t count = (size_t)(uintptr_t)[_mapTable objectForKey: object];

	if (SIZE_MAX - count < 1 || UINTPTR_MAX - count < 1)
		@throw [OFOutOfRangeException exception];

	[_mapTable setObject: (void *)(uintptr_t)(count + 1) forKey: object];

}

- (void)removeObject: (id)object
{
	size_t count = (size_t)(uintptr_t)[_mapTable objectForKey: object];

	if (count == 0)
		return;

	count--;

	if (count > 0)
		[_mapTable setObject: (void *)(uintptr_t)count forKey: object];

	else
		[_mapTable removeObjectForKey: object];
}

- (void)removeAllObjects
{
	[_mapTable removeAllObjects];
}

- (void)makeImmutable
{
}
@end

Modified src/OFCountedSet.h from [9ac0a7e235] to [b22ee9691d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 * @brief A block for enumerating an OFCountedSet.
 *
 * @param object The current object
 * @param count The count of the object
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration
 */
typedef void (^of_counted_set_enumeration_block_t)(id object, size_t count,
    bool *stop);
#endif

/**
 * @class OFCountedSet OFCountedSet.h ObjFW/OFCountedSet.h
 *
 * @brief An abstract class for a mutable unordered set of objects, counting how







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * @brief A block for enumerating an OFCountedSet.
 *
 * @param object The current object
 * @param count The count of the object
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration
 */
typedef void (^OFCountedSetEnumerationBlock)(id object, size_t count,
    bool *stop);
#endif

/**
 * @class OFCountedSet OFCountedSet.h ObjFW/OFCountedSet.h
 *
 * @brief An abstract class for a mutable unordered set of objects, counting how
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each object in the set.
 *
 * @param block The block to execute for each object in the set
 */
- (void)enumerateObjectsAndCountUsingBlock:
    (of_counted_set_enumeration_block_t)block;
#endif
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END







|
<







55
56
57
58
59
60
61
62

63
64
65
66
67
68
69

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each object in the set.
 *
 * @param block The block to execute for each object in the set
 */
- (void)enumerateObjectsAndCountUsingBlock: (OFCountedSetEnumerationBlock)block;

#endif
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END

Modified src/OFCountedSet.m from [e24282a6de] to [985e10aa24].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
	ret = [[OFCountedMapTableSet alloc] initWithObject: firstObject
						 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	return (id)[[OFCountedMapTableSet alloc] initWithObjects: objects
							   count: count];
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	return (id)[[OFCountedMapTableSet alloc] initWithObject: firstObject
						      arguments: arguments];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{







|
<





|
<







55
56
57
58
59
60
61
62

63
64
65
66
67
68

69
70
71
72
73
74
75
	ret = [[OFCountedMapTableSet alloc] initWithObject: firstObject
						 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	return (id)[[OFCountedMapTableSet alloc] initWithObjects: objects
							   count: count];
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	return (id)[[OFCountedMapTableSet alloc] initWithObject: firstObject
						      arguments: arguments];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
		[ret appendFormat: @": %zu", [self countForObject: object]];

		if (++i < count)
			[ret appendString: @",\n"];

		objc_autoreleasePoolPop(pool2);
	}
	[ret replaceOccurrencesOfString: @"\n"
			     withString: @"\n\t"];
	[ret appendString: @"\n)}"];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}








|
<

<







154
155
156
157
158
159
160
161

162

163
164
165
166
167
168
169
		[ret appendFormat: @": %zu", [self countForObject: object]];

		if (++i < count)
			[ret appendString: @",\n"];

		objc_autoreleasePoolPop(pool2);
	}
	[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];

	[ret appendString: @"\n)}"];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

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

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

	element = [OFXMLElement elementWithName: @"OFCountedSet"
				      namespace: OF_SERIALIZATION_NS];

	for (id <OFSerialization> object in self) {
		void *pool2 = objc_autoreleasePoolPush();

		OFXMLElement *objectElement;
		OFString *count;

		count =
		    [OFString stringWithFormat: @"%zu",
						[self countForObject: object]];

		objectElement = [OFXMLElement
		    elementWithName: @"object"
			  namespace: OF_SERIALIZATION_NS];
		[objectElement addAttributeWithName: @"count"
					stringValue: count];
		[objectElement addChild: object.XMLElementBySerializing];
		[element addChild: objectElement];

		objc_autoreleasePoolPop(pool2);
	}

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsAndCountUsingBlock:
    (of_counted_set_enumeration_block_t)block
{
	[self enumerateObjectsUsingBlock: ^ (id object, bool *stop) {
		block(object, [self countForObject: object], stop);
	}];
}
#endif








|













|
















|
<







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

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

	element = [OFXMLElement elementWithName: @"OFCountedSet"
				      namespace: OFSerializationNS];

	for (id <OFSerialization> object in self) {
		void *pool2 = objc_autoreleasePoolPush();

		OFXMLElement *objectElement;
		OFString *count;

		count =
		    [OFString stringWithFormat: @"%zu",
						[self countForObject: object]];

		objectElement = [OFXMLElement
		    elementWithName: @"object"
			  namespace: OFSerializationNS];
		[objectElement addAttributeWithName: @"count"
					stringValue: count];
		[objectElement addChild: object.XMLElementBySerializing];
		[element addChild: objectElement];

		objc_autoreleasePoolPop(pool2);
	}

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsAndCountUsingBlock: (OFCountedSetEnumerationBlock)block

{
	[self enumerateObjectsUsingBlock: ^ (id object, bool *stop) {
		block(object, [self countForObject: object], stop);
	}];
}
#endif

Renamed and modified src/OFCryptoHash.h [ec7cd6aa74] to src/OFCryptographicHash.h [21f208b184].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

OF_ASSUME_NONNULL_BEGIN

/**
 * @protocol OFCryptoHash OFCryptoHash.h ObjFW/OFCryptoHash.h

 *
 * @brief A protocol for classes providing cryptographic hash functions.
 *
 * A cryptographic hash implementing this protocol can be copied. The entire
 * state is copied, allowing to calculate a new hash from there. This is
 * especially useful for generating many hashes with a common prefix.
 */
@protocol OFCryptoHash <OFObject, OFCopying>
#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) size_t digestSize;
@property (class, readonly, nonatomic) size_t blockSize;
#endif

/**
 * @brief The digest size of the cryptographic hash, in bytes.

<
<
|


















|
>







|







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


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

OF_ASSUME_NONNULL_BEGIN

/**
 * @protocol OFCryptographicHash \
 *	     OFCryptographicHash.h ObjFW/OFCryptographicHash.h
 *
 * @brief A protocol for classes providing cryptographic hash functions.
 *
 * A cryptographic hash implementing this protocol can be copied. The entire
 * state is copied, allowing to calculate a new hash from there. This is
 * especially useful for generating many hashes with a common prefix.
 */
@protocol OFCryptographicHash <OFObject, OFCopying>
#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) size_t digestSize;
@property (class, readonly, nonatomic) size_t blockSize;
#endif

/**
 * @brief The digest size of the cryptographic hash, in bytes.
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Creates a new cryptographic hash.
 *
 * @return A new autoreleased cryptographic hash
 */
+ (instancetype)cryptoHashWithAllowsSwappableMemory:
    (bool)allowsSwappableMemory;

/**
 * @brief Returns the digest size of the cryptographic hash, in bytes.
 *
 * @return The digest size of the cryptographic hash, in bytes
 */
+ (size_t)digestSize;







<
|







63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Creates a new cryptographic hash.
 *
 * @return A new autoreleased cryptographic hash
 */

+ (instancetype)hashWithAllowsSwappableMemory: (bool)allowsSwappableMemory;

/**
 * @brief Returns the digest size of the cryptographic hash, in bytes.
 *
 * @return The digest size of the cryptographic hash, in bytes
 */
+ (size_t)digestSize;
96
97
98
99
100
101
102
103
104




105
106
107
108
109
110
111
112
113
114
115
116

/**
 * @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 Resets all state so that a new hash can be calculated.
 *
 * @warning This invalidates any pointer previously returned by @ref digest. If
 *	    you are still interested in the previous digest, you need to memcpy
 *	    it yourself before calling @ref reset!
 */
- (void)reset;
@end

OF_ASSUME_NONNULL_END







|
|
>
>
>
>












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

/**
 * @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
 *	    you are still interested in the previous digest, you need to memcpy
 *	    it yourself before calling @ref reset!
 */
- (void)reset;
@end

OF_ASSUME_NONNULL_END

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

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 * @class OFDNSQuery OFDNSQuery.h ObjFW/OFDNSQuery.h
 *
 * @brief A class representing a DNS query.
 */
@interface OFDNSQuery: OFObject <OFCopying>
{
	OFString *_domainName;
	of_dns_class_t _DNSClass;
	of_dns_record_type_t _recordType;
	OF_RESERVE_IVARS(OFDNSQuery, 4)
}

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

/**
 * @brief The DNS class of the query.
 */
@property (readonly, nonatomic) of_dns_class_t DNSClass;

/**
 * @brief The record type of the query.
 */
@property (readonly, nonatomic) of_dns_record_type_t recordType;

/**
 * @brief Creates a new, autoreleased OFDNSQuery.
 *
 * @param domainName The domain name to query
 * @param DNSClass The DNS class of the query
 * @param recordType The record type of the query
 * @return A new, autoreleased OFDNSQuery
 */
+ (instancetype)queryWithDomainName: (OFString *)domainName
			   DNSClass: (of_dns_class_t)DNSClass
			 recordType: (of_dns_record_type_t)recordType;

/**
 * @brief Initializes an already allocated OFDNSQuery.
 *
 * @param domainName The domain name to query
 * @param DNSClass The DNS class of the query
 * @param recordType The record type of the query
 * @return An initialized OFDNSQuery
 */
- (instancetype)initWithDomainName: (OFString *)domainName
			  DNSClass: (of_dns_class_t)DNSClass
			recordType: (of_dns_record_type_t)recordType
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END







|
|











|




|










|
|










|
|






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

/**
 * @brief The DNS class of the query.
 */
@property (readonly, nonatomic) OFDNSClass DNSClass;

/**
 * @brief The record type of the query.
 */
@property (readonly, nonatomic) OFDNSRecordType recordType;

/**
 * @brief Creates a new, autoreleased OFDNSQuery.
 *
 * @param domainName The domain name to query
 * @param DNSClass The DNS class of the query
 * @param recordType The record type of the query
 * @return A new, autoreleased OFDNSQuery
 */
+ (instancetype)queryWithDomainName: (OFString *)domainName
			   DNSClass: (OFDNSClass)DNSClass
			 recordType: (OFDNSRecordType)recordType;

/**
 * @brief Initializes an already allocated OFDNSQuery.
 *
 * @param domainName The domain name to query
 * @param DNSClass The DNS class of the query
 * @param recordType The record type of the query
 * @return An initialized OFDNSQuery
 */
- (instancetype)initWithDomainName: (OFString *)domainName
			  DNSClass: (OFDNSClass)DNSClass
			recordType: (OFDNSRecordType)recordType
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

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

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFString.h"

@implementation OFDNSQuery
@synthesize domainName = _domainName, DNSClass = _DNSClass;
@synthesize recordType = _recordType;

+ (instancetype)queryWithDomainName: (OFString *)domainName
			   DNSClass: (of_dns_class_t)DNSClass
			 recordType: (of_dns_record_type_t)recordType
{
	return [[[self alloc] initWithDomainName: domainName
					DNSClass: DNSClass
				      recordType: recordType] autorelease];
}

- (instancetype)initWithDomainName: (OFString *)domainName
			  DNSClass: (of_dns_class_t)DNSClass
			recordType: (of_dns_record_type_t)recordType
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![domainName hasSuffix: @"."])







|
|







|
|







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
#import "OFString.h"

@implementation OFDNSQuery
@synthesize domainName = _domainName, DNSClass = _DNSClass;
@synthesize recordType = _recordType;

+ (instancetype)queryWithDomainName: (OFString *)domainName
			   DNSClass: (OFDNSClass)DNSClass
			 recordType: (OFDNSRecordType)recordType
{
	return [[[self alloc] initWithDomainName: domainName
					DNSClass: DNSClass
				      recordType: recordType] autorelease];
}

- (instancetype)initWithDomainName: (OFString *)domainName
			  DNSClass: (OFDNSClass)DNSClass
			recordType: (OFDNSRecordType)recordType
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![domainName hasSuffix: @"."])
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);
	OF_HASH_ADD_HASH(hash, _domainName.hash);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
	return [self retain];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@ %@ %@ %@>",
	    self.className, _domainName, of_dns_class_to_string(_DNSClass),
	    of_dns_record_type_to_string(_recordType)];
}
@end







|

|
|
|
|
|












|
|


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
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

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

	return hash;
}

- (id)copy
{
	return [self retain];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@ %@ %@ %@>",
	    self.className, _domainName, OFDNSClassName(_DNSClass),
	    OFDNSRecordTypeName(_recordType)];
}
@end

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

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFDNSResourceRecord.h"
#import "OFDNSResponse.h"
#import "OFRunLoop.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

#define OF_DNS_RESOLVER_BUFFER_LENGTH 512

@class OFArray OF_GENERIC(ObjectType);
@class OFDNSResolver;
@class OFDNSResolverContext;
@class OFDNSResolverSettings;
@class OFDate;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFNumber;
@class OFTCPSocket;
@class OFUDPSocket;

/**
 * @enum of_dns_resolver_error_t OFDNSResolver.h ObjFW/OFDNSResolver.h
 *
 * @brief An enum describing why resolving a host failed.
 */
typedef enum of_dns_resolver_error_t {
	/** An unknown error */
	OF_DNS_RESOLVER_ERROR_UNKNOWN,
	/** The query timed out */
	OF_DNS_RESOLVER_ERROR_TIMEOUT,
	/** The query was canceled */
	OF_DNS_RESOLVER_ERROR_CANCELED,
	/**
	 * No result for the specified host with the specified type and class.
	 *
	 * This is only used in situations where this is an error, e.g. when
	 * trying to connect to a host.
	 */
	OF_DNS_RESOLVER_ERROR_NO_RESULT,
	/** The server considered the query to be malformed */
	OF_DNS_RESOLVER_ERROR_SERVER_INVALID_FORMAT,
	/** The server was unable to process due to an internal error */
	OF_DNS_RESOLVER_ERROR_SERVER_FAILURE,
	/** The server returned an error that the domain does not exist */
	OF_DNS_RESOLVER_ERROR_SERVER_NAME_ERROR,
	/** The server does not have support for the requested query */
	OF_DNS_RESOLVER_ERROR_SERVER_NOT_IMPLEMENTED,
	/** The server refused the query */
	OF_DNS_RESOLVER_ERROR_SERVER_REFUSED,
	/** There was no name server to query */
	OF_DNS_RESOLVER_ERROR_NO_NAME_SERVER
} of_dns_resolver_error_t;

/**
 * @protocol OFDNSResolverQueryDelegate OFDNSResolver.h ObjFW/OFDNSResolver.h
 *
 * @brief A delegate for performed DNS queries.
 */
@protocol OFDNSResolverQueryDelegate <OFObject>







|













|



|

|

|

|






|

|

|

|

|

|

|
|







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
#import "OFDNSResourceRecord.h"
#import "OFDNSResponse.h"
#import "OFRunLoop.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

#define OFDNSResolverBufferLength 512

@class OFArray OF_GENERIC(ObjectType);
@class OFDNSResolver;
@class OFDNSResolverContext;
@class OFDNSResolverSettings;
@class OFDate;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFNumber;
@class OFTCPSocket;
@class OFUDPSocket;

/**
 * @enum OFDNSResolverErrorCode OFDNSResolver.h ObjFW/OFDNSResolver.h
 *
 * @brief An enum describing why resolving a host failed.
 */
typedef enum {
	/** An unknown error */
	OFDNSResolverErrorCodeUnknown,
	/** The query timed out */
	OFDNSResolverErrorCodeTimeout,
	/** The query was canceled */
	OFDNSResolverErrorCodeCanceled,
	/**
	 * No result for the specified host with the specified type and class.
	 *
	 * This is only used in situations where this is an error, e.g. when
	 * trying to connect to a host.
	 */
	OFDNSResolverErrorCodeNoResult,
	/** The server considered the query to be malformed */
	OFDNSResolverErrorCodeServerInvalidFormat,
	/** The server was unable to process due to an internal error */
	OFDNSResolverErrorCodeServerFailure,
	/** The server returned an error that the domain does not exist */
	OFDNSResolverErrorCodeServerNameError,
	/** The server does not have support for the requested query */
	OFDNSResolverErrorCodeServerNotImplemented,
	/** The server refused the query */
	OFDNSResolverErrorCodeServerRefused,
	/** There was no name server to query */
	OFDNSResolverErrorCodeNoNameServer
} OFDNSResolverErrorCode;

/**
 * @protocol OFDNSResolverQueryDelegate OFDNSResolver.h ObjFW/OFDNSResolver.h
 *
 * @brief A delegate for performed DNS queries.
 */
@protocol OFDNSResolverQueryDelegate <OFObject>
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@protocol OFDNSResolverHostDelegate <OFObject>
/**
 * @brief This method is called when a DNS resolver resolved a host to
 *	  addresses.
 *
 * @param resolver The acting resolver
 * @param host The host the resolver resolved
 * @param addresses OFData containing several of_socket_address_t
 * @param exception The exception that occurred during resolving, or nil on
 *		    success
 */
- (void)resolver: (OFDNSResolver *)resolver
  didResolveHost: (OFString *)host
       addresses: (nullable OFData *)addresses
       exception: (nullable id)exception;







|







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
@protocol OFDNSResolverHostDelegate <OFObject>
/**
 * @brief This method is called when a DNS resolver resolved a host to
 *	  addresses.
 *
 * @param resolver The acting resolver
 * @param host The host the resolver resolved
 * @param addresses OFData containing several OFSocketAddress
 * @param exception The exception that occurred during resolving, or nil on
 *		    success
 */
- (void)resolver: (OFDNSResolver *)resolver
  didResolveHost: (OFString *)host
       addresses: (nullable OFData *)addresses
       exception: (nullable id)exception;
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
@interface OFDNSResolver: OFObject
{
	OFDNSResolverSettings *_settings;
	OFUDPSocket *_IPv4Socket;
#ifdef OF_HAVE_IPV6
	OFUDPSocket *_IPv6Socket;
#endif
	char _buffer[OF_DNS_RESOLVER_BUFFER_LENGTH];
	OFMutableDictionary OF_GENERIC(OFNumber *, OFDNSResolverContext *)
	    *_queries;
	OFMutableDictionary OF_GENERIC(OFTCPSocket *, OFDNSResolverContext *)
	    *_TCPQueries;
}

/**







|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
@interface OFDNSResolver: OFObject
{
	OFDNSResolverSettings *_settings;
	OFUDPSocket *_IPv4Socket;
#ifdef OF_HAVE_IPV6
	OFUDPSocket *_IPv6Socket;
#endif
	char _buffer[OFDNSResolverBufferLength];
	OFMutableDictionary OF_GENERIC(OFNumber *, OFDNSResolverContext *)
	    *_queries;
	OFMutableDictionary OF_GENERIC(OFTCPSocket *, OFDNSResolverContext *)
	    *_TCPQueries;
}

/**
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
 */
@property (copy, nonatomic) OFArray OF_GENERIC(OFString *) *searchDomains;

/**
 * @brief The timeout, in seconds, after which the next name server should be
 *	  tried.
 */
@property (nonatomic) of_time_interval_t timeout;

/**
 * @brief The number of attempts before giving up to resolve a host.
 *
 * Trying all name servers once is considered a single attempt.
 */
@property (nonatomic) unsigned int maxAttempts;







|







161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
 */
@property (copy, nonatomic) OFArray OF_GENERIC(OFString *) *searchDomains;

/**
 * @brief The timeout, in seconds, after which the next name server should be
 *	  tried.
 */
@property (nonatomic) OFTimeInterval timeout;

/**
 * @brief The number of attempts before giving up to resolve a host.
 *
 * Trying all name servers once is considered a single attempt.
 */
@property (nonatomic) unsigned int maxAttempts;
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
@property (nonatomic) bool usesTCP;

/**
 * @brief The interval in seconds in which the config should be reloaded.
 *
 * Setting this to 0 disables config reloading.
 */
@property (nonatomic) of_time_interval_t configReloadInterval;

/**
 * @brief Creates a new, autoreleased OFDNSResolver.
 */
+ (instancetype)resolver;

/**







|







185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
@property (nonatomic) bool usesTCP;

/**
 * @brief The interval in seconds in which the config should be reloaded.
 *
 * Setting this to 0 disables config reloading.
 */
@property (nonatomic) OFTimeInterval configReloadInterval;

/**
 * @brief Creates a new, autoreleased OFDNSResolver.
 */
+ (instancetype)resolver;

/**
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
 * @brief Asynchronously performs the specified query.
 *
 * @param query The query to perform
 * @param runLoopMode The run loop mode in which to resolve
 * @param delegate The delegate to use for callbacks
 */
- (void)asyncPerformQuery: (OFDNSQuery *)query
	      runLoopMode: (of_run_loop_mode_t)runLoopMode
		 delegate: (id <OFDNSResolverQueryDelegate>)delegate;

/**
 * @brief Asynchronously resolves the specified host to socket addresses.
 *
 * @param host The host to resolve
 * @param delegate The delegate to use for callbacks
 */
- (void)asyncResolveAddressesForHost: (OFString *)host
			    delegate: (id <OFDNSResolverHostDelegate>)delegate;

/**
 * @brief Asynchronously resolves the specified host to socket addresses.
 *
 * @param host The host to resolve
 * @param addressFamily The desired socket address family
 * @param delegate The delegate to use for callbacks
 */
- (void)asyncResolveAddressesForHost: (OFString *)host
		       addressFamily: (of_socket_address_family_t)addressFamily
			    delegate: (id <OFDNSResolverHostDelegate>)delegate;

/**
 * @brief Asynchronously resolves the specified host to socket addresses.
 *
 * @param host The host to resolve
 * @param addressFamily The desired socket address family
 * @param runLoopMode The run loop mode in which to resolve
 * @param delegate The delegate to use for callbacks
 */
- (void)asyncResolveAddressesForHost: (OFString *)host
		       addressFamily: (of_socket_address_family_t)addressFamily
			 runLoopMode: (of_run_loop_mode_t)runLoopMode
			    delegate: (id <OFDNSResolverHostDelegate>)delegate;

/**
 * @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 of_socket_address_t
 */
- (OFData *)resolveAddressesForHost: (OFString *)host
		      addressFamily: (of_socket_address_family_t)addressFamily;

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

OF_ASSUME_NONNULL_END







|



















|











|
|







|


|








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
 * @brief Asynchronously performs the specified query.
 *
 * @param query The query to perform
 * @param runLoopMode The run loop mode in which to resolve
 * @param delegate The delegate to use for callbacks
 */
- (void)asyncPerformQuery: (OFDNSQuery *)query
	      runLoopMode: (OFRunLoopMode)runLoopMode
		 delegate: (id <OFDNSResolverQueryDelegate>)delegate;

/**
 * @brief Asynchronously resolves the specified host to socket addresses.
 *
 * @param host The host to resolve
 * @param delegate The delegate to use for callbacks
 */
- (void)asyncResolveAddressesForHost: (OFString *)host
			    delegate: (id <OFDNSResolverHostDelegate>)delegate;

/**
 * @brief Asynchronously resolves the specified host to socket addresses.
 *
 * @param host The host to resolve
 * @param addressFamily The desired socket address family
 * @param delegate The delegate to use for callbacks
 */
- (void)asyncResolveAddressesForHost: (OFString *)host
		       addressFamily: (OFSocketAddressFamily)addressFamily
			    delegate: (id <OFDNSResolverHostDelegate>)delegate;

/**
 * @brief Asynchronously resolves the specified host to socket addresses.
 *
 * @param host The host to resolve
 * @param addressFamily The desired socket address family
 * @param runLoopMode The run loop mode in which to resolve
 * @param delegate The delegate to use for callbacks
 */
- (void)asyncResolveAddressesForHost: (OFString *)host
		       addressFamily: (OFSocketAddressFamily)addressFamily
			 runLoopMode: (OFRunLoopMode)runLoopMode
			    delegate: (id <OFDNSResolverHostDelegate>)delegate;

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

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

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFUDPSocket+Private.h"

#import "OFDNSQueryFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"

#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"

#ifndef SOCK_DNS
# define SOCK_DNS 0
#endif

#define BUFFER_LENGTH OF_DNS_RESOLVER_BUFFER_LENGTH
#define MAX_DNS_RESPONSE_LENGTH 65536

/*
 * RFC 1035 doesn't specify if pointers to pointers are allowed, and if so how
 * many. Since it's unspecified, we have to assume that it might happen, but we
 * also want to limit it to avoid DoS. Limiting it to 16 levels of pointers and
 * immediately rejecting pointers to itself seems like a fair balance.
 */
#define MAX_ALLOWED_POINTERS 16

#define CNAME_RECURSION 3

@interface OFDNSResolver () <OFUDPSocketDelegate, OFTCPSocketDelegate>
- (void)of_contextTimedOut: (OFDNSResolverContext *)context;
@end

OF_DIRECT_MEMBERS
@interface OFDNSResolverContext: OFObject
{
@public
	OFDNSQuery *_query;
	OFNumber *_ID;
	OFDNSResolverSettings *_settings;
	size_t _nameServersIndex;
	unsigned int _attempt;
	id <OFDNSResolverQueryDelegate> _delegate;
	OFData *_queryData;
	of_socket_address_t _usedNameServer;
	OFTCPSocket *_TCPSocket;
	OFMutableData *_TCPQueryData;
	void *_TCPBuffer;
	size_t _responseLength;
	OFTimer *_cancelTimer;
}








>







|
|







<
|
<
















|







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

static const size_t bufferLength = OFDNSResolverBufferLength;
static const size_t maxDNSResponseLength = 65536;

/*
 * RFC 1035 doesn't specify if pointers to pointers are allowed, and if so how
 * many. Since it's unspecified, we have to assume that it might happen, but we
 * also want to limit it to avoid DoS. Limiting it to 16 levels of pointers and
 * immediately rejecting pointers to itself seems like a fair balance.
 */

static const uint_fast8_t maxAllowedPointers = 16;


@interface OFDNSResolver () <OFUDPSocketDelegate, OFTCPSocketDelegate>
- (void)of_contextTimedOut: (OFDNSResolverContext *)context;
@end

OF_DIRECT_MEMBERS
@interface OFDNSResolverContext: OFObject
{
@public
	OFDNSQuery *_query;
	OFNumber *_ID;
	OFDNSResolverSettings *_settings;
	size_t _nameServersIndex;
	unsigned int _attempt;
	id <OFDNSResolverQueryDelegate> _delegate;
	OFData *_queryData;
	OFSocketAddress _usedNameServer;
	OFTCPSocket *_TCPSocket;
	OFMutableData *_TCPQueryData;
	void *_TCPBuffer;
	size_t _responseLength;
	OFTimer *_cancelTimer;
}

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
		[components addObject: component];
	} while (componentLength > 0);

	return [components componentsJoinedByString: @"."];
}

static OF_KINDOF(OFDNSResourceRecord *)
parseResourceRecord(OFString *name, of_dns_class_t DNSClass,
    of_dns_record_type_t recordType, uint32_t TTL, const unsigned char *buffer,
    size_t length, size_t i, uint16_t dataLength)
{
	if (recordType == OF_DNS_RECORD_TYPE_A && DNSClass == OF_DNS_CLASS_IN) {
		of_socket_address_t address;

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

		memset(&address, 0, sizeof(address));
		address.family = OF_SOCKET_ADDRESS_FAMILY_IPV4;
		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 == OF_DNS_RECORD_TYPE_NS) {
		size_t j = i;
		OFString *authoritativeHost = parseName(buffer, length, &j,
		    MAX_ALLOWED_POINTERS);

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

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

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

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

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

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

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







|
|


|
|





|









|


|









|


|









|


|








|







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
		[components addObject: component];
	} while (componentLength > 0);

	return [components componentsJoinedByString: @"."];
}

static OF_KINDOF(OFDNSResourceRecord *)
parseResourceRecord(OFString *name, OFDNSClass DNSClass,
    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) |
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
		     responsiblePerson: responsiblePerson
			  serialNumber: serialNumber
		       refreshInterval: refreshInterval
			 retryInterval: retryInterval
		    expirationInterval: expirationInterval
				minTTL: minTTL
				   TTL: TTL] autorelease];
	} else if (recordType == OF_DNS_RECORD_TYPE_PTR) {
		size_t j = i;
		OFString *domainName = parseName(buffer, length, &j,
		    MAX_ALLOWED_POINTERS);

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

		return [[[OFPTRDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
		      domainName: domainName
			     TTL: TTL] autorelease];
	} else if (recordType == OF_DNS_RECORD_TYPE_HINFO) {
		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 == OF_DNS_RECORD_TYPE_MX) {
		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,
		    MAX_ALLOWED_POINTERS);

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

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

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

			if (stringLength > dataLength)







|


|









|


















|











|










|







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
		     responsiblePerson: responsiblePerson
			  serialNumber: serialNumber
		       refreshInterval: refreshInterval
			 retryInterval: retryInterval
		    expirationInterval: expirationInterval
				minTTL: minTTL
				   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)
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
		[textStrings makeImmutable];

		return [[[OFTXTDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
		     textStrings: textStrings
			     TTL: TTL] autorelease];
	} else if (recordType == OF_DNS_RECORD_TYPE_RP) {
		size_t j = i;
		OFString *mailbox = parseName(buffer, length, &j,
		    MAX_ALLOWED_POINTERS);
		OFString *TXTDomainName;

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

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

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

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

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

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

#ifdef AF_INET6
		address.sockaddr.in6.sin6_family = AF_INET6;
#else
		address.sockaddr.in6.sin6_family = AF_UNSPEC;
#endif
		memcpy(address.sockaddr.in6.sin6_addr.s6_addr, buffer + i, 16);

		return [[[OFAAAADNSResourceRecord alloc]
		    initWithName: name
			 address: &address
			     TTL: TTL] autorelease];
	} else if (recordType == OF_DNS_RECORD_TYPE_SRV &&
	    DNSClass == OF_DNS_CLASS_IN) {
		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, MAX_ALLOWED_POINTERS);

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

		return [[[OFSRVDNSResourceRecord alloc]
		    initWithName: name
			priority: priority







|


|






|










|
|
|





|













|
|












|







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
		[textStrings makeImmutable];

		return [[[OFTXTDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
		     textStrings: textStrings
			     TTL: TTL] autorelease];
	} 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;
#else
		address.sockaddr.in6.sin6_family = AF_UNSPEC;
#endif
		memcpy(address.sockaddr.in6.sin6_addr.s6_addr, buffer + i, 16);

		return [[[OFAAAADNSResourceRecord alloc]
		    initWithName: name
			 address: &address
			     TTL: TTL] autorelease];
	} 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
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
{
	OFMutableDictionary *ret = [OFMutableDictionary dictionary];
	OFEnumerator OF_GENERIC(OFMutableArray *) *objectEnumerator;
	OFMutableArray *array;

	for (uint_fast16_t j = 0; j < count; j++) {
		OFString *name = parseName(buffer, length, i,
		    MAX_ALLOWED_POINTERS);
		of_dns_class_t DNSClass;
		of_dns_record_type_t recordType;
		uint32_t TTL;
		uint16_t dataLength;
		OFDNSResourceRecord *record;

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








|
|
|







419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
{
	OFMutableDictionary *ret = [OFMutableDictionary dictionary];
	OFEnumerator OF_GENERIC(OFMutableArray *) *objectEnumerator;
	OFMutableArray *array;

	for (uint_fast16_t j = 0; j < count; j++) {
		OFString *name = parseName(buffer, length, i,
		    maxAllowedPointers);
		OFDNSClass DNSClass;
		OFDNSRecordType recordType;
		uint32_t TTL;
		uint16_t dataLength;
		OFDNSResourceRecord *record;

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

451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
		    buffer, length, *i, dataLength);
		*i += dataLength;

		array = [ret objectForKey: name];

		if (array == nil) {
			array = [OFMutableArray array];
			[ret setObject: array
				forKey: name];
		}

		[array addObject: record];
	}

	objectEnumerator = [ret objectEnumerator];
	while ((array = [objectEnumerator nextObject]) != nil)







|
<







448
449
450
451
452
453
454
455

456
457
458
459
460
461
462
		    buffer, length, *i, dataLength);
		*i += dataLength;

		array = [ret objectForKey: name];

		if (array == nil) {
			array = [OFMutableArray array];
			[ret setObject: array forKey: name];

		}

		[array addObject: record];
	}

	objectEnumerator = [ret objectEnumerator];
	while ((array = [objectEnumerator nextObject]) != nil)
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
		_settings = [settings copy];
		_delegate = [delegate retain];

		queryData = [OFMutableData dataWithCapacity: 512];

		/* Header */

		tmp = OF_BSWAP16_IF_LE(_ID.unsignedShortValue);
		[queryData addItems: &tmp
			      count: 2];

		/* RD */
		tmp = OF_BSWAP16_IF_LE(1u << 8);
		[queryData addItems: &tmp
			      count: 2];

		/* QDCOUNT */
		tmp = OF_BSWAP16_IF_LE(1);
		[queryData addItems: &tmp
			      count: 2];

		/* ANCOUNT, NSCOUNT and ARCOUNT */
		[queryData increaseCountBy: 6];

		/* Question */

		/* QNAME */
		for (OFString *component in







|
|
<
<

|
|
<
<

|
|
<
<







485
486
487
488
489
490
491
492
493


494
495
496


497
498
499


500
501
502
503
504
505
506
		_settings = [settings copy];
		_delegate = [delegate retain];

		queryData = [OFMutableData dataWithCapacity: 512];

		/* Header */

		tmp = OFToBigEndian16(_ID.unsignedShortValue);
		[queryData addItems: &tmp count: 2];


		/* RD */
		tmp = OFToBigEndian16(1u << 8);
		[queryData addItems: &tmp count: 2];


		/* QDCOUNT */
		tmp = OFToBigEndian16(1);
		[queryData addItems: &tmp count: 2];


		/* ANCOUNT, NSCOUNT and ARCOUNT */
		[queryData increaseCountBy: 6];

		/* Question */

		/* QNAME */
		for (OFString *component in
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
			length8 = (uint8_t)length;
			[queryData addItem: &length8];
			[queryData addItems: component.UTF8String
				      count: length];
		}

		/* QTYPE */
		tmp = OF_BSWAP16_IF_LE(_query.recordType);
		[queryData addItems: &tmp
			      count: 2];

		/* QCLASS */
		tmp = OF_BSWAP16_IF_LE(_query.DNSClass);
		[queryData addItems: &tmp
			      count: 2];

		[queryData makeImmutable];

		_queryData = [queryData copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];







|
|
<
<

|
|
<
<







514
515
516
517
518
519
520
521
522


523
524
525


526
527
528
529
530
531
532
			length8 = (uint8_t)length;
			[queryData addItem: &length8];
			[queryData addItems: component.UTF8String
				      count: length];
		}

		/* QTYPE */
		tmp = OFToBigEndian16(_query.recordType);
		[queryData addItems: &tmp count: 2];


		/* QCLASS */
		tmp = OFToBigEndian16(_query.DNSClass);
		[queryData addItems: &tmp count: 2];


		[queryData makeImmutable];

		_queryData = [queryData copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
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
	[_query release];
	[_ID release];
	[_settings release];
	[_delegate release];
	[_queryData release];
	[_TCPSocket release];
	[_TCPQueryData release];

	[_cancelTimer release];

	[super dealloc];
}
@end

@implementation OFDNSResolver
#ifdef OF_AMIGAOS
+ (void)initialize
{
	if (self != [OFDNSResolver class])
		return;

	if (!of_socket_init())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}
#endif

+ (instancetype)resolver
{







>













|







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
	[_query release];
	[_ID release];
	[_settings release];
	[_delegate release];
	[_queryData release];
	[_TCPSocket release];
	[_TCPQueryData release];
	OFFreeMemory(_TCPBuffer);
	[_cancelTimer release];

	[super dealloc];
}
@end

@implementation OFDNSResolver
#ifdef OF_AMIGAOS
+ (void)initialize
{
	if (self != [OFDNSResolver class])
		return;

	if (!OFSocketInit())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}
#endif

+ (instancetype)resolver
{
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
- (void)setSearchDomains: (OFArray *)searchDomains
{
	OFArray *old = _settings->_searchDomains;
	_settings->_searchDomains = [searchDomains copy];
	[old release];
}

- (of_time_interval_t)timeout
{
	return _settings->_timeout;
}

- (void)setTimeout: (of_time_interval_t)timeout
{
	_settings->_timeout = timeout;
}

- (unsigned int)maxAttempts
{
	return _settings->_maxAttempts;







|




|







642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
- (void)setSearchDomains: (OFArray *)searchDomains
{
	OFArray *old = _settings->_searchDomains;
	_settings->_searchDomains = [searchDomains copy];
	[old release];
}

- (OFTimeInterval)timeout
{
	return _settings->_timeout;
}

- (void)setTimeout: (OFTimeInterval)timeout
{
	_settings->_timeout = timeout;
}

- (unsigned int)maxAttempts
{
	return _settings->_maxAttempts;
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
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
}

- (void)setUsesTCP: (bool)usesTCP
{
	_settings->_usesTCP = usesTCP;
}

- (of_time_interval_t)configReloadInterval
{
	return _settings->_configReloadInterval;
}

- (void)setConfigReloadInterval: (of_time_interval_t)configReloadInterval
{
	_settings->_configReloadInterval = configReloadInterval;
}

- (void)of_sendQueryForContext: (OFDNSResolverContext *)context
		   runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	OFUDPSocket *sock;
	OFString *nameServer;

	[_queries setObject: context
		     forKey: context->_ID];

	[context->_cancelTimer invalidate];
	[context->_cancelTimer release];
	context->_cancelTimer = nil;
	context->_cancelTimer = [[OFTimer alloc]
	    initWithFireDate: [OFDate dateWithTimeIntervalSinceNow:
				  context->_settings->_timeout]
		    interval: context->_settings->_timeout
		      target: self
		    selector: @selector(of_contextTimedOut:)
		      object: context
		     repeats: false];
	[[OFRunLoop currentRunLoop] addTimer: context->_cancelTimer
				     forMode: runLoopMode];

	nameServer = [context->_settings->_nameServers
	    objectAtIndex: context->_nameServersIndex];

	if (context->_settings->_usesTCP) {
		OF_ENSURE(context->_TCPSocket == nil);

		context->_TCPSocket = [[OFTCPSocket alloc] init];
		[_TCPQueries setObject: context
				forKey: context->_TCPSocket];

		context->_TCPSocket.delegate = self;
		[context->_TCPSocket asyncConnectToHost: nameServer
						   port: 53
					    runLoopMode: runLoopMode];
		return;
	}

	context->_usedNameServer = of_socket_address_parse_ip(nameServer, 53);

	switch (context->_usedNameServer.family) {
#ifdef OF_HAVE_IPV6
	case OF_SOCKET_ADDRESS_FAMILY_IPV6:
		if (_IPv6Socket == nil) {
			of_socket_address_t address =
			    of_socket_address_parse_ip(@"::", 0);

			_IPv6Socket = [[OFUDPSocket alloc] init];
			[_IPv6Socket of_bindToAddress: &address
					    extraType: SOCK_DNS];

			_IPv6Socket.canBlock = false;



			_IPv6Socket.delegate = self;
		}

		sock = _IPv6Socket;
		break;
#endif
	case OF_SOCKET_ADDRESS_FAMILY_IPV4:
		if (_IPv4Socket == nil) {
			of_socket_address_t address =
			    of_socket_address_parse_ip(@"0.0.0.0", 0);

			_IPv4Socket = [[OFUDPSocket alloc] init];
			[_IPv4Socket of_bindToAddress: &address
					    extraType: SOCK_DNS];

			_IPv4Socket.canBlock = false;



			_IPv4Socket.delegate = self;
		}

		sock = _IPv4Socket;
		break;
	default:
		@throw [OFInvalidArgumentException exception];
	}

	[sock asyncSendData: context->_queryData
		   receiver: &context->_usedNameServer
		runLoopMode: runLoopMode];
	[sock asyncReceiveIntoBuffer: _buffer
			      length: BUFFER_LENGTH
			 runLoopMode: runLoopMode];
}

- (void)asyncPerformQuery: (OFDNSQuery *)query
		 delegate: (id <OFDNSResolverQueryDelegate>)delegate
{
	[self asyncPerformQuery: query
		    runLoopMode: of_run_loop_mode_default
		       delegate: delegate];
}

- (void)asyncPerformQuery: (OFDNSQuery *)query
	      runLoopMode: (of_run_loop_mode_t)runLoopMode
		 delegate: (id <OFDNSResolverQueryDelegate>)delegate
{
	void *pool = objc_autoreleasePoolPush();
	OFNumber *ID;
	OFDNSResolverContext *context;

	/* Random, unused ID */
	do {
		ID = [OFNumber numberWithUnsignedShort: of_random16()];
	} while ([_queries objectForKey: ID] != nil);

	if (query.domainName.UTF8StringLength > 253)
		@throw [OFOutOfRangeException exception];

	if (_settings->_nameServers.count == 0) {
		id exception = [OFDNSQueryFailedException
		    exceptionWithQuery: query
				 error: OF_DNS_RESOLVER_ERROR_NO_NAME_SERVER];
		[delegate  resolver: self
		    didPerformQuery: query
			   response: nil
			  exception: exception];
		return;
	}

	context = [[[OFDNSResolverContext alloc]
	    initWithQuery: query
		       ID: ID
		 settings: _settings
		 delegate: delegate] autorelease];
	[self of_sendQueryForContext: context
			 runLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}

- (void)of_contextTimedOut: (OFDNSResolverContext *)context
{
	of_run_loop_mode_t runLoopMode = [OFRunLoop currentRunLoop].currentMode;
	OFDNSQueryFailedException *exception;

	if (context->_TCPSocket != nil) {
		context->_TCPSocket.delegate = nil;
		[context->_TCPSocket cancelAsyncRequests];

		[_TCPQueries removeObjectForKey: context->_TCPSocket];
		[context->_TCPSocket release];
		context->_TCPSocket = nil;
		context->_responseLength = 0;
	}

	if (context->_nameServersIndex + 1 <
	    context->_settings->_nameServers.count) {
		context->_nameServersIndex++;
		[self of_sendQueryForContext: context
				 runLoopMode: runLoopMode];
		return;
	}

	if (++context->_attempt < context->_settings->_maxAttempts) {
		context->_nameServersIndex = 0;
		[self of_sendQueryForContext: context
				 runLoopMode: runLoopMode];
		return;
	}

	context = [[context retain] autorelease];
	[_queries removeObjectForKey: context->_ID];

	/*
	 * Cancel any pending queries, to avoid a send being still pending and
	 * trying to access the query once it no longer exists.
	 */
	[_IPv4Socket cancelAsyncRequests];
	[_IPv4Socket asyncReceiveIntoBuffer: _buffer
				     length: BUFFER_LENGTH];
#ifdef OF_HAVE_IPV6
	[_IPv6Socket cancelAsyncRequests];
	[_IPv6Socket asyncReceiveIntoBuffer: _buffer
				     length: BUFFER_LENGTH];
#endif

	exception = [OFDNSQueryFailedException
	    exceptionWithQuery: context->_query
			 error: OF_DNS_RESOLVER_ERROR_TIMEOUT];

	[context->_delegate resolver: self
		     didPerformQuery: context->_query
			    response: nil
			   exception: exception];
}

- (bool)of_handleResponseBuffer: (unsigned char *)buffer
			 length: (size_t)length
			 sender: (const of_socket_address_t *)sender
{
	OFDictionary *answerRecords = nil, *authorityRecords = nil;
	OFDictionary *additionalRecords = nil;
	OFDNSResponse *response = nil;
	id exception = nil;
	OFNumber *ID;
	OFDNSResolverContext *context;







|




|





|




|
<



















|


|
<








|



|

|
|




>
|
>
>
>






|

|
|




>
|
>
>
>













|







|




|








|








|












|
<






|















|
<





|
<











|
<


|
<




|









|







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
891
892
893
894
895
896
897
898
}

- (void)setUsesTCP: (bool)usesTCP
{
	_settings->_usesTCP = usesTCP;
}

- (OFTimeInterval)configReloadInterval
{
	return _settings->_configReloadInterval;
}

- (void)setConfigReloadInterval: (OFTimeInterval)configReloadInterval
{
	_settings->_configReloadInterval = configReloadInterval;
}

- (void)of_sendQueryForContext: (OFDNSResolverContext *)context
		   runLoopMode: (OFRunLoopMode)runLoopMode
{
	OFUDPSocket *sock;
	OFString *nameServer;

	[_queries setObject: context forKey: context->_ID];


	[context->_cancelTimer invalidate];
	[context->_cancelTimer release];
	context->_cancelTimer = nil;
	context->_cancelTimer = [[OFTimer alloc]
	    initWithFireDate: [OFDate dateWithTimeIntervalSinceNow:
				  context->_settings->_timeout]
		    interval: context->_settings->_timeout
		      target: self
		    selector: @selector(of_contextTimedOut:)
		      object: context
		     repeats: false];
	[[OFRunLoop currentRunLoop] addTimer: context->_cancelTimer
				     forMode: runLoopMode];

	nameServer = [context->_settings->_nameServers
	    objectAtIndex: context->_nameServersIndex];

	if (context->_settings->_usesTCP) {
		OFEnsure(context->_TCPSocket == nil);

		context->_TCPSocket = [[OFTCPSocket alloc] init];
		[_TCPQueries setObject: context forKey: context->_TCPSocket];


		context->_TCPSocket.delegate = self;
		[context->_TCPSocket asyncConnectToHost: nameServer
						   port: 53
					    runLoopMode: runLoopMode];
		return;
	}

	context->_usedNameServer = OFSocketAddressParseIP(nameServer, 53);

	switch (context->_usedNameServer.family) {
#ifdef OF_HAVE_IPV6
	case OFSocketAddressFamilyIPv6:
		if (_IPv6Socket == nil) {
			OFSocketAddress address =
			    OFSocketAddressParseIPv6(@"::", 0);

			_IPv6Socket = [[OFUDPSocket alloc] init];
			[_IPv6Socket of_bindToAddress: &address
					    extraType: SOCK_DNS];
			@try {
				_IPv6Socket.canBlock = false;
			} @catch (OFNotImplementedException *e) {
				/* Can't do anything about it... */
			}
			_IPv6Socket.delegate = self;
		}

		sock = _IPv6Socket;
		break;
#endif
	case OFSocketAddressFamilyIPv4:
		if (_IPv4Socket == nil) {
			OFSocketAddress address =
			    OFSocketAddressParseIPv4(@"0.0.0.0", 0);

			_IPv4Socket = [[OFUDPSocket alloc] init];
			[_IPv4Socket of_bindToAddress: &address
					    extraType: SOCK_DNS];
			@try {
				_IPv4Socket.canBlock = false;
			} @catch (OFNotImplementedException *e) {
				/* Can't do anything about it... */
			}
			_IPv4Socket.delegate = self;
		}

		sock = _IPv4Socket;
		break;
	default:
		@throw [OFInvalidArgumentException exception];
	}

	[sock asyncSendData: context->_queryData
		   receiver: &context->_usedNameServer
		runLoopMode: runLoopMode];
	[sock asyncReceiveIntoBuffer: _buffer
			      length: bufferLength
			 runLoopMode: runLoopMode];
}

- (void)asyncPerformQuery: (OFDNSQuery *)query
		 delegate: (id <OFDNSResolverQueryDelegate>)delegate
{
	[self asyncPerformQuery: query
		    runLoopMode: OFDefaultRunLoopMode
		       delegate: delegate];
}

- (void)asyncPerformQuery: (OFDNSQuery *)query
	      runLoopMode: (OFRunLoopMode)runLoopMode
		 delegate: (id <OFDNSResolverQueryDelegate>)delegate
{
	void *pool = objc_autoreleasePoolPush();
	OFNumber *ID;
	OFDNSResolverContext *context;

	/* Random, unused ID */
	do {
		ID = [OFNumber numberWithUnsignedShort: OFRandom16()];
	} while ([_queries objectForKey: ID] != nil);

	if (query.domainName.UTF8StringLength > 253)
		@throw [OFOutOfRangeException exception];

	if (_settings->_nameServers.count == 0) {
		id exception = [OFDNSQueryFailedException
		    exceptionWithQuery: query
			     errorCode: OFDNSResolverErrorCodeNoNameServer];
		[delegate  resolver: self
		    didPerformQuery: query
			   response: nil
			  exception: exception];
		return;
	}

	context = [[[OFDNSResolverContext alloc]
	    initWithQuery: query
		       ID: ID
		 settings: _settings
		 delegate: delegate] autorelease];
	[self of_sendQueryForContext: context runLoopMode: runLoopMode];


	objc_autoreleasePoolPop(pool);
}

- (void)of_contextTimedOut: (OFDNSResolverContext *)context
{
	OFRunLoopMode runLoopMode = [OFRunLoop currentRunLoop].currentMode;
	OFDNSQueryFailedException *exception;

	if (context->_TCPSocket != nil) {
		context->_TCPSocket.delegate = nil;
		[context->_TCPSocket cancelAsyncRequests];

		[_TCPQueries removeObjectForKey: context->_TCPSocket];
		[context->_TCPSocket release];
		context->_TCPSocket = nil;
		context->_responseLength = 0;
	}

	if (context->_nameServersIndex + 1 <
	    context->_settings->_nameServers.count) {
		context->_nameServersIndex++;
		[self of_sendQueryForContext: context runLoopMode: runLoopMode];

		return;
	}

	if (++context->_attempt < context->_settings->_maxAttempts) {
		context->_nameServersIndex = 0;
		[self of_sendQueryForContext: context runLoopMode: runLoopMode];

		return;
	}

	context = [[context retain] autorelease];
	[_queries removeObjectForKey: context->_ID];

	/*
	 * Cancel any pending queries, to avoid a send being still pending and
	 * trying to access the query once it no longer exists.
	 */
	[_IPv4Socket cancelAsyncRequests];
	[_IPv4Socket asyncReceiveIntoBuffer: _buffer length: bufferLength];

#ifdef OF_HAVE_IPV6
	[_IPv6Socket cancelAsyncRequests];
	[_IPv6Socket asyncReceiveIntoBuffer: _buffer length: bufferLength];

#endif

	exception = [OFDNSQueryFailedException
	    exceptionWithQuery: context->_query
		     errorCode: OFDNSResolverErrorCodeTimeout];

	[context->_delegate resolver: self
		     didPerformQuery: context->_query
			    response: nil
			   exception: exception];
}

- (bool)of_handleResponseBuffer: (unsigned char *)buffer
			 length: (size_t)length
			 sender: (const OFSocketAddress *)sender
{
	OFDictionary *answerRecords = nil, *authorityRecords = nil;
	OFDictionary *additionalRecords = nil;
	OFDNSResponse *response = nil;
	id exception = nil;
	OFNumber *ID;
	OFDNSResolverContext *context;
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941

	if (context == nil)
		return true;

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

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

	@try {
		of_dns_resolver_error_t error = 0;
		bool tryNextNameServer = false;
		const unsigned char *queryDataBuffer;
		size_t i;
		uint16_t numQuestions, numAnswers, numAuthorityRecords;
		uint16_t numAdditionalRecords;

		if (length < 12)







|








|







906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929

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

	@try {
		OFDNSResolverErrorCode errorCode = 0;
		bool tryNextNameServer = false;
		const unsigned char *queryDataBuffer;
		size_t i;
		uint16_t numQuestions, numAnswers, numAuthorityRecords;
		uint16_t numAdditionalRecords;

		if (length < 12)
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

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

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

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

			context->_settings->_usesTCP = true;
			runLoopMode = [OFRunLoop currentRunLoop].currentMode;
			[self of_sendQueryForContext: context
					 runLoopMode: runLoopMode];
			return false;
		}

		/* RCODE */
		switch (buffer[3] & 0x0F) {
		case 0:
			break;
		case 1:
			error = OF_DNS_RESOLVER_ERROR_SERVER_INVALID_FORMAT;
			break;
		case 2:
			error = OF_DNS_RESOLVER_ERROR_SERVER_FAILURE;
			tryNextNameServer = true;
			break;
		case 3:
			error = OF_DNS_RESOLVER_ERROR_SERVER_NAME_ERROR;
			break;
		case 4:
			error = OF_DNS_RESOLVER_ERROR_SERVER_NOT_IMPLEMENTED;
			tryNextNameServer = true;
			break;
		case 5:
			error = OF_DNS_RESOLVER_ERROR_SERVER_REFUSED;
			tryNextNameServer = true;
			break;
		default:
			error = OF_DNS_RESOLVER_ERROR_UNKNOWN;
			tryNextNameServer = true;
			break;
		}

		if (tryNextNameServer) {
			if (context->_nameServersIndex + 1 <
			    context->_settings->_nameServers.count) {
				of_run_loop_mode_t runLoopMode =
				    [OFRunLoop currentRunLoop].currentMode;

				context->_nameServersIndex++;

				[self of_sendQueryForContext: context
						 runLoopMode: runLoopMode];
				return false;
			}
		}

		if (buffer[3] & 0x0F)
			@throw [OFDNSQueryFailedException
			    exceptionWithQuery: context->_query
					 error: error];

		numQuestions = (buffer[4] << 8) | buffer[5];
		numAnswers = (buffer[6] << 8) | buffer[7];
		numAuthorityRecords = (buffer[8] << 8) | buffer[9];
		numAdditionalRecords = (buffer[10] << 8) | buffer[11];

		i = 12;

		/*
		 * Skip over the questions - we use the ID to identify the
		 * query.
		 *
		 * TODO: Compare to our query, just in case?
		 */
		for (uint_fast16_t j = 0; j < numQuestions; j++) {
			parseName(buffer, length, &i, MAX_ALLOWED_POINTERS);
			i += 4;
		}

		answerRecords = parseSection(buffer, length, &i, numAnswers);
		authorityRecords = parseSection(buffer, length, &i,
		    numAuthorityRecords);
		additionalRecords = parseSection(buffer, length, &i,







|
















|


|



|


|



|



|







|













|















|







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

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

			context->_settings->_usesTCP = true;
			runLoopMode = [OFRunLoop currentRunLoop].currentMode;
			[self of_sendQueryForContext: context
					 runLoopMode: runLoopMode];
			return false;
		}

		/* RCODE */
		switch (buffer[3] & 0x0F) {
		case 0:
			break;
		case 1:
			errorCode = OFDNSResolverErrorCodeServerInvalidFormat;
			break;
		case 2:
			errorCode = OFDNSResolverErrorCodeServerFailure;
			tryNextNameServer = true;
			break;
		case 3:
			errorCode = OFDNSResolverErrorCodeServerNameError;
			break;
		case 4:
			errorCode = OFDNSResolverErrorCodeServerNotImplemented;
			tryNextNameServer = true;
			break;
		case 5:
			errorCode = OFDNSResolverErrorCodeServerRefused;
			tryNextNameServer = true;
			break;
		default:
			errorCode = OFDNSResolverErrorCodeUnknown;
			tryNextNameServer = true;
			break;
		}

		if (tryNextNameServer) {
			if (context->_nameServersIndex + 1 <
			    context->_settings->_nameServers.count) {
				OFRunLoopMode runLoopMode =
				    [OFRunLoop currentRunLoop].currentMode;

				context->_nameServersIndex++;

				[self of_sendQueryForContext: context
						 runLoopMode: runLoopMode];
				return false;
			}
		}

		if (buffer[3] & 0x0F)
			@throw [OFDNSQueryFailedException
			    exceptionWithQuery: context->_query
				     errorCode: errorCode];

		numQuestions = (buffer[4] << 8) | buffer[5];
		numAnswers = (buffer[6] << 8) | buffer[7];
		numAuthorityRecords = (buffer[8] << 8) | buffer[9];
		numAdditionalRecords = (buffer[10] << 8) | buffer[11];

		i = 12;

		/*
		 * Skip over the questions - we use the ID to identify the
		 * query.
		 *
		 * TODO: Compare to our query, just in case?
		 */
		for (uint_fast16_t j = 0; j < numQuestions; j++) {
			parseName(buffer, length, &i, maxAllowedPointers);
			i += 4;
		}

		answerRecords = parseSection(buffer, length, &i, numAnswers);
		authorityRecords = parseSection(buffer, length, &i,
		    numAuthorityRecords);
		additionalRecords = parseSection(buffer, length, &i,
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

	return false;
}

-	  (bool)socket: (OFDatagramSocket *)sock
  didReceiveIntoBuffer: (void *)buffer
		length: (size_t)length
		sender: (const of_socket_address_t *)sender
	     exception: (id)exception
{
	if (exception != nil)
		return true;

	return [self of_handleResponseBuffer: buffer
				      length: length
				      sender: sender];
}

-     (void)socket: (OFTCPSocket *)sock
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (id)exception
{
	OFDNSResolverContext *context = [_TCPQueries objectForKey: sock];

	OF_ENSURE(context != nil);

	if (exception != nil) {
		/*
		 * TODO: Handle error immediately instead of waiting for the
		 *	 timer to try the next nameserver or to retry.
		 */
		[_TCPQueries removeObjectForKey: context->_TCPSocket];







|

















|







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

	return false;
}

-	  (bool)socket: (OFDatagramSocket *)sock
  didReceiveIntoBuffer: (void *)buffer
		length: (size_t)length
		sender: (const OFSocketAddress *)sender
	     exception: (id)exception
{
	if (exception != nil)
		return true;

	return [self of_handleResponseBuffer: buffer
				      length: length
				      sender: sender];
}

-     (void)socket: (OFTCPSocket *)sock
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (id)exception
{
	OFDNSResolverContext *context = [_TCPQueries objectForKey: sock];

	OFEnsure(context != nil);

	if (exception != nil) {
		/*
		 * TODO: Handle error immediately instead of waiting for the
		 *	 timer to try the next nameserver or to retry.
		 */
		[_TCPQueries removeObjectForKey: context->_TCPSocket];
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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

		if (queryDataCount > UINT16_MAX)
			@throw [OFOutOfRangeException exception];

		context->_TCPQueryData = [[OFMutableData alloc]
		    initWithCapacity: queryDataCount + 2];

		tmp = OF_BSWAP16_IF_LE(queryDataCount);
		[context->_TCPQueryData addItems: &tmp
					   count: sizeof(tmp)];
		[context->_TCPQueryData addItems: context->_queryData.items
					   count: queryDataCount];
	}

	[sock asyncWriteData: context->_TCPQueryData];
}

- (OFData *)stream: (OFStream *)stream
      didWriteData: (OFData *)data
      bytesWritten: (size_t)bytesWritten
	 exception: (id)exception
{
	OFTCPSocket *sock = (OFTCPSocket *)stream;
	OFDNSResolverContext *context = [_TCPQueries objectForKey: sock];

	OF_ENSURE(context != nil);

	if (exception != nil) {
		/*
		 * TODO: Handle error immediately instead of waiting for the
		 *	 timer to try the next nameserver or to retry.
		 */
		[_TCPQueries removeObjectForKey: context->_TCPSocket];
		[context->_TCPSocket release];
		context->_TCPSocket = nil;
		context->_responseLength = 0;
		return nil;
	}

	if (context->_TCPBuffer == nil)
		context->_TCPBuffer =
		    [context allocMemoryWithSize: MAX_DNS_RESPONSE_LENGTH];

	[sock asyncReadIntoBuffer: context->_TCPBuffer
		      exactLength: 2];
	return nil;
}

-      (bool)stream: (OFStream *)stream
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (id)exception
{
	OFTCPSocket *sock = (OFTCPSocket *)stream;
	OFDNSResolverContext *context = [_TCPQueries objectForKey: sock];

	OF_ENSURE(context != nil);

	if (exception != nil) {
		/*
		 * TODO: Handle error immediately instead of waiting for the
		 *	 timer to try the next nameserver or to retry.
		 */
		goto done;
	}

	if (context->_responseLength == 0) {
		unsigned char *ucBuffer = buffer;

		OF_ENSURE(length == 2);

		context->_responseLength = (ucBuffer[0] << 8) | ucBuffer[1];

		if (context->_responseLength > MAX_DNS_RESPONSE_LENGTH)
			@throw [OFOutOfRangeException exception];

		if (context->_responseLength == 0)
			goto done;

		[sock asyncReadIntoBuffer: context->_TCPBuffer
			      exactLength: context->_responseLength];
		return false;
	}

	if (length != context->_responseLength)
		/*
		 * The connection was closed before we received the entire
		 * response.
		 */
		goto done;

	[self of_handleResponseBuffer: buffer
			       length: length
			       sender: NULL];

done:
	[_TCPQueries removeObjectForKey: context->_TCPSocket];
	[context->_TCPSocket release];
	context->_TCPSocket = nil;
	context->_responseLength = 0;

	return false;
}

- (void)asyncResolveAddressesForHost: (OFString *)host
			    delegate: (id <OFDNSResolverHostDelegate>)delegate
{
	[self asyncResolveAddressesForHost: host
			     addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY
			       runLoopMode: of_run_loop_mode_default
				  delegate: delegate];
}

- (void)asyncResolveAddressesForHost: (OFString *)host
		       addressFamily: (of_socket_address_family_t)addressFamily
			    delegate: (id <OFDNSResolverHostDelegate>)delegate
{
	[self asyncResolveAddressesForHost: host
			     addressFamily: addressFamily
			       runLoopMode: of_run_loop_mode_default
				  delegate: delegate];
}

- (void)asyncResolveAddressesForHost: (OFString *)host
		       addressFamily: (of_socket_address_family_t)addressFamily
			 runLoopMode: (of_run_loop_mode_t)runLoopMode
			    delegate: (id <OFDNSResolverHostDelegate>)delegate
{
	void *pool = objc_autoreleasePoolPush();
	OFHostAddressResolver *resolver = [[[OFHostAddressResolver alloc]
	    initWithHost: host
	   addressFamily: addressFamily
		resolver: self
		settings: _settings
	     runLoopMode: runLoopMode
		delegate: delegate] autorelease];

	[resolver asyncResolve];

	objc_autoreleasePoolPop(pool);
}

- (OFData *)resolveAddressesForHost: (OFString *)host
		      addressFamily: (of_socket_address_family_t)addressFamily
{
	void *pool = objc_autoreleasePoolPush();
	OFHostAddressResolver *resolver = [[[OFHostAddressResolver alloc]
	    initWithHost: host
	   addressFamily: addressFamily
		resolver: self
		settings: _settings







|
|
<















|














|
<

|
<











|












|



|

















|
<
<














|
|




|




|




|
|

















|







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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233

		if (queryDataCount > UINT16_MAX)
			@throw [OFOutOfRangeException exception];

		context->_TCPQueryData = [[OFMutableData alloc]
		    initWithCapacity: queryDataCount + 2];

		tmp = OFToBigEndian16(queryDataCount);
		[context->_TCPQueryData addItems: &tmp count: sizeof(tmp)];

		[context->_TCPQueryData addItems: context->_queryData.items
					   count: queryDataCount];
	}

	[sock asyncWriteData: context->_TCPQueryData];
}

- (OFData *)stream: (OFStream *)stream
      didWriteData: (OFData *)data
      bytesWritten: (size_t)bytesWritten
	 exception: (id)exception
{
	OFTCPSocket *sock = (OFTCPSocket *)stream;
	OFDNSResolverContext *context = [_TCPQueries objectForKey: sock];

	OFEnsure(context != nil);

	if (exception != nil) {
		/*
		 * TODO: Handle error immediately instead of waiting for the
		 *	 timer to try the next nameserver or to retry.
		 */
		[_TCPQueries removeObjectForKey: context->_TCPSocket];
		[context->_TCPSocket release];
		context->_TCPSocket = nil;
		context->_responseLength = 0;
		return nil;
	}

	if (context->_TCPBuffer == nil)
		context->_TCPBuffer = OFAllocMemory(maxDNSResponseLength, 1);


	[sock asyncReadIntoBuffer: context->_TCPBuffer exactLength: 2];

	return nil;
}

-      (bool)stream: (OFStream *)stream
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (id)exception
{
	OFTCPSocket *sock = (OFTCPSocket *)stream;
	OFDNSResolverContext *context = [_TCPQueries objectForKey: sock];

	OFEnsure(context != nil);

	if (exception != nil) {
		/*
		 * TODO: Handle error immediately instead of waiting for the
		 *	 timer to try the next nameserver or to retry.
		 */
		goto done;
	}

	if (context->_responseLength == 0) {
		unsigned char *ucBuffer = buffer;

		OFEnsure(length == 2);

		context->_responseLength = (ucBuffer[0] << 8) | ucBuffer[1];

		if (context->_responseLength > maxDNSResponseLength)
			@throw [OFOutOfRangeException exception];

		if (context->_responseLength == 0)
			goto done;

		[sock asyncReadIntoBuffer: context->_TCPBuffer
			      exactLength: context->_responseLength];
		return false;
	}

	if (length != context->_responseLength)
		/*
		 * The connection was closed before we received the entire
		 * response.
		 */
		goto done;

	[self of_handleResponseBuffer: buffer length: length sender: NULL];



done:
	[_TCPQueries removeObjectForKey: context->_TCPSocket];
	[context->_TCPSocket release];
	context->_TCPSocket = nil;
	context->_responseLength = 0;

	return false;
}

- (void)asyncResolveAddressesForHost: (OFString *)host
			    delegate: (id <OFDNSResolverHostDelegate>)delegate
{
	[self asyncResolveAddressesForHost: host
			     addressFamily: OFSocketAddressFamilyAny
			       runLoopMode: OFDefaultRunLoopMode
				  delegate: delegate];
}

- (void)asyncResolveAddressesForHost: (OFString *)host
		       addressFamily: (OFSocketAddressFamily)addressFamily
			    delegate: (id <OFDNSResolverHostDelegate>)delegate
{
	[self asyncResolveAddressesForHost: host
			     addressFamily: addressFamily
			       runLoopMode: OFDefaultRunLoopMode
				  delegate: delegate];
}

- (void)asyncResolveAddressesForHost: (OFString *)host
		       addressFamily: (OFSocketAddressFamily)addressFamily
			 runLoopMode: (OFRunLoopMode)runLoopMode
			    delegate: (id <OFDNSResolverHostDelegate>)delegate
{
	void *pool = objc_autoreleasePoolPush();
	OFHostAddressResolver *resolver = [[[OFHostAddressResolver alloc]
	    initWithHost: host
	   addressFamily: addressFamily
		resolver: self
		settings: _settings
	     runLoopMode: runLoopMode
		delegate: delegate] autorelease];

	[resolver asyncResolve];

	objc_autoreleasePoolPop(pool);
}

- (OFData *)resolveAddressesForHost: (OFString *)host
		      addressFamily: (OFSocketAddressFamily)addressFamily
{
	void *pool = objc_autoreleasePoolPush();
	OFHostAddressResolver *resolver = [[[OFHostAddressResolver alloc]
	    initWithHost: host
	   addressFamily: addressFamily
		resolver: self
		settings: _settings
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296

	enumerator = [_queries objectEnumerator];
	while ((context = [enumerator nextObject]) != nil) {
		OFDNSQueryFailedException *exception;

		exception = [OFDNSQueryFailedException
		    exceptionWithQuery: context->_query
				 error: OF_DNS_RESOLVER_ERROR_CANCELED];

		[context->_delegate resolver: self
			     didPerformQuery: context->_query
				    response: nil
				   exception: exception];
	}

	[_queries removeAllObjects];

	objc_autoreleasePoolPop(pool);
}
@end







|












1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279

	enumerator = [_queries objectEnumerator];
	while ((context = [enumerator nextObject]) != nil) {
		OFDNSQueryFailedException *exception;

		exception = [OFDNSQueryFailedException
		    exceptionWithQuery: context->_query
			     errorCode: OFDNSResolverErrorCodeCanceled];

		[context->_delegate resolver: self
			     didPerformQuery: context->_query
				    response: nil
				   exception: exception];
	}

	[_queries removeAllObjects];

	objc_autoreleasePoolPop(pool);
}
@end

Modified src/OFDNSResolverSettings.h from [ff67eea8fc] to [1250cc4b4c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
@public
	OFDictionary OF_GENERIC(OFString *, OFArray OF_GENERIC(OFString *) *)
	    *_staticHosts;
	OFArray OF_GENERIC(OFString *) *_nameServers;
	OFString *_Nullable _localDomain;
	OFArray OF_GENERIC(OFString *) *_searchDomains;
	of_time_interval_t _timeout;
	unsigned int _maxAttempts, _minNumberOfDotsInAbsoluteName;
	bool _usesTCP;
	of_time_interval_t _configReloadInterval;
@protected
	OFDate *_lastConfigReload;
}

- (void)reload;
@end

OF_ASSUME_NONNULL_END







|


|








25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
{
@public
	OFDictionary OF_GENERIC(OFString *, OFArray OF_GENERIC(OFString *) *)
	    *_staticHosts;
	OFArray OF_GENERIC(OFString *) *_nameServers;
	OFString *_Nullable _localDomain;
	OFArray OF_GENERIC(OFString *) *_searchDomains;
	OFTimeInterval _timeout;
	unsigned int _maxAttempts, _minNumberOfDotsInAbsoluteName;
	bool _usesTCP;
	OFTimeInterval _configReloadInterval;
@protected
	OFDate *_lastConfigReload;
}

- (void)reload;
@end

OF_ASSUME_NONNULL_END

Modified src/OFDNSResolverSettings.m from [7c81772fe9] to [94502fce5f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
22
23
24
25
26
27
28

29
30
31
32
33
34
35
#import "OFDNSResolverSettings.h"
#import "OFArray.h"
#import "OFCharacterSet.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFFile.h"
#import "OFLocale.h"

#import "OFString.h"
#ifdef OF_WINDOWS
# import "OFWindowsRegistryKey.h"
#endif

#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"







>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#import "OFDNSResolverSettings.h"
#import "OFArray.h"
#import "OFCharacterSet.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFFile.h"
#import "OFLocale.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#ifdef OF_WINDOWS
# import "OFWindowsRegistryKey.h"
#endif

#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"
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
#ifdef OF_NINTENDO_3DS
/* Newer versions of libctru started using id as a parameter name. */
# define id id_3ds
# include <3ds.h>
# undef id
#endif



#import "socket_helpers.h"



#if defined(OF_HAIKU)
# define HOSTS_PATH @"/system/settings/network/hosts"
# define RESOLV_CONF_PATH @"/system/settings/network/resolv.conf"
#elif defined(OF_MORPHOS)
# define HOSTS_PATH @"ENV:sys/net/hosts"
# define RESOLV_CONF_PATH @"ENV:sys/net/resolv.conf"
#elif defined(OF_AMIGAOS4)
# define HOSTS_PATH @"DEVS:Internet/hosts"
#elif defined(OF_AMIGAOS)
# define HOSTS_PATH @"AmiTCP:db/hosts"
# define RESOLV_CONF_PATH @"AmiTCP:db/resolv.conf"
#else
# define HOSTS_PATH @"/etc/hosts"
# define RESOLV_CONF_PATH @"/etc/resolv.conf"
#endif

#ifndef OF_WII
static OFString *
domainFromHostname(void)
{
	char hostname[256];
	OFString *domain, *ret;

	if (gethostname(hostname, 256) != 0)
		return nil;

	domain = [OFString stringWithCString: hostname
				    encoding: [OFLocale encoding]];

	@try {
		of_socket_address_parse_ip(domain, 0);

		/*
		 * If we are still here, the host name is a valid IP address.
		 * We can't use that as local domain.
		 */
		return nil;
	} @catch (OFInvalidFormatException *e) {
		/* Not an IP address -> we can use it if it contains a dot. */
		size_t pos = [domain rangeOfString: @"."].location;

		if (pos == OF_NOT_FOUND)
			return nil;

		ret = [domain substringFromIndex: pos + 1];
	}

	return ret;
}
#endif























































































@implementation OFDNSResolverSettings
- (void)dealloc
{
	[_staticHosts release];
	[_nameServers release];
	[_localDomain release];







>
>
|
>
>




<
<
<












|

<
|

|


<
<
<

|





|


|

|
|
|
|





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







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
#ifdef OF_NINTENDO_3DS
/* Newer versions of libctru started using id as a parameter name. */
# define id id_3ds
# include <3ds.h>
# undef id
#endif

#ifdef OF_MORPHOS
# include <proto/rexxsyslib.h>
# include <rexx/errors.h>
# include <rexx/storage.h>
#endif

#if defined(OF_HAIKU)
# define HOSTS_PATH @"/system/settings/network/hosts"
# define RESOLV_CONF_PATH @"/system/settings/network/resolv.conf"



#elif defined(OF_AMIGAOS4)
# define HOSTS_PATH @"DEVS:Internet/hosts"
#elif defined(OF_AMIGAOS)
# define HOSTS_PATH @"AmiTCP:db/hosts"
# define RESOLV_CONF_PATH @"AmiTCP:db/resolv.conf"
#else
# define HOSTS_PATH @"/etc/hosts"
# define RESOLV_CONF_PATH @"/etc/resolv.conf"
#endif

#ifndef OF_WII
static OFString *
domainFromHostname(OFString *hostname)
{

	OFString *ret;

	if (hostname == nil)
		return nil;




	@try {
		OFSocketAddressParseIP(hostname, 0);

		/*
		 * If we are still here, the host name is a valid IP address.
		 * We can't use that as local domain.
		 */
		ret = nil;
	} @catch (OFInvalidFormatException *e) {
		/* Not an IP address -> we can use it if it contains a dot. */
		size_t pos = [hostname rangeOfString: @"."].location;

		if (pos != OFNotFound)
			ret = [hostname substringFromIndex: pos + 1];
		else
			ret = nil;
	}

	return ret;
}
#endif

#if !defined(OF_WII) && !defined(OF_MORPHOS)
static OFString *
obtainHostname(void)
{
	char hostname[256];

	if (gethostname(hostname, 256) != 0)
		return nil;

	return [OFString stringWithCString: hostname
				  encoding: [OFLocale encoding]];
}
#endif

#ifdef OF_MORPHOS
static OFString *
arexxCommand(const char *port, const char *command)
{
	struct Library *RexxSysBase;
	struct MsgPort *replyPort = NULL;
	struct RexxMsg *msg = NULL;

	if ((RexxSysBase = OpenLibrary("rexxsyslib.library", 36)) == NULL)
		return nil;

	@try {
		struct MsgPort *rexxPort;

		if ((replyPort = CreateMsgPort()) == NULL)
			return nil;

		if ((msg = CreateRexxMsg(replyPort, NULL, port)) == NULL)
			return nil;

		msg->rm_Action = RXCOMM | RXFF_RESULT;

		if ((msg->rm_Args[0] = (char *)CreateArgstring(
		    command, strlen(command))) == NULL)
			return nil;

		Forbid();

		if ((rexxPort = FindPort(port)) == NULL) {
			Permit();
			return nil;
		}

		PutMsg(rexxPort, &msg->rm_Node);
		Permit();
		WaitPort(replyPort);
		GetMsg(replyPort);

		if (msg->rm_Result1 != RC_OK || msg->rm_Result2 == 0)
			return nil;

		return [OFString stringWithCString: (char *)msg->rm_Result2
					  encoding: [OFLocale encoding]];
	} @finally {
		if (msg != NULL) {
			if (msg->rm_Args[0] != NULL)
				DeleteArgstring(msg->rm_Args[0]);
			if (msg->rm_Result2 != 0)
				DeleteArgstring((char *)msg->rm_Result2);

			DeleteRexxMsg(msg);
		}

		if (replyPort != NULL)
			DeleteMsgPort(replyPort);

		CloseLibrary(RexxSysBase);
	}
}

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
{
	[_staticHosts release];
	[_nameServers release];
	[_localDomain release];
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
#ifndef OF_NINTENDO_3DS
	_configReloadInterval = 2;
#else
	_configReloadInterval = 0;
#endif
}

#if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_3DS)
- (void)parseHosts: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFCharacterSet *whitespaceCharacterSet =
	    [OFCharacterSet whitespaceCharacterSet];
	OFMutableDictionary *staticHosts;
	OFFile *file;
	OFString *line;
	OFEnumerator *enumerator;
	OFMutableArray *addresses;

	@try {
		file = [OFFile fileWithPath: path
				       mode: @"r"];
	} @catch (OFOpenItemFailedException *e) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	staticHosts = [OFMutableDictionary dictionary];

	while ((line = [file readLine]) != nil) {
		void *pool2 = objc_autoreleasePoolPush();
		OFArray *components, *hosts;
		size_t pos;
		OFString *address;

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

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

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

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

		for (OFString *host in hosts) {

			addresses = [staticHosts objectForKey: host];

			if (addresses == nil) {
				addresses = [OFMutableArray array];
				[staticHosts setObject: addresses
						forKey: host];
			}

			[addresses addObject: address];
		}

		objc_autoreleasePoolPop(pool2);
	}

	enumerator = [staticHosts objectEnumerator];
	while ((addresses = [enumerator nextObject]) != nil)
		[addresses makeImmutable];

	[staticHosts makeImmutable];

	[_staticHosts release];
	_staticHosts = [staticHosts copy];

	objc_autoreleasePoolPop(pool);
}

# if !defined(OF_WINDOWS) && !defined(OF_AMIGAOS4)
- (void)parseResolvConfOption: (OFString *)option







|








<
<


|
<








<





|




|

|
<

<



|


>
|



|
<




|
<
<
<
|
<



<
<







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
#ifndef OF_NINTENDO_3DS
	_configReloadInterval = 2;
#else
	_configReloadInterval = 0;
#endif
}

#if defined(OF_HAVE_FILES) && !defined(OF_MORPHOS) && !defined(OF_NINTENDO_3DS)
- (void)parseHosts: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFCharacterSet *whitespaceCharacterSet =
	    [OFCharacterSet whitespaceCharacterSet];
	OFMutableDictionary *staticHosts;
	OFFile *file;
	OFString *line;



	@try {
		file = [OFFile fileWithPath: path mode: @"r"];

	} @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];
				[staticHosts setObject: addresses forKey: host];

			}

			[addresses addObject: address];
		}
	}



	for (OFMutableArray *addresses in [staticHosts objectEnumerator])

		[addresses makeImmutable];

	[staticHosts makeImmutable];


	_staticHosts = [staticHosts copy];

	objc_autoreleasePoolPop(pool);
}

# if !defined(OF_WINDOWS) && !defined(OF_AMIGAOS4)
- (void)parseResolvConfOption: (OFString *)option
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
	OFCharacterSet *commentCharacters = [OFCharacterSet
	    characterSetWithCharactersInString: @"#;"];
	OFMutableArray *nameServers = [[_nameServers mutableCopy] autorelease];
	OFFile *file;
	OFString *line;

	@try {
		file = [OFFile fileWithPath: path
				       mode: @"r"];
	} @catch (OFOpenItemFailedException *e) {
		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 != OF_NOT_FOUND)
			line = [line substringToIndex: pos];

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

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

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

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

			[nameServers addObject: [arguments firstObject]];
		} else if ([option isEqual: @"domain"]) {
			if (arguments.count != 1) {
				objc_autoreleasePoolPop(pool2);
				continue;
			}

			[_localDomain release];







|
<















|




|








|







|







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
	OFCharacterSet *commentCharacters = [OFCharacterSet
	    characterSetWithCharactersInString: @"#;"];
	OFMutableArray *nameServers = [[_nameServers mutableCopy] autorelease];
	OFFile *file;
	OFString *line;

	@try {
		file = [OFFile fileWithPath: path mode: @"r"];

	} @catch (OFOpenItemFailedException *e) {
		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;
			}

			[nameServers addObject: arguments.firstObject];
		} else if ([option isEqual: @"domain"]) {
			if (arguments.count != 1) {
				objc_autoreleasePoolPop(pool2);
				continue;
			}

			[_localDomain release];
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
}
# endif
#endif

#ifdef OF_WINDOWS
- (void)obtainWindowsSystemConfig
{
	of_string_encoding_t encoding = [OFLocale encoding];
	OFMutableArray *nameServers;
	/*
	 * We need more space than FIXED_INFO in case we have more than one
	 * name server, but we also want it to be properly aligned, meaning we
	 * can't just get a buffer of bytes. Thus, we just get space for 8.
	 */
	FIXED_INFO fixedInfo[8];







|







421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
}
# endif
#endif

#ifdef OF_WINDOWS
- (void)obtainWindowsSystemConfig
{
	OFStringEncoding encoding = [OFLocale encoding];
	OFMutableArray *nameServers;
	/*
	 * We need more space than FIXED_INFO in case we have more than one
	 * name server, but we also want it to be properly aligned, meaning we
	 * can't just get a buffer of bytes. Thus, we just get space for 8.
	 */
	FIXED_INFO fixedInfo[8];
384
385
386
387
388
389
390
391


















































392
393
394
395
396
397
398
399
400
401
402
403

	if (fixedInfo->DomainName[0] != '\0')
		_localDomain = [[OFString alloc]
		    initWithCString: fixedInfo->DomainName
			   encoding: encoding];
}
#endif



















































#ifdef OF_AMIGAOS4
- (void)obtainAmigaOS4SystemConfig
{
	OFMutableArray *nameServers = [OFMutableArray array];
	of_string_encoding_t encoding = [OFLocale encoding];
	struct List *nameServerList = ObtainDomainNameServerList();
	char buffer[MAXHOSTNAMELEN];

	if (nameServerList == NULL)
		@throw [OFOutOfMemoryException exception];

	@try {








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




|







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

	if (fixedInfo->DomainName[0] != '\0')
		_localDomain = [[OFString alloc]
		    initWithCString: fixedInfo->DomainName
			   encoding: encoding];
}
#endif

#ifdef OF_MORPHOS
- (void)obtainMorphOSSystemConfig
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary *staticHosts;

	_nameServers = [parseNetStackArray(arexxCommand("NETSTACK",
	    "QUERY NAMESERVERS")) copy];
	_localDomain = [domainFromHostname(arexxCommand("NETSTACK",
	    "QUERY HOSTNAME")) copy];
	_searchDomains = [parseNetStackArray(arexxCommand("NETSTACK",
	    "QUERY DOMAINS")) copy];

	staticHosts = [OFMutableDictionary dictionary];

	for (OFString *entry in parseNetStackArray(arexxCommand("NETSTACK",
	    "QUERY HOSTS"))) {
		OFArray *components = [entry componentsSeparatedByString: @" "];
		OFString *address;
		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];
				[staticHosts setObject: addresses forKey: host];
			}

			[addresses addObject: address];
		}
	}
	for (OFMutableArray *addresses in [staticHosts objectEnumerator])
		[addresses makeImmutable];

	[staticHosts makeImmutable];
	_staticHosts = [staticHosts copy];

	objc_autoreleasePoolPop(pool);
}
#endif

#ifdef OF_AMIGAOS4
- (void)obtainAmigaOS4SystemConfig
{
	OFMutableArray *nameServers = [OFMutableArray array];
	OFStringEncoding encoding = [OFLocale encoding];
	struct List *nameServerList = ObtainDomainNameServerList();
	char buffer[MAXHOSTNAMELEN];

	if (nameServerList == NULL)
		@throw [OFOutOfMemoryException exception];

	@try {
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
	 * We're fine if this gets smaller in a future release (unlikely), as
	 * long as two entries still fit.
	 */
	if (optLen < sizeof(buffer.entries))
		return;

	for (uint_fast8_t i = 0; i < 2; i++) {
		uint32_t ip = OF_BSWAP32_IF_LE(buffer.entries[i].ip.s_addr);

		if (ip == 0)
			continue;

		[nameServers addObject: [OFString stringWithFormat:
		    @"%u.%u.%u.%u", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF,
		    (ip >> 8) & 0xFF, ip & 0xFF]];







|







573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
	 * We're fine if this gets smaller in a future release (unlikely), as
	 * long as two entries still fit.
	 */
	if (optLen < sizeof(buffer.entries))
		return;

	for (uint_fast8_t i = 0; i < 2; i++) {
		uint32_t ip = OFFromBigEndian32(buffer.entries[i].ip.s_addr);

		if (ip == 0)
			continue;

		[nameServers addObject: [OFString stringWithFormat:
		    @"%u.%u.%u.%u", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF,
		    (ip >> 8) & 0xFF, ip & 0xFF]];
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512


513
514
515
516
517
518
519

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

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

	[self obtainWindowsSystemConfig];


#elif defined(OF_AMIGAOS4)
	[self parseHosts: HOSTS_PATH];
	[self obtainAmigaOS4SystemConfig];
#elif defined(OF_NINTENDO_3DS)
	[self obtainNintendo3DSSytemConfig];
#elif defined(OF_HAVE_FILES)
	[self parseHosts: HOSTS_PATH];







|








>
>







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

#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

	[self obtainWindowsSystemConfig];
#elif defined(OF_MORPHOS)
	[self obtainMorphOSSystemConfig];
#elif defined(OF_AMIGAOS4)
	[self parseHosts: HOSTS_PATH];
	[self obtainAmigaOS4SystemConfig];
#elif defined(OF_NINTENDO_3DS)
	[self obtainNintendo3DSSytemConfig];
#elif defined(OF_HAVE_FILES)
	[self parseHosts: HOSTS_PATH];
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
#ifdef OF_HAVE_IPV6
		_nameServers = [[OFArray alloc]
		    initWithObjects: @"127.0.0.1", @"::1", nil];
#else
		_nameServers = [[OFArray alloc] initWithObject: @"127.0.0.1"];
#endif

#ifndef OF_WII
	if (_localDomain == nil)
		_localDomain = [domainFromHostname() copy];
#endif

	if (_searchDomains == nil) {
		if (_localDomain != nil)
			_searchDomains = [[OFArray alloc]
			    initWithObject: _localDomain];
		else
			_searchDomains = [[OFArray alloc] init];
	}

	objc_autoreleasePoolPop(pool);
}
@end







|

|













658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
#ifdef OF_HAVE_IPV6
		_nameServers = [[OFArray alloc]
		    initWithObjects: @"127.0.0.1", @"::1", nil];
#else
		_nameServers = [[OFArray alloc] initWithObject: @"127.0.0.1"];
#endif

#if !defined(OF_WII) && !defined(OF_MORPHOS)
	if (_localDomain == nil)
		_localDomain = [domainFromHostname(obtainHostname()) copy];
#endif

	if (_searchDomains == nil) {
		if (_localDomain != nil)
			_searchDomains = [[OFArray alloc]
			    initWithObject: _localDomain];
		else
			_searchDomains = [[OFArray alloc] init];
	}

	objc_autoreleasePoolPop(pool);
}
@end

Modified src/OFDNSResourceRecord.h from [4b74104aa3] to [1c8f2631a7].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFArray OF_GENERIC(ObjectType);
@class OFData;

/**
 * @brief The DNS class.
 */
typedef enum {
	/** IN */
	OF_DNS_CLASS_IN	 =   1,
	/** Any class. Only for queries. */
	OF_DNS_CLASS_ANY = 255,
} of_dns_class_t;

/**
 * @brief The type of a DNS resource record.
 */
typedef enum {
	/** A */
	OF_DNS_RECORD_TYPE_A	 =   1,
	/** NS */
	OF_DNS_RECORD_TYPE_NS	 =   2,
	/** CNAME */
	OF_DNS_RECORD_TYPE_CNAME =   5,
	/** SOA */
	OF_DNS_RECORD_TYPE_SOA	 =   6,
	/** PTR */
	OF_DNS_RECORD_TYPE_PTR	 =  12,
	/** HINFO */
	OF_DNS_RECORD_TYPE_HINFO =  13,
	/** MX */
	OF_DNS_RECORD_TYPE_MX	 =  15,
	/** TXT */
	OF_DNS_RECORD_TYPE_TXT	 =  16,
	/** RP */
	OF_DNS_RECORD_TYPE_RP	 =  17,
	/** AAAA */
	OF_DNS_RECORD_TYPE_AAAA	 =  28,
	/** SRV */
	OF_DNS_RECORD_TYPE_SRV	 =  33,
	/** All types. Only for queries. */
	OF_DNS_RECORD_TYPE_ALL	 = 255,
} of_dns_record_type_t;

/**
 * @class OFDNSResourceRecord OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class representing a DNS resource record.
 */
@interface OFDNSResourceRecord: OFObject <OFCopying>
{
	OFString *_name;
	of_dns_class_t _DNSClass;
	of_dns_record_type_t _recordType;
	uint32_t _TTL;
	OF_RESERVE_IVARS(OFDNSResourceRecord, 4)
}

/**
 * @brief The domain name to which the resource record belongs.
 */
@property (readonly, nonatomic) OFString *name;

/**
 * @brief The DNS class.
 */
@property (readonly, nonatomic) of_dns_class_t DNSClass;

/**
 * @brief The resource record type code.
 */
@property (readonly, nonatomic) of_dns_record_type_t recordType;

/**
 * @brief The number of seconds after which the resource record should be
 *	  discarded from the cache.
 */
@property (readonly, nonatomic) uint32_t TTL;

/**
 * @brief Initializes an already allocated OFDNSResourceRecord with the
 *	  specified name, class, type, data and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param recordType The type code for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFADNSResourceRecord OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class representing an A DNS resource record.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFADNSResourceRecord: OFDNSResourceRecord
{
	of_socket_address_t _address;
}

/**
 * @brief The IPv4 address of the resource record.
 */
@property (readonly, nonatomic) const of_socket_address_t *address;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFADNSResourceRecord with the
 *	  specified name, class, address and time to live.
 *
 * @param name The name for the resource record
 * @param address The address for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFADNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		     address: (const of_socket_address_t *)address
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFAAAADNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class represenging a DNS resource record.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFAAAADNSResourceRecord: OFDNSResourceRecord
{
	of_socket_address_t _address;
}

/**
 * @brief The IPv6 address of the resource record.
 */
@property (readonly, nonatomic) const of_socket_address_t *address;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFAAAADNSResourceRecord with the
 *	  specified name, class, address and time to live.
 *
 * @param name The name for the resource record
 * @param address The address for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFAAAADNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		     address: (const of_socket_address_t *)address
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFCNAMEDNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *

<
<
|














|
<
|













|

|
|






|

|

|

|

|

|

|

|

|

|

|

|
|









|
|












|




|


















|
|











|





|


|
|












|












|





|


|
|












|







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


 * 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 "OFSocket.h"

#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFArray OF_GENERIC(ObjectType);
@class OFData;

/**
 * @brief The DNS class.
 */
typedef enum {
	/** IN */
	OFDNSClassIN  =   1,
	/** Any class. Only for queries. */
	OFDNSClassAny = 255,
} OFDNSClass;

/**
 * @brief The type of a DNS resource record.
 */
typedef enum {
	/** A */
	OFDNSRecordTypeA     =   1,
	/** NS */
	OFDNSRecordTypeNS    =   2,
	/** CNAME */
	OFDNSRecordTypeCNAME =   5,
	/** SOA */
	OFDNSRecordTypeSOA   =   6,
	/** PTR */
	OFDNSRecordTypePTR   =  12,
	/** HINFO */
	OFDNSRecordTypeHINFO =  13,
	/** MX */
	OFDNSRecordTypeMX    =  15,
	/** TXT */
	OFDNSRecordTypeTXT   =  16,
	/** RP */
	OFDNSRecordTypeRP    =  17,
	/** AAAA */
	OFDNSRecordTypeAAAA  =  28,
	/** SRV */
	OFDNSRecordTypeSRV   =  33,
	/** All types. Only for queries. */
	OFDNSRecordTypeAll   = 255,
} OFDNSRecordType;

/**
 * @class OFDNSResourceRecord OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class representing a DNS resource record.
 */
@interface OFDNSResourceRecord: OFObject <OFCopying>
{
	OFString *_name;
	OFDNSClass _DNSClass;
	OFDNSRecordType _recordType;
	uint32_t _TTL;
	OF_RESERVE_IVARS(OFDNSResourceRecord, 4)
}

/**
 * @brief The domain name to which the resource record belongs.
 */
@property (readonly, nonatomic) OFString *name;

/**
 * @brief The DNS class.
 */
@property (readonly, nonatomic) OFDNSClass DNSClass;

/**
 * @brief The resource record type code.
 */
@property (readonly, nonatomic) OFDNSRecordType recordType;

/**
 * @brief The number of seconds after which the resource record should be
 *	  discarded from the cache.
 */
@property (readonly, nonatomic) uint32_t TTL;

/**
 * @brief Initializes an already allocated OFDNSResourceRecord with the
 *	  specified name, class, type, data and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param recordType The type code for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFADNSResourceRecord OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class representing an A DNS resource record.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFADNSResourceRecord: OFDNSResourceRecord
{
	OFSocketAddress _address;
}

/**
 * @brief The IPv4 address of the resource record.
 */
@property (readonly, nonatomic) const OFSocketAddress *address;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFADNSResourceRecord with the
 *	  specified name, class, address and time to live.
 *
 * @param name The name for the resource record
 * @param address The address for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFADNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		     address: (const OFSocketAddress *)address
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFAAAADNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class represenging a DNS resource record.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFAAAADNSResourceRecord: OFDNSResourceRecord
{
	OFSocketAddress _address;
}

/**
 * @brief The IPv6 address of the resource record.
 */
@property (readonly, nonatomic) const OFSocketAddress *address;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFAAAADNSResourceRecord with the
 *	  specified name, class, address and time to live.
 *
 * @param name The name for the resource record
 * @param address The address for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFAAAADNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		     address: (const OFSocketAddress *)address
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFCNAMEDNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
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

/**
 * @brief The alias of the resource record.
 */
@property (readonly, nonatomic) OFString *alias;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFCNAMEDNSResourceRecord with the
 *	  specified name, class, alias and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param alias The alias for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFCNAMEDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		       alias: (OFString *)alias
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFHINFODNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h







|
|













|







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

/**
 * @brief The alias of the resource record.
 */
@property (readonly, nonatomic) OFString *alias;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFCNAMEDNSResourceRecord with the
 *	  specified name, class, alias and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param alias The alias for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFCNAMEDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		       alias: (OFString *)alias
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFHINFODNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
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

/**
 * @brief The OS of the host info of the resource record.
 */
@property (readonly, nonatomic) OFString *OS;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFHINFODNSResourceRecord with the
 *	  specified name, class, domain name and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param CPU The CPU of the host info for the resource record
 * @param OS The OS of the host info for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFHINFODNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
			 CPU: (OFString *)CPU
			  OS: (OFString *)OS
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFMXDNSResourceRecord \







|
|














|







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

/**
 * @brief The OS of the host info of the resource record.
 */
@property (readonly, nonatomic) OFString *OS;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFHINFODNSResourceRecord with the
 *	  specified name, class, domain name and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param CPU The CPU of the host info for the resource record
 * @param OS The OS of the host info for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFHINFODNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
			 CPU: (OFString *)CPU
			  OS: (OFString *)OS
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFMXDNSResourceRecord \
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

/**
 * @brief The mail exchange of the resource record.
 */
@property (readonly, nonatomic) OFString *mailExchange;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFMXDNSResourceRecord with the
 *	  specified name, class, preference, mail exchange and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param preference The preference for the resource record
 * @param mailExchange The mail exchange for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFMXDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  preference: (uint16_t)preference
		mailExchange: (OFString *)mailExchange
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFNSDNSResourceRecord \







|
|














|







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

/**
 * @brief The mail exchange of the resource record.
 */
@property (readonly, nonatomic) OFString *mailExchange;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFMXDNSResourceRecord with the
 *	  specified name, class, preference, mail exchange and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param preference The preference for the resource record
 * @param mailExchange The mail exchange for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFMXDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  preference: (uint16_t)preference
		mailExchange: (OFString *)mailExchange
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFNSDNSResourceRecord \
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

/**
 * @brief The authoritative host of the resource record.
 */
@property (readonly, nonatomic) OFString *authoritativeHost;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFNSDNSResourceRecord with the
 *	  specified name, class, authoritative host and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param authoritativeHost The authoritative host for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFNSDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
	   authoritativeHost: (OFString *)authoritativeHost
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFPTRDNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h







|
|













|







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

/**
 * @brief The authoritative host of the resource record.
 */
@property (readonly, nonatomic) OFString *authoritativeHost;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFNSDNSResourceRecord with the
 *	  specified name, class, authoritative host and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param authoritativeHost The authoritative host for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFNSDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
	   authoritativeHost: (OFString *)authoritativeHost
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFPTRDNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
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

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

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFPTRDNSResourceRecord with the
 *	  specified name, class, domain name and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param domainName The domain name for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFPTRDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  domainName: (OFString *)domainName
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFRPNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h







|
|













|







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

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

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFPTRDNSResourceRecord with the
 *	  specified name, class, domain name and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param domainName The domain name for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFPTRDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  domainName: (OFString *)domainName
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFRPNSResourceRecord \
 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
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
/**
 * @brief A domain name that contains a TXT resource record for the responsible
 *	  person of the resource record.
 */
@property (readonly, nonatomic) OFString *TXTDomainName;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFRPDNSResourceRecord with the
 *	  specified name, class, alias and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param mailbox The mailbox of the responsible person of the resource record
 * @param TXTDomainName A domain name that contains a TXT resource record for
 *			the responsible person of the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFRPDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		     mailbox: (OFString *)mailbox
	       TXTDomainName: (OFString *)TXTDomainName
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFSOADNSResourceRecord \







|
|















|







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
/**
 * @brief A domain name that contains a TXT resource record for the responsible
 *	  person of the resource record.
 */
@property (readonly, nonatomic) OFString *TXTDomainName;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFRPDNSResourceRecord with the
 *	  specified name, class, alias and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param mailbox The mailbox of the responsible person of the resource record
 * @param TXTDomainName A domain name that contains a TXT resource record for
 *			the responsible person of the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFRPDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		     mailbox: (OFString *)mailbox
	       TXTDomainName: (OFString *)TXTDomainName
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

/**
 * @class OFSOADNSResourceRecord \
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

/**
 * @brief The minimum TTL of the zone.
 */
@property (readonly, nonatomic) uint32_t minTTL;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFSOADNSResourceRecord with the
 *	  specified name, class, text data and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param primaryNameServer The the primary name server for the zone
 * @param responsiblePerson The mailbox of the person responsible for the zone
 * @param serialNumber The serial number of the original copy of the zone
 * @param refreshInterval The refresh interval of the zone
 * @param retryInterval The retry interval of the zone
 * @param expirationInterval The expiration interval of the zone
 * @param minTTL The minimum TTL of the zone
 * @param TTL The time to live for the resource record
 * @return An initialized OFSOADNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
	   primaryNameServer: (OFString *)primaryNameServer
	   responsiblePerson: (OFString *)responsiblePerson
		serialNumber: (uint32_t)serialNumber
	     refreshInterval: (uint32_t)refreshInterval
	       retryInterval: (uint32_t)retryInterval
	  expirationInterval: (uint32_t)expirationInterval
		      minTTL: (uint32_t)minTTL







|
|



















|







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

/**
 * @brief The minimum TTL of the zone.
 */
@property (readonly, nonatomic) uint32_t minTTL;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFSOADNSResourceRecord with the
 *	  specified name, class, text data and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param primaryNameServer The the primary name server for the zone
 * @param responsiblePerson The mailbox of the person responsible for the zone
 * @param serialNumber The serial number of the original copy of the zone
 * @param refreshInterval The refresh interval of the zone
 * @param retryInterval The retry interval of the zone
 * @param expirationInterval The expiration interval of the zone
 * @param minTTL The minimum TTL of the zone
 * @param TTL The time to live for the resource record
 * @return An initialized OFSOADNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
	   primaryNameServer: (OFString *)primaryNameServer
	   responsiblePerson: (OFString *)responsiblePerson
		serialNumber: (uint32_t)serialNumber
	     refreshInterval: (uint32_t)refreshInterval
	       retryInterval: (uint32_t)retryInterval
	  expirationInterval: (uint32_t)expirationInterval
		      minTTL: (uint32_t)minTTL
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569

/**
 * @brief The port on the target of the resource record.
 */
@property (readonly, nonatomic) uint16_t port;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFSRVDNSResourceRecord with the
 *	  specified name, class, preference, mail exchange and time to live.
 *
 * @param name The name for the resource record







|
|







551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566

/**
 * @brief The port on the target of the resource record.
 */
@property (readonly, nonatomic) uint16_t port;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFSRVDNSResourceRecord with the
 *	  specified name, class, preference, mail exchange and time to live.
 *
 * @param name The name for the resource record
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

/**
 * @brief The text of the resource record.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(OFData *) *textStrings;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFTXTDNSResourceRecord with the
 *	  specified name, class, text data and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param textStrings An array of text strings for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFTXTDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		 textStrings: (OFArray OF_GENERIC(OFData *) *)textStrings
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

#ifdef __cplusplus
extern "C" {
#endif






extern OFString *_Nonnull of_dns_class_to_string(of_dns_class_t DNSClass);







extern OFString *_Nonnull of_dns_record_type_to_string(
    of_dns_record_type_t recordType);






extern of_dns_class_t of_dns_class_parse(OFString *_Nonnull string);







extern of_dns_record_type_t of_dns_record_type_parse(OFString *_Nonnull string);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|
|













|







>
>
>
>
>
>
|
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
|
>
>
>
>
>
>
>
|





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

/**
 * @brief The text of the resource record.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(OFData *) *textStrings;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFTXTDNSResourceRecord with the
 *	  specified name, class, text data and time to live.
 *
 * @param name The name for the resource record
 * @param DNSClass The class code for the resource record
 * @param textStrings An array of text strings for the resource record
 * @param TTL The time to live for the resource record
 * @return An initialized OFTXTDNSResourceRecord
 */
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		 textStrings: (OFArray OF_GENERIC(OFData *) *)textStrings
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Returns the name for the specified OFDNSClass.
 *
 * @param DNSClass The OFDNSClass to return the name for
 * @return The name for the specified OFDNSClass
 */
extern OFString *_Nonnull OFDNSClassName(OFDNSClass DNSClass);

/**
 * @brief Returns the name for the specified OFDNSRecordType.
 *
 * @param recordType The OFDNSRecordType to return the name for
 * @return The name for the specified OFDNSRecordType
 */
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

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

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFArray.h"
#import "OFData.h"

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

OFString *
of_dns_class_to_string(of_dns_class_t DNSClass)
{
	switch (DNSClass) {
	case OF_DNS_CLASS_IN:
		return @"IN";
	case OF_DNS_CLASS_ANY:
		return @"any";
	default:
		return [OFString stringWithFormat: @"%u", DNSClass];
	}
}

OFString *
of_dns_record_type_to_string(of_dns_record_type_t recordType)
{
	switch (recordType) {
	case OF_DNS_RECORD_TYPE_A:
		return @"A";
	case OF_DNS_RECORD_TYPE_NS:
		return @"NS";
	case OF_DNS_RECORD_TYPE_CNAME:
		return @"CNAME";
	case OF_DNS_RECORD_TYPE_SOA:
		return @"SOA";
	case OF_DNS_RECORD_TYPE_PTR:
		return @"PTR";
	case OF_DNS_RECORD_TYPE_HINFO:
		return @"HINFO";
	case OF_DNS_RECORD_TYPE_MX:
		return @"MX";
	case OF_DNS_RECORD_TYPE_TXT:
		return @"TXT";
	case OF_DNS_RECORD_TYPE_RP:
		return @"RP";
	case OF_DNS_RECORD_TYPE_AAAA:
		return @"AAAA";
	case OF_DNS_RECORD_TYPE_SRV:
		return @"SRV";
	case OF_DNS_RECORD_TYPE_ALL:
		return @"all";
	default:
		return [OFString stringWithFormat: @"%u", recordType];
	}
}


of_dns_class_t of_dns_class_parse(OFString *string)
{
	void *pool = objc_autoreleasePoolPush();
	of_dns_class_t DNSClass;

	string = string.uppercaseString;

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

	objc_autoreleasePoolPop(pool);

	return DNSClass;
}


of_dns_record_type_t of_dns_record_type_parse(OFString *string)
{
	void *pool = objc_autoreleasePoolPush();
	of_dns_record_type_t recordType;

	string = string.uppercaseString;

	if ([string isEqual: @"A"])
		recordType = OF_DNS_RECORD_TYPE_A;
	else if ([string isEqual: @"NS"])
		recordType = OF_DNS_RECORD_TYPE_NS;
	else if ([string isEqual: @"CNAME"])
		recordType = OF_DNS_RECORD_TYPE_CNAME;
	else if ([string isEqual: @"SOA"])
		recordType = OF_DNS_RECORD_TYPE_SOA;
	else if ([string isEqual: @"PTR"])
		recordType = OF_DNS_RECORD_TYPE_PTR;
	else if ([string isEqual: @"HINFO"])
		recordType = OF_DNS_RECORD_TYPE_HINFO;
	else if ([string isEqual: @"MX"])
		recordType = OF_DNS_RECORD_TYPE_MX;
	else if ([string isEqual: @"TXT"])
		recordType = OF_DNS_RECORD_TYPE_TXT;
	else if ([string isEqual: @"RP"])
		recordType = OF_DNS_RECORD_TYPE_RP;
	else if ([string isEqual: @"AAAA"])
		recordType = OF_DNS_RECORD_TYPE_AAAA;
	else if ([string isEqual: @"SRV"])
		recordType = OF_DNS_RECORD_TYPE_SRV;
	else if ([string isEqual: @"ALL"])
		recordType = OF_DNS_RECORD_TYPE_ALL;
	else {
		@try {
			recordType = (of_dns_record_type_t)
			    [string unsignedLongLongValueWithBase: 0];
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidArgumentException exception];
		}
	}

	objc_autoreleasePoolPop(pool);

	return recordType;
}

@implementation OFDNSResourceRecord
@synthesize name = _name, DNSClass = _DNSClass, recordType = _recordType;
@synthesize TTL = _TTL;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	self = [super init];

	@try {
		_name = [name copy];
		_DNSClass = DNSClass;







|


|

|







|


|

|

|

|

|

|

|

|

|

|

|

|






>
|


|




|


|











>
|


|




|

|

|

|

|

|

|

|

|

|

|

|


|
















|
|







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
#import "OFArray.h"
#import "OFData.h"

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

OFString *
OFDNSClassName(OFDNSClass DNSClass)
{
	switch (DNSClass) {
	case OFDNSClassIN:
		return @"IN";
	case OFDNSClassAny:
		return @"any";
	default:
		return [OFString stringWithFormat: @"%u", DNSClass];
	}
}

OFString *
OFDNSRecordTypeName(OFDNSRecordType recordType)
{
	switch (recordType) {
	case OFDNSRecordTypeA:
		return @"A";
	case OFDNSRecordTypeNS:
		return @"NS";
	case OFDNSRecordTypeCNAME:
		return @"CNAME";
	case OFDNSRecordTypeSOA:
		return @"SOA";
	case OFDNSRecordTypePTR:
		return @"PTR";
	case OFDNSRecordTypeHINFO:
		return @"HINFO";
	case OFDNSRecordTypeMX:
		return @"MX";
	case OFDNSRecordTypeTXT:
		return @"TXT";
	case OFDNSRecordTypeRP:
		return @"RP";
	case OFDNSRecordTypeAAAA:
		return @"AAAA";
	case OFDNSRecordTypeSRV:
		return @"SRV";
	case OFDNSRecordTypeAll:
		return @"all";
	default:
		return [OFString stringWithFormat: @"%u", recordType];
	}
}

OFDNSClass
OFDNSClassParseName(OFString *string)
{
	void *pool = objc_autoreleasePoolPush();
	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;
}

OFDNSRecordType
OFDNSRecordTypeParseName(OFString *string)
{
	void *pool = objc_autoreleasePoolPush();
	OFDNSRecordType recordType;

	string = string.uppercaseString;

	if ([string isEqual: @"A"])
		recordType = OFDNSRecordTypeA;
	else if ([string isEqual: @"NS"])
		recordType = OFDNSRecordTypeNS;
	else if ([string isEqual: @"CNAME"])
		recordType = OFDNSRecordTypeCNAME;
	else if ([string isEqual: @"SOA"])
		recordType = OFDNSRecordTypeSOA;
	else if ([string isEqual: @"PTR"])
		recordType = OFDNSRecordTypePTR;
	else if ([string isEqual: @"HINFO"])
		recordType = OFDNSRecordTypeHINFO;
	else if ([string isEqual: @"MX"])
		recordType = OFDNSRecordTypeMX;
	else if ([string isEqual: @"TXT"])
		recordType = OFDNSRecordTypeTXT;
	else if ([string isEqual: @"RP"])
		recordType = OFDNSRecordTypeRP;
	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;
}

@implementation OFDNSResourceRecord
@synthesize name = _name, DNSClass = _DNSClass, recordType = _recordType;
@synthesize TTL = _TTL;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	self = [super init];

	@try {
		_name = [name copy];
		_DNSClass = DNSClass;
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
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tType = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass),
	    of_dns_record_type_to_string(_recordType), _TTL];
}
@end

@implementation OFADNSResourceRecord
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		     address: (const of_socket_address_t *)address
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: OF_DNS_CLASS_IN
			recordType: OF_DNS_RECORD_TYPE_A
			       TTL: TTL];

	_address = *address;

	return self;
}

- (const of_socket_address_t *)address
{
	return &_address;
}

- (bool)isEqual: (id)object
{
	OFADNSResourceRecord *record;







|
|





|
|






|



|
|







|







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
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tType = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass),
	    OFDNSRecordTypeName(_recordType), _TTL];
}
@end

@implementation OFADNSResourceRecord
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		     address: (const OFSocketAddress *)address
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: OFDNSClassIN
			recordType: OFDNSRecordTypeA
			       TTL: TTL];

	_address = *address;

	return self;
}

- (const OFSocketAddress *)address
{
	return &_address;
}

- (bool)isEqual: (id)object
{
	OFADNSResourceRecord *record;
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

	if (record->_DNSClass != _DNSClass)
		return false;

	if (record->_recordType != _recordType)
		return false;

	if (!of_socket_address_equal(&record->_address, &_address))
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, of_socket_address_hash(&_address));

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tAddress = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name,
	    of_socket_address_ip_string(&_address, NULL), _TTL];
}
@end

@implementation OFAAAADNSResourceRecord
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		     address: (const of_socket_address_t *)address
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: OF_DNS_CLASS_IN
			recordType: OF_DNS_RECORD_TYPE_AAAA
			       TTL: TTL];

	_address = *address;

	return self;
}

- (const of_socket_address_t *)address
{
	return &_address;
}

- (bool)isEqual: (id)object
{
	OFAAAADNSResourceRecord *record;







|







|

|

|
|
|
|
|
|

|












|
<





|
|






|



|
|







|







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

	if (record->_DNSClass != _DNSClass)
		return false;

	if (record->_recordType != _recordType)
		return false;

	if (!OFSocketAddressEqual(&record->_address, &_address))
		return false;

	return true;
}

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

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tAddress = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFSocketAddressString(&_address), _TTL];

}
@end

@implementation OFAAAADNSResourceRecord
- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		     address: (const OFSocketAddress *)address
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: OFDNSClassIN
			recordType: OFDNSRecordTypeAAAA
			       TTL: TTL];

	_address = *address;

	return self;
}

- (const OFSocketAddress *)address
{
	return &_address;
}

- (bool)isEqual: (id)object
{
	OFAAAADNSResourceRecord *record;
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

	if (record->_DNSClass != _DNSClass)
		return false;

	if (record->_recordType != _recordType)
		return false;

	if (!of_socket_address_equal(&record->_address, &_address))
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, of_socket_address_hash(&_address));

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tAddress = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name,
	    of_socket_address_ip_string(&_address, NULL), _TTL];
}
@end

@implementation OFCNAMEDNSResourceRecord
@synthesize alias = _alias;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		       alias: (OFString *)alias
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OF_DNS_RECORD_TYPE_CNAME
			       TTL: TTL];

	@try {
		_alias = [alias copy];
	} @catch (id e) {
		[self release];
		@throw e;







|







|

|

|
|
|
|
|
|

|












|
<







|
|






|





|







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

	if (record->_DNSClass != _DNSClass)
		return false;

	if (record->_recordType != _recordType)
		return false;

	if (!OFSocketAddressEqual(&record->_address, &_address))
		return false;

	return true;
}

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

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tAddress = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFSocketAddressString(&_address), _TTL];

}
@end

@implementation OFCNAMEDNSResourceRecord
@synthesize alias = _alias;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		       alias: (OFString *)alias
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OFDNSRecordTypeCNAME
			       TTL: TTL];

	@try {
		_alias = [alias copy];
	} @catch (id e) {
		[self release];
		@throw e;
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, _alias.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tAlias = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass), _alias,
	    _TTL];
}
@end

@implementation OFHINFODNSResourceRecord
@synthesize CPU = _CPU, OS = _OS;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
			 CPU: (OFString *)CPU
			  OS: (OFString *)OS
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OF_DNS_RECORD_TYPE_HINFO
			       TTL: TTL];

	@try {
		_CPU = [CPU copy];
		_OS = [OS copy];
	} @catch (id e) {
		[self release];







|

|

|
|
|
|
|
|

|













|
<







|
|






|






|







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
		return false;

	return true;
}

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

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tAlias = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass), _alias, _TTL];

}
@end

@implementation OFHINFODNSResourceRecord
@synthesize CPU = _CPU, OS = _OS;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
			 CPU: (OFString *)CPU
			  OS: (OFString *)OS
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OFDNSRecordTypeHINFO
			       TTL: TTL];

	@try {
		_CPU = [CPU copy];
		_OS = [OS copy];
	} @catch (id e) {
		[self release];
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, _CPU.hash);
	OF_HASH_ADD_HASH(hash, _OS.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tCPU = %@\n"
	    @"\tOS = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass), _CPU, _OS,
	    _TTL];
}
@end

@implementation OFMXDNSResourceRecord
@synthesize preference = _preference, mailExchange = _mailExchange;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  preference: (uint16_t)preference
		mailExchange: (OFString *)mailExchange
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OF_DNS_RECORD_TYPE_MX
			       TTL: TTL];

	@try {
		_preference = preference;
		_mailExchange = [mailExchange copy];
	} @catch (id e) {
		[self release];







|

|

|
|
|
|
|
|
|

|














|
<







|
|






|






|







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
		return false;

	return true;
}

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

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tCPU = %@\n"
	    @"\tOS = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass), _CPU, _OS, _TTL];

}
@end

@implementation OFMXDNSResourceRecord
@synthesize preference = _preference, mailExchange = _mailExchange;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  preference: (uint16_t)preference
		mailExchange: (OFString *)mailExchange
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OFDNSRecordTypeMX
			       TTL: TTL];

	@try {
		_preference = preference;
		_mailExchange = [mailExchange copy];
	} @catch (id e) {
		[self release];
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD(hash, _preference >> 8);
	OF_HASH_ADD(hash, _preference);
	OF_HASH_ADD_HASH(hash, _mailExchange.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tPreference = %" PRIu16 "\n"
	    @"\tMail Exchange = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass),
	    _preference, _mailExchange, _TTL];
}
@end

@implementation OFNSDNSResourceRecord
@synthesize authoritativeHost = _authoritativeHost;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
	   authoritativeHost: (OFString *)authoritativeHost
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OF_DNS_RECORD_TYPE_NS
			       TTL: TTL];

	@try {
		_authoritativeHost = [authoritativeHost copy];
	} @catch (id e) {
		[self release];
		@throw e;







|

|

|
|
|
|
|
|
|
|

|














|
|







|
|






|





|







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
		return false;

	return true;
}

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

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tPreference = %" PRIu16 "\n"
	    @"\tMail Exchange = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass), _preference,
	    _mailExchange, _TTL];
}
@end

@implementation OFNSDNSResourceRecord
@synthesize authoritativeHost = _authoritativeHost;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
	   authoritativeHost: (OFString *)authoritativeHost
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OFDNSRecordTypeNS
			       TTL: TTL];

	@try {
		_authoritativeHost = [authoritativeHost copy];
	} @catch (id e) {
		[self release];
		@throw e;
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, _authoritativeHost.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tAuthoritative Host = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass),
	    _authoritativeHost, _TTL];
}
@end

@implementation OFPTRDNSResourceRecord
@synthesize domainName = _domainName;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  domainName: (OFString *)domainName
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OF_DNS_RECORD_TYPE_PTR
			       TTL: TTL];

	@try {
		_domainName = [domainName copy];
	} @catch (id e) {
		[self release];
		@throw e;







|

|

|
|
|
|
|
|

|













|








|
|






|





|







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
		return false;

	return true;
}

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

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tAuthoritative Host = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass),
	    _authoritativeHost, _TTL];
}
@end

@implementation OFPTRDNSResourceRecord
@synthesize domainName = _domainName;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  domainName: (OFString *)domainName
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OFDNSRecordTypePTR
			       TTL: TTL];

	@try {
		_domainName = [domainName copy];
	} @catch (id e) {
		[self release];
		@throw e;
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, _domainName.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tDomain Name = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass),
	    _domainName, _TTL];
}
@end

@implementation OFRPDNSResourceRecord
@synthesize mailbox = _mailbox, TXTDomainName = _TXTDomainName;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		     mailbox: (OFString *)mailbox
	       TXTDomainName: (OFString *)TXTDomainName
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OF_DNS_RECORD_TYPE_RP
			       TTL: TTL];

	@try {
		_mailbox = [mailbox copy];
		_TXTDomainName = [TXTDomainName copy];
	} @catch (id e) {
		[self release];







|

|

|
|
|
|
|
|

|













|
|







|
|






|






|







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
		return false;

	return true;
}

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

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tDomain Name = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass), _domainName,
	    _TTL];
}
@end

@implementation OFRPDNSResourceRecord
@synthesize mailbox = _mailbox, TXTDomainName = _TXTDomainName;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		     mailbox: (OFString *)mailbox
	       TXTDomainName: (OFString *)TXTDomainName
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OFDNSRecordTypeRP
			       TTL: TTL];

	@try {
		_mailbox = [mailbox copy];
		_TXTDomainName = [TXTDomainName copy];
	} @catch (id e) {
		[self release];
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, _mailbox.hash);
	OF_HASH_ADD_HASH(hash, _TXTDomainName.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tMailbox = %@\n"
	    @"\tTXT Domain Name = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass), _mailbox,
	    _TXTDomainName, _TTL];
}
@end

@implementation OFSOADNSResourceRecord
@synthesize primaryNameServer = _primaryNameServer;
@synthesize responsiblePerson = _responsiblePerson;
@synthesize serialNumber = _serialNumber, refreshInterval = _refreshInterval;
@synthesize retryInterval = _retryInterval;
@synthesize expirationInterval = _expirationInterval, minTTL = _minTTL;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
	   primaryNameServer: (OFString *)primaryNameServer
	   responsiblePerson: (OFString *)responsiblePerson
		serialNumber: (uint32_t)serialNumber
	     refreshInterval: (uint32_t)refreshInterval
	       retryInterval: (uint32_t)retryInterval
	  expirationInterval: (uint32_t)expirationInterval
		      minTTL: (uint32_t)minTTL
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OF_DNS_RECORD_TYPE_SOA
			       TTL: TTL];

	@try {
		_primaryNameServer = [primaryNameServer copy];
		_responsiblePerson = [responsiblePerson copy];
		_serialNumber = serialNumber;
		_refreshInterval = refreshInterval;







|

|

|
|
|
|
|
|
|

|














|












|
|






|











|







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
		return false;

	return true;
}

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

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tMailbox = %@\n"
	    @"\tTXT Domain Name = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass), _mailbox,
	    _TXTDomainName, _TTL];
}
@end

@implementation OFSOADNSResourceRecord
@synthesize primaryNameServer = _primaryNameServer;
@synthesize responsiblePerson = _responsiblePerson;
@synthesize serialNumber = _serialNumber, refreshInterval = _refreshInterval;
@synthesize retryInterval = _retryInterval;
@synthesize expirationInterval = _expirationInterval, minTTL = _minTTL;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
	   primaryNameServer: (OFString *)primaryNameServer
	   responsiblePerson: (OFString *)responsiblePerson
		serialNumber: (uint32_t)serialNumber
	     refreshInterval: (uint32_t)refreshInterval
	       retryInterval: (uint32_t)retryInterval
	  expirationInterval: (uint32_t)expirationInterval
		      minTTL: (uint32_t)minTTL
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OFDNSRecordTypeSOA
			       TTL: TTL];

	@try {
		_primaryNameServer = [primaryNameServer copy];
		_responsiblePerson = [responsiblePerson copy];
		_serialNumber = serialNumber;
		_refreshInterval = refreshInterval;
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, _primaryNameServer.hash);
	OF_HASH_ADD_HASH(hash, _responsiblePerson.hash);
	OF_HASH_ADD(hash, _serialNumber >> 24);
	OF_HASH_ADD(hash, _serialNumber >> 16);
	OF_HASH_ADD(hash, _serialNumber >> 8);
	OF_HASH_ADD(hash, _serialNumber);
	OF_HASH_ADD(hash, _refreshInterval >> 24);
	OF_HASH_ADD(hash, _refreshInterval >> 16);
	OF_HASH_ADD(hash, _refreshInterval >> 8);
	OF_HASH_ADD(hash, _refreshInterval);
	OF_HASH_ADD(hash, _retryInterval >> 24);
	OF_HASH_ADD(hash, _retryInterval >> 16);
	OF_HASH_ADD(hash, _retryInterval >> 8);
	OF_HASH_ADD(hash, _retryInterval);
	OF_HASH_ADD(hash, _expirationInterval >> 24);
	OF_HASH_ADD(hash, _expirationInterval >> 16);
	OF_HASH_ADD(hash, _expirationInterval >> 8);
	OF_HASH_ADD(hash, _expirationInterval);
	OF_HASH_ADD(hash, _minTTL >> 24);
	OF_HASH_ADD(hash, _minTTL >> 16);
	OF_HASH_ADD(hash, _minTTL >> 8);
	OF_HASH_ADD(hash, _minTTL);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tPrimary Name Server = %@\n"
	    @"\tResponsible Person = %@\n"
	    @"\tSerial Number = %" PRIu32 "\n"
	    @"\tRefresh Interval = %" PRIu32 "\n"
	    @"\tRetry Interval = %" PRIu32 "\n"
	    @"\tExpiration Interval = %" PRIu32 "\n"
	    @"\tMinimum TTL = %" PRIu32 "\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass),
	    _primaryNameServer, _responsiblePerson, _serialNumber,
	    _refreshInterval, _retryInterval, _expirationInterval, _minTTL,
	    _TTL];
}
@end

@implementation OFSRVDNSResourceRecord
@synthesize priority = _priority, weight = _weight, target = _target;
@synthesize port = _port;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    priority: (uint16_t)priority
		      weight: (uint16_t)weight
		      target: (OFString *)target
			port: (uint16_t)port
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: OF_DNS_CLASS_IN
			recordType: OF_DNS_RECORD_TYPE_SRV
			       TTL: TTL];

	@try {
		_priority = priority;
		_weight = weight;
		_target = [target copy];
		_port = port;







|

|

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

|



















|











|
|













|
|







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
		return false;

	return true;
}

- (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
{
	return [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tPrimary Name Server = %@\n"
	    @"\tResponsible Person = %@\n"
	    @"\tSerial Number = %" PRIu32 "\n"
	    @"\tRefresh Interval = %" PRIu32 "\n"
	    @"\tRetry Interval = %" PRIu32 "\n"
	    @"\tExpiration Interval = %" PRIu32 "\n"
	    @"\tMinimum TTL = %" PRIu32 "\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass),
	    _primaryNameServer, _responsiblePerson, _serialNumber,
	    _refreshInterval, _retryInterval, _expirationInterval, _minTTL,
	    _TTL];
}
@end

@implementation OFSRVDNSResourceRecord
@synthesize priority = _priority, weight = _weight, target = _target;
@synthesize port = _port;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    priority: (uint16_t)priority
		      weight: (uint16_t)weight
		      target: (OFString *)target
			port: (uint16_t)port
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: OFDNSClassIN
			recordType: OFDNSRecordTypeSRV
			       TTL: TTL];

	@try {
		_priority = priority;
		_weight = weight;
		_target = [target copy];
		_port = port;
1212
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
1238
1239
1240
1241
1242
1243
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD(hash, _priority >> 8);
	OF_HASH_ADD(hash, _priority);
	OF_HASH_ADD(hash, _weight >> 8);
	OF_HASH_ADD(hash, _weight);
	OF_HASH_ADD_HASH(hash, _target.hash);
	OF_HASH_ADD(hash, _port >> 8);
	OF_HASH_ADD(hash, _port);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [OFString stringWithFormat:







|

|

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

|







1208
1209
1210
1211
1212
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
1238
1239
		return false;

	return true;
}

- (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
{
	return [OFString stringWithFormat:
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
}
@end

@implementation OFTXTDNSResourceRecord
@synthesize textStrings = _textStrings;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		  recordType: (of_dns_record_type_t)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (of_dns_class_t)DNSClass
		 textStrings: (OFArray OF_GENERIC(OFData *) *)textStrings
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OF_DNS_RECORD_TYPE_TXT
			       TTL: TTL];

	@try {
		_textStrings = [textStrings copy];
	} @catch (id e) {
		[self release];
		@throw e;







|
|






|





|







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
}
@end

@implementation OFTXTDNSResourceRecord
@synthesize textStrings = _textStrings;

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		  recordType: (OFDNSRecordType)recordType
			 TTL: (uint32_t)TTL
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
		    DNSClass: (OFDNSClass)DNSClass
		 textStrings: (OFArray OF_GENERIC(OFData *) *)textStrings
			 TTL: (uint32_t)TTL
{
	self = [super initWithName: name
			  DNSClass: DNSClass
			recordType: OFDNSRecordTypeTXT
			       TTL: TTL];

	@try {
		_textStrings = [textStrings copy];
	} @catch (id e) {
		[self release];
		@throw e;
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD(hash, _DNSClass >> 8);
	OF_HASH_ADD(hash, _DNSClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, _textStrings.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();







|

|

|
|
|
|
|
|

|







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
		return false;

	return true;
}

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

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
	ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tText strings = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass), text,
	    _TTL];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end







|
<








1369
1370
1371
1372
1373
1374
1375
1376

1377
1378
1379
1380
1381
1382
1383
1384
	ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tText strings = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, OFDNSClassName(_DNSClass), text, _TTL];


	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

Modified src/OFDNSResponse.h from [49ce9c1af3] to [12d5300b31].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);

typedef OFDictionary OF_GENERIC(OFString *, OFArray OF_GENERIC(
    OF_KINDOF(OFDNSResourceRecord *)) *) *of_dns_response_records_t;

/**
 * @class OFDNSResponse OFDNSResponse.h ObjFW/OFDNSResponse.h
 *
 * @brief A class storing a response from @ref OFDNSResolver.
 */
@interface OFDNSResponse: OFObject
{
	OFString *_domainName;
	of_dns_response_records_t _answerRecords;
	of_dns_response_records_t _authorityRecords;
	of_dns_response_records_t _additionalRecords;
	OF_RESERVE_IVARS(OFDNSResponse, 4)
}

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

/**
 * @brief The answer records of the response.
 *
 * This is a dictionary with the key being the domain name and the value being
 * an array of @ref OFDNSResourceRecord.
 */
@property (readonly, nonatomic) of_dns_response_records_t answerRecords;

/**
 * @brief The authority records of the response.
 *
 * This is a dictionary with the key being the domain name and the value being
 * an array of @ref OFDNSResourceRecord.
 */
@property (readonly, nonatomic) of_dns_response_records_t authorityRecords;

/**
 * @brief The additional records of the response.
 *
 * This is a dictionary with the key being the domain name and the value being
 * an array of @ref OFDNSResourceRecord.
 */
@property (readonly, nonatomic) of_dns_response_records_t additionalRecords;

/**
 * @brief Creates a new, autoreleased OFDNSResponse.
 *
 * @param domainName The domain name the response is for
 * @param answerRecords The answer records of the response
 * @param authorityRecords The authority records of the response
 * @param additionalRecords The additional records of the response
 * @return A new, autoreleased OFDNSResponse
 */
+ (instancetype)
    responseWithDomainName: (OFString *)domainName
	     answerRecords: (of_dns_response_records_t)answerRecords
	  authorityRecords: (of_dns_response_records_t)authorityRecords
	 additionalRecords: (of_dns_response_records_t)additionalRecords;

/**
 * @brief Initializes an already allocated OFDNSResponse.
 *
 * @param domainName The domain name the response is for
 * @param answerRecords The answer records of the response
 * @param authorityRecords The authority records of the response
 * @param additionalRecords The additional records of the response
 * @return An initialized OFDNSResponse
 */
- (instancetype)
    initWithDomainName: (OFString *)domainName
	 answerRecords: (of_dns_response_records_t)answerRecords
      authorityRecords: (of_dns_response_records_t)authorityRecords
     additionalRecords: (of_dns_response_records_t)additionalRecords
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END







|









<
|
|














|







|







|










<
|
|
|
|










<
|
|
|
|






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

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);

typedef OFDictionary OF_GENERIC(OFString *, OFArray OF_GENERIC(
    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;

/**
 * @brief The answer records of the response.
 *
 * This is a dictionary with the key being the domain name and the value being
 * an array of @ref OFDNSResourceRecord.
 */
@property (readonly, nonatomic) OFDNSResponseRecords answerRecords;

/**
 * @brief The authority records of the response.
 *
 * This is a dictionary with the key being the domain name and the value being
 * an array of @ref OFDNSResourceRecord.
 */
@property (readonly, nonatomic) OFDNSResponseRecords authorityRecords;

/**
 * @brief The additional records of the response.
 *
 * This is a dictionary with the key being the domain name and the value being
 * an array of @ref OFDNSResourceRecord.
 */
@property (readonly, nonatomic) OFDNSResponseRecords additionalRecords;

/**
 * @brief Creates a new, autoreleased OFDNSResponse.
 *
 * @param domainName The domain name the response is for
 * @param answerRecords The answer records of the response
 * @param authorityRecords The authority records of the response
 * @param additionalRecords The additional records of the response
 * @return A new, autoreleased OFDNSResponse
 */

+ (instancetype)responseWithDomainName: (OFString *)domainName
			 answerRecords: (OFDNSResponseRecords)answerRecords
		      authorityRecords: (OFDNSResponseRecords)authorityRecords
		     additionalRecords: (OFDNSResponseRecords)additionalRecords;

/**
 * @brief Initializes an already allocated OFDNSResponse.
 *
 * @param domainName The domain name the response is for
 * @param answerRecords The answer records of the response
 * @param authorityRecords The authority records of the response
 * @param additionalRecords The additional records of the response
 * @return An initialized OFDNSResponse
 */

- (instancetype)initWithDomainName: (OFString *)domainName
		     answerRecords: (OFDNSResponseRecords)answerRecords
		  authorityRecords: (OFDNSResponseRecords)authorityRecords
		 additionalRecords: (OFDNSResponseRecords)additionalRecords
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/OFDNSResponse.m from [f58f953bdc] to [6e61ece6f0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFString.h"

@implementation OFDNSResponse
@synthesize domainName = _domainName, answerRecords = _answerRecords;
@synthesize authorityRecords = _authorityRecords;
@synthesize additionalRecords = _additionalRecords;

+ (instancetype)
    responseWithDomainName: (OFString *)domainName
	     answerRecords: (of_dns_response_records_t)answerRecords
	  authorityRecords: (of_dns_response_records_t)authorityRecords
	 additionalRecords: (of_dns_response_records_t)additionalRecords
{
	return [[[self alloc]
	    initWithDomainName: domainName
		 answerRecords: answerRecords
	      authorityRecords: authorityRecords
	     additionalRecords: additionalRecords] autorelease];
}

- (instancetype)initWithDomainName: (OFString *)domainName
		     answerRecords: (of_dns_response_records_t)answerRecords
		  authorityRecords: (of_dns_response_records_t)authorityRecords
		 additionalRecords: (of_dns_response_records_t)additionalRecords
{
	self = [super init];

	@try {
		_domainName = [domainName copy];
		_answerRecords = [answerRecords copy];
		_authorityRecords = [authorityRecords copy];
		_additionalRecords = [additionalRecords copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init OF_UNAVAILABLE
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_domainName release];







<
|
|
|
|









|
|
|
















|







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
#import "OFString.h"

@implementation OFDNSResponse
@synthesize domainName = _domainName, answerRecords = _answerRecords;
@synthesize authorityRecords = _authorityRecords;
@synthesize additionalRecords = _additionalRecords;


+ (instancetype)responseWithDomainName: (OFString *)domainName
			 answerRecords: (OFDNSResponseRecords)answerRecords
		      authorityRecords: (OFDNSResponseRecords)authorityRecords
		     additionalRecords: (OFDNSResponseRecords)additionalRecords
{
	return [[[self alloc]
	    initWithDomainName: domainName
		 answerRecords: answerRecords
	      authorityRecords: authorityRecords
	     additionalRecords: additionalRecords] autorelease];
}

- (instancetype)initWithDomainName: (OFString *)domainName
		     answerRecords: (OFDNSResponseRecords)answerRecords
		  authorityRecords: (OFDNSResponseRecords)authorityRecords
		 additionalRecords: (OFDNSResponseRecords)additionalRecords
{
	self = [super init];

	@try {
		_domainName = [domainName copy];
		_answerRecords = [answerRecords copy];
		_authorityRecords = [authorityRecords copy];
		_additionalRecords = [additionalRecords copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_domainName release];
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);
	OF_HASH_ADD_HASH(hash, _domainName.hash);
	OF_HASH_ADD_HASH(hash, [_answerRecords hash]);
	OF_HASH_ADD_HASH(hash, [_authorityRecords hash]);
	OF_HASH_ADD_HASH(hash, [_additionalRecords hash]);
	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	OFString *answerRecords = [_answerRecords.description







|

|
|
|
|
|
|







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);
	OFHashAddHash(&hash, _domainName.hash);
	OFHashAddHash(&hash, [_answerRecords hash]);
	OFHashAddHash(&hash, [_authorityRecords hash]);
	OFHashAddHash(&hash, [_additionalRecords hash]);
	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)description
{
	OFString *answerRecords = [_answerRecords.description

Deleted src/OFData+ASN1DERParsing.h version [7ece1a8d4e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFData.h"
#import "OFASN1Value.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __cplusplus
extern "C" {
#endif
extern int _OFData_ASN1DERParsing_reference;
#ifdef __cplusplus
}
#endif

@interface OFData (ASN1DERParsing)
/**
 * @brief The data interpreted as ASN.1 in DER representation and parsed as an
 *	  object.
 *
 * This is either an OFArray (for a sequence), an OFSet (for a set) or an
 * OFASN1Value.
 */
@property (readonly, nonatomic) id objectByParsingASN1DER;

/**
 * @brief Parses the ASN.1 DER representation and returns it as an object.
 *
 * This is either an OFArray (for a sequence), an OFSet (for a set) or an
 * OFASN1Value.
 *
 * @param depthLimit The maximum depth the parser should accept (defaults to 32
 *		     if not specified, 0 means no limit (insecure!))
 * @return The ASN.1 DER representation as an object
 */
- (id)objectByParsingASN1DERWithDepthLimit: (size_t)depthLimit;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































































Deleted src/OFData+ASN1DERParsing.m version [31638813f3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFData+ASN1DERParsing.h"
#import "OFASN1BitString.h"
#import "OFASN1Boolean.h"
#import "OFASN1Enumerated.h"
#import "OFASN1IA5String.h"
#import "OFASN1Integer.h"
#import "OFASN1NumericString.h"
#import "OFASN1ObjectIdentifier.h"
#import "OFASN1OctetString.h"
#import "OFASN1PrintableString.h"
#import "OFASN1UTF8String.h"
#import "OFASN1Value.h"
#import "OFArray.h"
#import "OFNull.h"
#import "OFSet.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"

enum {
	ASN1_TAG_CONSTRUCTED_MASK = 0x20
};

int _OFData_ASN1DERParsing_reference;

static size_t parseObject(OFData *self, id *object, size_t depthLimit);

static OFArray *
parseSequence(OFData *contents, size_t depthLimit)
{
	OFMutableArray *ret = [OFMutableArray array];
	size_t count = contents.count;

	if (depthLimit == 0)
		@throw [OFOutOfRangeException exception];

	while (count > 0) {
		id object;
		size_t objectLength;

		objectLength = parseObject(contents, &object, depthLimit);

		count -= objectLength;
		contents = [contents subdataWithRange:
		    of_range(objectLength, count)];

		[ret addObject: object];
	}

	[ret makeImmutable];

	return ret;
}

static OFSet *
parseSet(OFData *contents, size_t depthLimit)
{
	OFMutableSet *ret = [OFMutableSet set];
	size_t count = contents.count;
	OFData *previousObjectData = nil;

	if (depthLimit == 0)
		@throw [OFOutOfRangeException exception];

	while (count > 0) {
		id object;
		size_t objectLength;
		OFData *objectData;

		objectLength = parseObject(contents, &object, depthLimit);
		objectData = [contents subdataWithRange:
		    of_range(0, objectLength)];

		if (previousObjectData != nil &&
		    [objectData compare: previousObjectData] !=
		    OF_ORDERED_DESCENDING)
			@throw [OFInvalidFormatException exception];

		count -= objectLength;
		contents = [contents subdataWithRange:
		    of_range(objectLength, count)];

		[ret addObject: object];

		previousObjectData = objectData;
	}

	[ret makeImmutable];

	return ret;
}

static size_t
parseObject(OFData *self, id *object, size_t depthLimit)
{
	const unsigned char *items = self.items;
	size_t count = self.count;
	unsigned char tag;
	size_t contentsLength, bytesConsumed = 0;
	Class valueClass;
	OFData *contents;

	if (count < 2)
		@throw [OFTruncatedDataException exception];

	tag = *items++;
	contentsLength = *items++;
	bytesConsumed += 2;

	if (contentsLength > 127) {
		uint_fast8_t lengthLength = contentsLength & 0x7F;

		if (lengthLength > sizeof(size_t))
			@throw [OFOutOfRangeException exception];

		if (count - bytesConsumed < lengthLength)
			@throw [OFTruncatedDataException exception];

		if (lengthLength == 0 ||
		    (lengthLength == 1 && items[0] < 0x80) ||
		    (lengthLength >= 2 && items[0] == 0))
			@throw [OFInvalidFormatException exception];

		contentsLength = 0;

		for (uint_fast8_t i = 0; i < lengthLength; i++)
			contentsLength = (contentsLength << 8) | *items++;

		bytesConsumed += lengthLength;

		if (contentsLength <= 127)
			@throw [OFInvalidFormatException exception];
	}

	if (count - bytesConsumed < contentsLength)
		@throw [OFTruncatedDataException exception];

	contents = [self subdataWithRange:
	    of_range(bytesConsumed, contentsLength)];
	bytesConsumed += contentsLength;

	switch (tag & ~ASN1_TAG_CONSTRUCTED_MASK) {
	case OF_ASN1_TAG_NUMBER_BOOLEAN:
		valueClass = [OFASN1Boolean class];
		break;
	case OF_ASN1_TAG_NUMBER_INTEGER:
		valueClass = [OFASN1Integer class];
		break;
	case OF_ASN1_TAG_NUMBER_BIT_STRING:
		valueClass = [OFASN1BitString class];
		break;
	case OF_ASN1_TAG_NUMBER_OCTET_STRING:
		valueClass = [OFASN1OctetString class];
		break;
	case OF_ASN1_TAG_NUMBER_NULL:
		if (tag & ASN1_TAG_CONSTRUCTED_MASK)
			@throw [OFInvalidFormatException exception];

		if (contents.count != 0)
			@throw [OFInvalidFormatException exception];

		*object = [OFNull null];
		return bytesConsumed;
	case OF_ASN1_TAG_NUMBER_OBJECT_IDENTIFIER:
		valueClass = [OFASN1ObjectIdentifier class];
		break;
	case OF_ASN1_TAG_NUMBER_ENUMERATED:
		valueClass = [OFASN1Enumerated class];
		break;
	case OF_ASN1_TAG_NUMBER_UTF8_STRING:
		valueClass = [OFASN1UTF8String class];
		break;
	case OF_ASN1_TAG_NUMBER_SEQUENCE:
		if (!(tag & ASN1_TAG_CONSTRUCTED_MASK))
			@throw [OFInvalidFormatException exception];

		*object = parseSequence(contents, depthLimit - 1);
		return bytesConsumed;
	case OF_ASN1_TAG_NUMBER_SET:
		if (!(tag & ASN1_TAG_CONSTRUCTED_MASK))
			@throw [OFInvalidFormatException exception];

		*object = parseSet(contents, depthLimit - 1);
		return bytesConsumed;
	case OF_ASN1_TAG_NUMBER_NUMERIC_STRING:
		valueClass = [OFASN1NumericString class];
		break;
	case OF_ASN1_TAG_NUMBER_PRINTABLE_STRING:
		valueClass = [OFASN1PrintableString class];
		break;
	case OF_ASN1_TAG_NUMBER_IA5_STRING:
		valueClass = [OFASN1IA5String class];
		break;
	default:
		valueClass = [OFASN1Value class];
		break;
	}

	*object = [[[valueClass alloc]
	      initWithTagClass: tag >> 6
		     tagNumber: tag & 0x1F
		   constructed: tag & ASN1_TAG_CONSTRUCTED_MASK
	    DEREncodedContents: contents] autorelease];
	return bytesConsumed;
}

@implementation OFData (ASN1DERParsing)
- (id)objectByParsingASN1DER
{
	return [self objectByParsingASN1DERWithDepthLimit: 32];
}

- (id)objectByParsingASN1DERWithDepthLimit: (size_t)depthLimit
{
	void *pool = objc_autoreleasePoolPush();
	id object;

	if (self.itemSize != 1)
		@throw [OFInvalidArgumentException exception];

	if (parseObject(self, &object, depthLimit) != self.count)
		@throw [OFInvalidFormatException exception];

	[object retain];

	objc_autoreleasePoolPop(pool);

	return [object autorelease];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































































































































































































































Renamed and modified src/OFData+CryptoHashing.h [c36da9d392] to src/OFData+CryptographicHashing.h [7264dfad2c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
OF_ASSUME_NONNULL_BEGIN

@class OFString;

#ifdef __cplusplus
extern "C" {
#endif
extern int _OFData_CryptoHashing_reference;
#ifdef __cplusplus
}
#endif

@interface OFData (CryptoHashing)
/**
 * @brief The MD5 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *MD5Hash;

/**
 * @brief The RIPEMD-160 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *RIPEMD160Hash;

/**
 * @brief The SHA-1 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *SHA1Hash;

/**
 * @brief The SHA-224 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *SHA224Hash;

/**
 * @brief The SHA-256 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *SHA256Hash;

/**
 * @brief The SHA-384 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *SHA384Hash;

/**
 * @brief The SHA-512 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *SHA512Hash;
@end

OF_ASSUME_NONNULL_END







|




|



|




|




|




|




|




|




|



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
OF_ASSUME_NONNULL_BEGIN

@class OFString;

#ifdef __cplusplus
extern "C" {
#endif
extern int _OFData_CryptographicHashing_reference;
#ifdef __cplusplus
}
#endif

@interface OFData (CryptographicHashing)
/**
 * @brief The MD5 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *stringByMD5Hashing;

/**
 * @brief The RIPEMD-160 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *stringByRIPEMD160Hashing;

/**
 * @brief The SHA-1 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA1Hashing;

/**
 * @brief The SHA-224 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA224Hashing;

/**
 * @brief The SHA-256 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA256Hashing;

/**
 * @brief The SHA-384 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA384Hashing;

/**
 * @brief The SHA-512 hash of the data as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA512Hashing;
@end

OF_ASSUME_NONNULL_END

Renamed and modified src/OFData+CryptoHashing.m [7fbcd74f01] to src/OFData+CryptographicHashing.m [caf70aabc2].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFData+CryptoHashing.h"
#import "OFString.h"
#import "OFCryptoHash.h"
#import "OFMD5Hash.h"
#import "OFRIPEMD160Hash.h"
#import "OFSHA1Hash.h"
#import "OFSHA224Hash.h"
#import "OFSHA256Hash.h"
#import "OFSHA384Hash.h"
#import "OFSHA512Hash.h"

int _OFData_CryptoHashing_reference;

@implementation OFData (CryptoHashing)

- (OFString *)of_cryptoHashWithClass: (Class <OFCryptoHash>)class OF_DIRECT
{
	void *pool = objc_autoreleasePoolPush();
	id <OFCryptoHash> hash =
	    [class cryptoHashWithAllowsSwappableMemory: true];
	size_t digestSize = [class digestSize];
	const unsigned char *digest;
	char cString[digestSize * 2];

	[hash updateWithBuffer: _items
			length: _count * _itemSize];

	digest = hash.digest;

	for (size_t i = 0; i < digestSize; i++) {
		uint8_t high, low;

		high = digest[i] >> 4;
		low  = digest[i] & 0x0F;

		cString[i * 2] = (high > 9 ? high - 10 + 'a' : high + '0');
		cString[i * 2 + 1] = (low > 9 ? low - 10 + 'a' : low + '0');
	}

	objc_autoreleasePoolPop(pool);

	return [OFString stringWithCString: cString
				  encoding: OF_STRING_ENCODING_ASCII
				    length: digestSize * 2];
}

- (OFString *)MD5Hash
{
	return [self of_cryptoHashWithClass: [OFMD5Hash class]];
}

- (OFString *)RIPEMD160Hash
{
	return [self of_cryptoHashWithClass: [OFRIPEMD160Hash class]];
}

- (OFString *)SHA1Hash
{
	return [self of_cryptoHashWithClass: [OFSHA1Hash class]];
}

- (OFString *)SHA224Hash
{
	return [self of_cryptoHashWithClass: [OFSHA224Hash class]];
}

- (OFString *)SHA256Hash
{
	return [self of_cryptoHashWithClass: [OFSHA256Hash class]];
}

- (OFString *)SHA384Hash
{
	return [self of_cryptoHashWithClass: [OFSHA384Hash class]];
}

- (OFString *)SHA512Hash
{
	return [self of_cryptoHashWithClass: [OFSHA512Hash class]];
}
@end

<
<
|















|

|








|

|
>
|


|
|




|
|
>















|



|

|


|

|


|

|


|

|


|

|


|

|


|

|


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


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

#import "OFData+CryptographicHashing.h"
#import "OFString.h"
#import "OFCryptographicHash.h"
#import "OFMD5Hash.h"
#import "OFRIPEMD160Hash.h"
#import "OFSHA1Hash.h"
#import "OFSHA224Hash.h"
#import "OFSHA256Hash.h"
#import "OFSHA384Hash.h"
#import "OFSHA512Hash.h"

int _OFData_CryptographicHashing_reference;

@implementation OFData (CryptographicHashing)
static OFString *
stringByHashing(Class <OFCryptographicHash> class, OFData *self)
{
	void *pool = objc_autoreleasePoolPush();
	id <OFCryptographicHash> hash =
	    [class hashWithAllowsSwappableMemory: true];
	size_t digestSize = [class digestSize];
	const unsigned char *digest;
	char cString[digestSize * 2];

	[hash updateWithBuffer: self->_items
			length: self->_count * self->_itemSize];
	[hash calculate];
	digest = hash.digest;

	for (size_t i = 0; i < digestSize; i++) {
		uint8_t high, low;

		high = digest[i] >> 4;
		low  = digest[i] & 0x0F;

		cString[i * 2] = (high > 9 ? high - 10 + 'a' : high + '0');
		cString[i * 2 + 1] = (low > 9 ? low - 10 + 'a' : low + '0');
	}

	objc_autoreleasePoolPop(pool);

	return [OFString stringWithCString: cString
				  encoding: OFStringEncodingASCII
				    length: digestSize * 2];
}

- (OFString *)stringByMD5Hashing
{
	return stringByHashing([OFMD5Hash class], self);
}

- (OFString *)stringByRIPEMD160Hashing
{
	return stringByHashing([OFRIPEMD160Hash class], self);
}

- (OFString *)stringBySHA1Hashing
{
	return stringByHashing([OFSHA1Hash class], self);
}

- (OFString *)stringBySHA224Hashing
{
	return stringByHashing([OFSHA224Hash class], self);
}

- (OFString *)stringBySHA256Hashing
{
	return stringByHashing([OFSHA256Hash class], self);
}

- (OFString *)stringBySHA384Hashing
{
	return stringByHashing([OFSHA384Hash class], self);
}

- (OFString *)stringBySHA512Hashing
{
	return stringByHashing([OFSHA512Hash class], self);
}
@end

Modified src/OFData+MessagePackParsing.h from [174add13df] to [38e581e80e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFData+MessagePackParsing.m from [464ca7dfaa] to [8bd7ef8227].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
		pool = objc_autoreleasePoolPush();

		pos += parseObject(buffer + pos, length - pos, &key,
		    depthLimit);
		pos += parseObject(buffer + pos, length - pos, &value,
		    depthLimit);

		[*object setObject: value
			    forKey: key];

		objc_autoreleasePoolPop(pool);
	}

	return pos;
}

static OFDate *
createDate(OFData *data)
{
	switch (data.count) {
	case 4: {
		uint32_t timestamp;

		memcpy(&timestamp, data.items, 4);
		timestamp = OF_BSWAP32_IF_LE(timestamp);

		return [OFDate dateWithTimeIntervalSince1970: timestamp];
	}
	case 8: {
		uint64_t combined;

		memcpy(&combined, data.items, 8);
		combined = OF_BSWAP64_IF_LE(combined);

		return [OFDate dateWithTimeIntervalSince1970:
		    (double)(combined & 0x3FFFFFFFF) +
		    (double)(combined >> 34) / 1000000000];
	}
	case 12: {
		uint32_t nanoseconds;
		int64_t seconds;

		memcpy(&nanoseconds, data.items, 4);
		memcpy(&seconds, (char *)data.items + 4, 8);

		nanoseconds = OF_BSWAP32_IF_LE(nanoseconds);
		seconds = OF_BSWAP64_IF_LE(seconds);

		return [OFDate dateWithTimeIntervalSince1970:
		    (double)seconds + (double)nanoseconds / 1000000000];
	}
	default:
		@throw [OFInvalidFormatException exception];
	}







|
<















|







|












|
|







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
		pool = objc_autoreleasePoolPush();

		pos += parseObject(buffer + pos, length - pos, &key,
		    depthLimit);
		pos += parseObject(buffer + pos, length - pos, &value,
		    depthLimit);

		[*object setObject: value forKey: key];


		objc_autoreleasePoolPop(pool);
	}

	return pos;
}

static OFDate *
createDate(OFData *data)
{
	switch (data.count) {
	case 4: {
		uint32_t timestamp;

		memcpy(&timestamp, data.items, 4);
		timestamp = OFFromBigEndian32(timestamp);

		return [OFDate dateWithTimeIntervalSince1970: timestamp];
	}
	case 8: {
		uint64_t combined;

		memcpy(&combined, data.items, 8);
		combined = OFFromBigEndian64(combined);

		return [OFDate dateWithTimeIntervalSince1970:
		    (double)(combined & 0x3FFFFFFFF) +
		    (double)(combined >> 34) / 1000000000];
	}
	case 12: {
		uint32_t nanoseconds;
		int64_t seconds;

		memcpy(&nanoseconds, data.items, 4);
		memcpy(&seconds, (char *)data.items + 4, 8);

		nanoseconds = OFFromBigEndian32(nanoseconds);
		seconds = OFFromBigEndian64(seconds);

		return [OFDate dateWithTimeIntervalSince1970:
		    (double)seconds + (double)nanoseconds / 1000000000];
	}
	default:
		@throw [OFInvalidFormatException exception];
	}
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
		float f;

		if (length < 5)
			@throw [OFTruncatedDataException exception];

		memcpy(&f, buffer + 1, 4);

		*object = [OFNumber numberWithFloat: OF_BSWAP_FLOAT_IF_LE(f)];
		return 5;
	case 0xCB:; /* float 64 */
		double d;

		if (length < 9)
			@throw [OFTruncatedDataException exception];

		memcpy(&d, buffer + 1, 8);

		*object = [OFNumber numberWithDouble: OF_BSWAP_DOUBLE_IF_LE(d)];
		return 9;
	/* nil */
	case 0xC0:
		*object = [OFNull null];
		return 1;
	/* false */
	case 0xC2:







|









|







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
		float f;

		if (length < 5)
			@throw [OFTruncatedDataException exception];

		memcpy(&f, buffer + 1, 4);

		*object = [OFNumber numberWithFloat: OFFromBigEndianFloat(f)];
		return 5;
	case 0xCB:; /* float 64 */
		double d;

		if (length < 9)
			@throw [OFTruncatedDataException exception];

		memcpy(&d, buffer + 1, 8);

		*object = [OFNumber numberWithDouble: OFFromBigEndianDouble(d)];
		return 9;
	/* nil */
	case 0xC0:
		*object = [OFNull null];
		return 1;
	/* false */
	case 0xC2:
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
			@throw [OFTruncatedDataException exception];

		count = buffer[1];

		if (length < count + 2)
			@throw [OFTruncatedDataException exception];

		*object = [OFData dataWithItems: buffer + 2
					  count: count];

		return count + 2;
	case 0xC5: /* bin 16 */
		if (length < 3)
			@throw [OFTruncatedDataException exception];

		count = readUInt16(buffer + 1);

		if (length < count + 3)
			@throw [OFTruncatedDataException exception];

		*object = [OFData dataWithItems: buffer + 3
					  count: count];

		return count + 3;
	case 0xC6: /* bin 32 */
		if (length < 5)
			@throw [OFTruncatedDataException exception];

		count = readUInt32(buffer + 1);

		if (length < count + 5)
			@throw [OFTruncatedDataException exception];

		*object = [OFData dataWithItems: buffer + 5
					  count: count];

		return count + 5;
	/* Extensions */
	case 0xC7: /* ext 8 */
		if (length < 3)
			@throw [OFTruncatedDataException exception];

		count = buffer[1];

		if (length < count + 3)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 3
					       count: count];
		@try {
			*object = createExtension(buffer[2], data);
		} @finally {
			[data release];
		}

		return count + 3;
	case 0xC8: /* ext 16 */
		if (length < 4)
			@throw [OFTruncatedDataException exception];

		count = readUInt16(buffer + 1);

		if (length < count + 4)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 4
					       count: count];
		@try {
			*object = createExtension(buffer[3], data);
		} @finally {
			[data release];
		}

		return count + 4;
	case 0xC9: /* ext 32 */
		if (length < 6)
			@throw [OFTruncatedDataException exception];

		count = readUInt32(buffer + 1);

		if (length < count + 6)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 6
					       count: count];
		@try {
			*object = createExtension(buffer[5], data);
		} @finally {
			[data release];
		}

		return count + 6;
	case 0xD4: /* fixext 1 */
		if (length < 3)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2
					       count: 1];
		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 3;
	case 0xD5: /* fixext 2 */
		if (length < 4)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2
					       count: 2];
		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 4;
	case 0xD6: /* fixext 4 */
		if (length < 6)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2
					       count: 4];
		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 6;
	case 0xD7: /* fixext 8 */
		if (length < 10)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2
					       count: 8];
		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 10;
	case 0xD8: /* fixext 16 */
		if (length < 18)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2
					       count: 16];
		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 18;







|
<











|
<











|
<












|
<
















|
<
















|
<











|
<











|
<











|
<











|
<











|
<







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
			@throw [OFTruncatedDataException exception];

		count = buffer[1];

		if (length < count + 2)
			@throw [OFTruncatedDataException exception];

		*object = [OFData dataWithItems: buffer + 2 count: count];


		return count + 2;
	case 0xC5: /* bin 16 */
		if (length < 3)
			@throw [OFTruncatedDataException exception];

		count = readUInt16(buffer + 1);

		if (length < count + 3)
			@throw [OFTruncatedDataException exception];

		*object = [OFData dataWithItems: buffer + 3 count: count];


		return count + 3;
	case 0xC6: /* bin 32 */
		if (length < 5)
			@throw [OFTruncatedDataException exception];

		count = readUInt32(buffer + 1);

		if (length < count + 5)
			@throw [OFTruncatedDataException exception];

		*object = [OFData dataWithItems: buffer + 5 count: count];


		return count + 5;
	/* Extensions */
	case 0xC7: /* ext 8 */
		if (length < 3)
			@throw [OFTruncatedDataException exception];

		count = buffer[1];

		if (length < count + 3)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 3 count: count];

		@try {
			*object = createExtension(buffer[2], data);
		} @finally {
			[data release];
		}

		return count + 3;
	case 0xC8: /* ext 16 */
		if (length < 4)
			@throw [OFTruncatedDataException exception];

		count = readUInt16(buffer + 1);

		if (length < count + 4)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 4 count: count];

		@try {
			*object = createExtension(buffer[3], data);
		} @finally {
			[data release];
		}

		return count + 4;
	case 0xC9: /* ext 32 */
		if (length < 6)
			@throw [OFTruncatedDataException exception];

		count = readUInt32(buffer + 1);

		if (length < count + 6)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 6 count: count];

		@try {
			*object = createExtension(buffer[5], data);
		} @finally {
			[data release];
		}

		return count + 6;
	case 0xD4: /* fixext 1 */
		if (length < 3)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2 count: 1];

		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 3;
	case 0xD5: /* fixext 2 */
		if (length < 4)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2 count: 2];

		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 4;
	case 0xD6: /* fixext 4 */
		if (length < 6)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2 count: 4];

		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 6;
	case 0xD7: /* fixext 8 */
		if (length < 10)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2 count: 8];

		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 10;
	case 0xD8: /* fixext 16 */
		if (length < 18)
			@throw [OFTruncatedDataException exception];

		data = [[OFData alloc] initWithItems: buffer + 2 count: 16];

		@try {
			*object = createExtension(buffer[1], data);
		} @finally {
			[data release];
		}

		return 18;

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

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFSerialization.h"
#import "OFMessagePackRepresentation.h"



OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFURL;






enum {

	OF_DATA_SEARCH_BACKWARDS = 1
};


/**
 * @class OFData OFData.h ObjFW/OFData.h
 *
 * @brief A class for storing arbitrary data in an array.
 *
 * For security reasons, serialization and deserialization is only implemented
 * for OFData with item size 1.
 */
@interface OFData: OFObject <OFCopying, OFMutableCopying, OFComparing,
    OFSerialization, OFMessagePackRepresentation>
{
	unsigned char *_items;
	size_t _count, _itemSize;
	bool _freeWhenDone;
@private
	OFData *_parentData;
	OF_RESERVE_IVARS(OFData, 4)
}

/**
 * @brief The size of a single item in the OFData in bytes.
 */
@property (readonly, nonatomic) size_t itemSize;

/**
 * @brief The number of items in the OFData.
 */
@property (readonly, nonatomic) size_t count;

/**
 * @brief All elements of the OFData as a C array.
 *
 * @warning The pointer is only valid until the OFData is changed!
 */
@property (readonly, nonatomic) const void *items OF_RETURNS_INNER_POINTER;


/**
 * @brief The first item of the OFData or `NULL`.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) const void *firstItem
    OF_RETURNS_INNER_POINTER;


<
<
|
















>
>






>
>
>
>
>
|
>
|
<
>












|



|


















|
>







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


 * 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 "OFSerialization.h"
#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 {
	/** Search backwards in the data */
	OFDataSearchBackwards = 1

} OFDataSearchOptions;

/**
 * @class OFData OFData.h ObjFW/OFData.h
 *
 * @brief A class for storing arbitrary data in an array.
 *
 * For security reasons, serialization and deserialization is only implemented
 * for OFData with item size 1.
 */
@interface OFData: OFObject <OFCopying, OFMutableCopying, OFComparing,
    OFSerialization, OFMessagePackRepresentation>
{
	unsigned char *_Nullable _items;
	size_t _count, _itemSize;
	bool _freeWhenDone;
@private
	OFData *_Nullable _parentData;
	OF_RESERVE_IVARS(OFData, 4)
}

/**
 * @brief The size of a single item in the OFData in bytes.
 */
@property (readonly, nonatomic) size_t itemSize;

/**
 * @brief The number of items in the OFData.
 */
@property (readonly, nonatomic) size_t count;

/**
 * @brief All elements of the OFData as a C array.
 *
 * @warning The pointer is only valid until the OFData is changed!
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) const void *items
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The first item of the OFData or `NULL`.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) const void *firstItem
    OF_RETURNS_INNER_POINTER;

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
/**
 * @brief Creates a new OFData with the specified `count` items of size 1.
 *
 * @param items The items to store in the OFData
 * @param count The number of items
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithItems: (const void *)items
			count: (size_t)count;

/**
 * @brief Creates a new OFData with the specified `count` items of the
 *	  specified size.
 *
 * @param items The items to store in the OFData
 * @param itemSize The item size of a single item in bytes
 * @param count The number of items

 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithItems: (const void *)items
		     itemSize: (size_t)itemSize
			count: (size_t)count;


/**
 * @brief Creates a new OFData with the specified `count` items of size 1 by
 *	  taking over ownership of the specified items pointer.



 *
 * @param items The items to store in the OFData
 * @param count The number of items
 * @param freeWhenDone Whether to free the pointer when it is no longer needed
 *		       by the OFData
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone;

/**
 * @brief Creates a new OFData with the specified `count` items of the
 *	  specified size by taking ownership of the specified items pointer.
 *



 * @param items The items to store in the OFData
 * @param itemSize The item size of a single item in bytes
 * @param count The number of items

 * @param freeWhenDone Whether to free the pointer when it is no longer needed
 *		       by the OFData
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone;

#ifdef OF_HAVE_FILES
/**
 * @brief Creates a new OFData with an item size of 1, containing the data of
 *	  the specified file.
 *







|
<






<

>



<
|
>




>
>
>















>
>
>

<

>





<

>







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
/**
 * @brief Creates a new OFData with the specified `count` items of size 1.
 *
 * @param items The items to store in the OFData
 * @param count The number of items
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithItems: (const void *)items count: (size_t)count;


/**
 * @brief Creates a new OFData with the specified `count` items of the
 *	  specified size.
 *
 * @param items The items to store in the OFData

 * @param count The number of items
 * @param itemSize The item size of a single item in bytes
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithItems: (const void *)items

			count: (size_t)count
		     itemSize: (size_t)itemSize;

/**
 * @brief Creates a new OFData with the specified `count` items of size 1 by
 *	  taking over ownership of the specified items pointer.
 *
 * If initialization fails for whatever reason, the passed memory is *not*
 * free'd if `freeWhenDone` is true.
 *
 * @param items The items to store in the OFData
 * @param count The number of items
 * @param freeWhenDone Whether to free the pointer when it is no longer needed
 *		       by the OFData
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone;

/**
 * @brief Creates a new OFData with the specified `count` items of the
 *	  specified size by taking ownership of the specified items pointer.
 *
 * If initialization fails for whatever reason, the passed memory is *not*
 * free'd if `freeWhenDone` is true.
 *
 * @param items The items to store in the OFData

 * @param count The number of items
 * @param itemSize The item size of a single item in bytes
 * @param freeWhenDone Whether to free the pointer when it is no longer needed
 *		       by the OFData
 * @return A new autoreleased OFData
 */
+ (instancetype)dataWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone;

#ifdef OF_HAVE_FILES
/**
 * @brief Creates a new OFData with an item size of 1, containing the data of
 *	  the specified file.
 *
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
 * @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 string representation.
 *
 * @param string The 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 Initialized an already allocated OFData with the specified `count`
 *	  items of size 1.
 *
 * @param items The items to store in the OFData
 * @param count The number of items
 * @return An initialized OFData
 */
- (instancetype)initWithItems: (const void *)items
			count: (size_t)count;

/**
 * @brief Initialized an already allocated OFData with the specified `count`
 *	  items of the specified size.
 *
 * @param items The items to store in the OFData
 * @param itemSize The item size of a single item in bytes
 * @param count The number of items

 * @return An initialized OFData
 */
- (instancetype)initWithItems: (const void *)items
		     itemSize: (size_t)itemSize
			count: (size_t)count;


/**
 * @brief Initializes an already allocated OFData with the specified `count`
 *	  items of size 1 by taking over ownership of the specified items
 *	  pointer.



 *
 * @param items The items to store in the OFData
 * @param count The number of items
 * @param freeWhenDone Whether to free the pointer when it is no longer needed
 *		       by the OFData
 * @return An initialized OFData
 */
- (instancetype)initWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone;

/**
 * @brief Initializes an already allocated OFData with the specified `count`
 *	  items of the specified size by taking ownership of the specified
 *	  items pointer.
 *



 * @param items The items to store in the OFData
 * @param itemSize The item size of a single item in bytes
 * @param count The number of items

 * @param freeWhenDone Whether to free the pointer when it is no longer needed
 *		       by the OFData
 * @return An initialized OFData
 */
- (instancetype)initWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone;

#ifdef OF_HAVE_FILES
/**
 * @brief Initializes an already allocated OFData with an item size of 1,
 *	  containing the data of the specified file.
 *







|

|














|






|
<


|



<

>



<
|
>





>
>
>
















>
>
>

<

>





<

>







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
 * @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.
 *
 * @param items The items to store in the OFData
 * @param count The number of items
 * @return An initialized OFData
 */
- (instancetype)initWithItems: (const void *)items count: (size_t)count;


/**
 * @brief Initializes an already allocated OFData with the specified `count`
 *	  items of the specified size.
 *
 * @param items The items to store in the OFData

 * @param count The number of items
 * @param itemSize The item size of a single item in bytes
 * @return An initialized OFData
 */
- (instancetype)initWithItems: (const void *)items

			count: (size_t)count
		     itemSize: (size_t)itemSize;

/**
 * @brief Initializes an already allocated OFData with the specified `count`
 *	  items of size 1 by taking over ownership of the specified items
 *	  pointer.
 *
 * If initialization fails for whatever reason, the passed memory is *not*
 * free'd if `freeWhenDone` is true.
 *
 * @param items The items to store in the OFData
 * @param count The number of items
 * @param freeWhenDone Whether to free the pointer when it is no longer needed
 *		       by the OFData
 * @return An initialized OFData
 */
- (instancetype)initWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone;

/**
 * @brief Initializes an already allocated OFData with the specified `count`
 *	  items of the specified size by taking ownership of the specified
 *	  items pointer.
 *
 * If initialization fails for whatever reason, the passed memory is *not*
 * free'd if `freeWhenDone` is true.
 *
 * @param items The items to store in the OFData

 * @param count The number of items
 * @param itemSize The item size of a single item in bytes
 * @param freeWhenDone Whether to free the pointer when it is no longer needed
 *		       by the OFData
 * @return An initialized OFData
 */
- (instancetype)initWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone;

#ifdef OF_HAVE_FILES
/**
 * @brief Initializes an already allocated OFData with an item size of 1,
 *	  containing the data of the specified file.
 *
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
 * @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 string representation.
 *
 * @param string The 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 Returns a specific item of the OFData.
 *
 * @param index The number of the item to return
 * @return The specified item of the OFData
 */
- (const void *)itemAtIndex: (size_t)index OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the data in the specified range as a new OFData.
 *
 * @param range The range of the data for the new OFData
 * @return The data in the specified range as a new OFData
 */
- (OFData *)subdataWithRange: (of_range_t)range;

/**
 * @brief Returns the range of the data.
 *
 * @param data The data to search for
 * @param options Options modifying search behavior.@n
 *		  Possible values are:
 *		  Value                      | Description
 *		  ---------------------------|-----------------------------
 *		  `OF_DATA_SEARCH_BACKWARDS` | Search backwards in the data
 * @param range The range in which to search
 * @return The range of the first occurrence of the data or a range with
 *	   `OF_NOT_FOUND` as start position if it was not found.
 */
- (of_range_t)rangeOfData: (OFData *)data
		  options: (int)options
		    range: (of_range_t)range;

#ifdef OF_HAVE_FILES
/**
 * @brief Writes the OFData into the specified file.
 *
 * @param path The path of the file to write to
 */







|

|













>
>
>
>
>
>
>
>














|





|
<
<
<
<


|

|
|
|







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
 * @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
 * @return The result of the comparison
 */
- (OFComparisonResult)compare: (OFData *)data;

/**
 * @brief Returns a specific item of the OFData.
 *
 * @param index The number of the item to return
 * @return The specified item of the OFData
 */
- (const void *)itemAtIndex: (size_t)index OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the data in the specified range as a new OFData.
 *
 * @param range The range of the data for the new OFData
 * @return The data in the specified range as a new OFData
 */
- (OFData *)subdataWithRange: (OFRange)range;

/**
 * @brief Returns the range of the data.
 *
 * @param data The data to search for
 * @param options Options modifying search behavior




 * @param range The range in which to search
 * @return The range of the first occurrence of the data or a range with
 *	   `OFNotFound` as start position if it was not found.
 */
- (OFRange)rangeOfData: (OFData *)data
	       options: (OFDataSearchOptions)options
		 range: (OFRange)range;

#ifdef OF_HAVE_FILES
/**
 * @brief Writes the OFData into the specified file.
 *
 * @param path The path of the file to write to
 */
323
324
325
326
327
328
329
330
331
332
 */
- (void)writeToURL: (OFURL *)URL;
@end

OF_ASSUME_NONNULL_END

#import "OFMutableData.h"
#import "OFData+ASN1DERParsing.h"
#import "OFData+CryptoHashing.h"
#import "OFData+MessagePackParsing.h"







<
|

344
345
346
347
348
349
350

351
352
 */
- (void)writeToURL: (OFURL *)URL;
@end

OF_ASSUME_NONNULL_END

#import "OFMutableData.h"

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

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

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdlib.h>
#include <string.h>
#include <limits.h>

#import "OFData.h"

#import "OFDictionary.h"
#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"

#import "base64.h"

/* References for static linking */
void
_references_to_categories_of_OFData(void)
{
	_OFData_ASN1DERParsing_reference = 1;
	_OFData_CryptoHashing_reference = 1;
	_OFData_MessagePackParsing_reference = 1;
}

@implementation OFData
@synthesize itemSize = _itemSize;

+ (instancetype)dataWithItems: (const void *)items
			count: (size_t)count
{
	return [[[self alloc] initWithItems: items
				      count: count] autorelease];
}

+ (instancetype)dataWithItems: (const void *)items
		     itemSize: (size_t)itemSize
			count: (size_t)count

{
	return [[[self alloc] initWithItems: items

				   itemSize: itemSize
				      count: count] autorelease];
}

+ (instancetype)dataWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone
{
	return [[[self alloc] initWithItemsNoCopy: items
					    count: count
				     freeWhenDone: freeWhenDone] autorelease];
}

+ (instancetype)dataWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone
{
	return [[[self alloc] initWithItemsNoCopy: items
					 itemSize: itemSize
					    count: count

				     freeWhenDone: freeWhenDone] autorelease];
}

#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path
{
	return [[[self alloc] initWithContentsOfFile: path] autorelease];

<
<
|




















>




















<
<




<
|






|
<

|
<



<

>


>
|
<












<

>



<

>







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

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <limits.h>

#import "OFData.h"
#import "OFBase64.h"
#import "OFDictionary.h"
#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
_references_to_categories_of_OFData(void)
{

	_OFData_CryptographicHashing_reference = 1;
	_OFData_MessagePackParsing_reference = 1;
}

@implementation OFData
@synthesize itemSize = _itemSize;

+ (instancetype)dataWithItems: (const void *)items count: (size_t)count

{
	return [[[self alloc] initWithItems: items count: count] autorelease];

}

+ (instancetype)dataWithItems: (const void *)items

			count: (size_t)count
		     itemSize: (size_t)itemSize
{
	return [[[self alloc] initWithItems: items
				      count: count
				   itemSize: itemSize] autorelease];

}

+ (instancetype)dataWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone
{
	return [[[self alloc] initWithItemsNoCopy: items
					    count: count
				     freeWhenDone: freeWhenDone] autorelease];
}

+ (instancetype)dataWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone
{
	return [[[self alloc] initWithItemsNoCopy: items

					    count: count
					 itemSize: itemSize
				     freeWhenDone: freeWhenDone] autorelease];
}

#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path
{
	return [[[self alloc] initWithContentsOfFile: path] autorelease];
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
}

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

- (instancetype)initWithItems: (const void *)items
			count: (size_t)count
{
	return [self initWithItems: items
			  itemSize: 1
			     count: count];
}

- (instancetype)initWithItems: (const void *)items
		     itemSize: (size_t)itemSize
			count: (size_t)count

{
	self = [super init];

	@try {
		if (itemSize == 0)
			@throw [OFInvalidArgumentException exception];

		_items = [self allocMemoryWithSize: itemSize
					     count: count];
		_itemSize = itemSize;
		_count = count;

		memcpy(_items, items, count * itemSize);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone
{
	return [self initWithItemsNoCopy: items
				itemSize: 1
				   count: count

			    freeWhenDone: freeWhenDone];
}

- (instancetype)initWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone
{
	self = [super init];

	@try {
		if (itemSize == 0)
			@throw [OFInvalidArgumentException exception];

		_items = (unsigned char *)items;
		_itemSize = itemSize;
		_count = count;

		_freeWhenDone = freeWhenDone;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;







|
<

|
<
<



<

>







|
|

|















<

>




<

>









<

>







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
}

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

- (instancetype)initWithItems: (const void *)items count: (size_t)count

{
	return [self initWithItems: items count: count itemSize: 1];


}

- (instancetype)initWithItems: (const void *)items

			count: (size_t)count
		     itemSize: (size_t)itemSize
{
	self = [super init];

	@try {
		if (itemSize == 0)
			@throw [OFInvalidArgumentException exception];

		_items = OFAllocMemory(count, itemSize);
		_count = count;
		_itemSize = itemSize;
		_freeWhenDone = true;

		memcpy(_items, items, count * itemSize);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone
{
	return [self initWithItemsNoCopy: items

				   count: count
				itemSize: 1
			    freeWhenDone: freeWhenDone];
}

- (instancetype)initWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone
{
	self = [super init];

	@try {
		if (itemSize == 0)
			@throw [OFInvalidArgumentException exception];

		_items = (unsigned char *)items;

		_count = count;
		_itemSize = itemSize;
		_freeWhenDone = freeWhenDone;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
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
		    attributesOfItemAtPath: path].fileSize;

# if ULLONG_MAX > SIZE_MAX
		if (size > SIZE_MAX)
			@throw [OFOutOfRangeException exception];
# endif

		buffer = of_malloc(1, (size_t)size);
		file = [[OFFile alloc] initWithPath: path
					       mode: @"r"];
		@try {
			[file readIntoBuffer: buffer
				 exactLength: (size_t)size];
		} @finally {
			[file release];
		}
	} @catch (id e) {
		of_free(buffer);
		[self release];

		@throw e;
	}

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

	return self;
}
#endif








|
|
<

|
<




|










|







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

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
		size_t pageSize;
		unsigned char *buffer;

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

		stream = [URLHandler openItemAtURL: URL
					      mode: @"r"];


		_itemSize = 1;
		_count = 0;

		pageSize = [OFSystemInfo pageSize];
		buffer = [self allocMemoryWithSize: pageSize];


		while (!stream.atEndOfStream) {
			size_t length = [stream readIntoBuffer: buffer

							length: pageSize];

			if (SIZE_MAX - _count < length)
				@throw [OFOutOfRangeException exception];


			_items = [self resizeMemory: _items
					       size: _count + length];
			memcpy(_items + _count, buffer, length);
			_count += length;



		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithStringRepresentation: (OFString *)string
{
	self = [super init];

	@try {
		size_t count = [string
		    cStringLengthWithEncoding: OF_STRING_ENCODING_ASCII];
		const char *cString;

		if (count % 2 != 0)
			@throw [OFInvalidFormatException exception];

		count /= 2;

		_items = [self allocMemoryWithSize: count];

		_itemSize = 1;
		_count = count;

		cString = [string
		    cStringWithEncoding: OF_STRING_ENCODING_ASCII];

		for (size_t i = 0; i < count; i++) {
			uint8_t c1 = cString[2 * i];
			uint8_t c2 = cString[2 * i + 1];
			uint8_t byte;

			if (c1 >= '0' && c1 <= '9')







|
<

>

|


|

>
|
|
>
|

|
|
>

|
|
|
|
>
>
>

















|







|
>

|

|
<







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
		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);

		@try {
			while (!stream.atEndOfStream) {
				size_t length = [stream
				    readIntoBuffer: buffer
					    length: pageSize];

				if (SIZE_MAX - _count < length)
					@throw [OFOutOfRangeException
					    exception];

				_items = OFResizeMemory(_items,
				    _count + length, 1);
				memcpy(_items + _count, buffer, length);
				_count += length;
			}
		} @finally {
			OFFreeMemory(buffer);
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithStringRepresentation: (OFString *)string
{
	self = [super init];

	@try {
		size_t count = [string
		    cStringLengthWithEncoding: OFStringEncodingASCII];
		const char *cString;

		if (count % 2 != 0)
			@throw [OFInvalidFormatException exception];

		count /= 2;

		_items = OFAllocMemory(count, 1);
		_count = count;
		_itemSize = 1;
		_freeWhenDone = true;

		cString = [string cStringWithEncoding: OFStringEncodingASCII];


		for (size_t i = 0; i < count; i++) {
			uint8_t c1 = cString[2 * i];
			uint8_t c2 = cString[2 * i + 1];
			uint8_t byte;

			if (c1 >= '0' && c1 <= '9')
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
		[self release];
		self = [OFMutableData alloc];
	}

	self = [(OFMutableData *)self initWithCapacity: string.length / 3];

	@try {
		if (!of_base64_decode((OFMutableData *)self,
		    [string cStringWithEncoding: OF_STRING_ENCODING_ASCII],
		    [string cStringLengthWithEncoding:
		    OF_STRING_ENCODING_ASCII]))
			@throw [OFInvalidFormatException exception];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	if (!mutable)
		[(OFMutableData *)self makeImmutable];

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stringValue;

	@try {
		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		stringValue = element.stringValue;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithBase64EncodedString: stringValue];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (void)dealloc
{
	if (_freeWhenDone)
		of_free(_items);

	[_parentData release];

	[super dealloc];
}

- (size_t)count







|
|
|
<



















|


















|







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
		[self release];
		self = [OFMutableData alloc];
	}

	self = [(OFMutableData *)self initWithCapacity: string.length / 3];

	@try {
		if (!OFBase64Decode((OFMutableData *)self,
		    [string cStringWithEncoding: OFStringEncodingASCII],
		    [string cStringLengthWithEncoding: OFStringEncodingASCII]))

			@throw [OFInvalidFormatException exception];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	if (!mutable)
		[(OFMutableData *)self makeImmutable];

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stringValue;

	@try {
		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		stringValue = element.stringValue;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithBase64EncodedString: stringValue];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (void)dealloc
{
	if (_freeWhenDone)
		OFFreeMemory(_items);

	[_parentData release];

	[super dealloc];
}

- (size_t)count
425
426
427
428
429
430
431

432
433
434
435
436
437
438
439
440
{
	return [self retain];
}

- (id)mutableCopy
{
	return [[OFMutableData alloc] initWithItems: _items

					   itemSize: _itemSize
					      count: _count];
}

- (bool)isEqual: (id)object
{
	OFData *data;

	if (object == self)







>
|
<







419
420
421
422
423
424
425
426
427

428
429
430
431
432
433
434
{
	return [self retain];
}

- (id)mutableCopy
{
	return [[OFMutableData alloc] initWithItems: _items
					      count: _count
					   itemSize: _itemSize];

}

- (bool)isEqual: (id)object
{
	OFData *data;

	if (object == self)
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
		return false;
	if (memcmp(data.items, _items, _count * _itemSize) != 0)
		return false;

	return true;
}

- (of_comparison_result_t)compare: (id <OFComparing>)object
{
	OFData *data;
	int comparison;
	size_t count, minCount;

	if (![(id)object isKindOfClass: [OFData class]])
		@throw [OFInvalidArgumentException exception];

	data = (OFData *)object;

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

	count = data.count;
	minCount = (_count > count ? count : _count);

	if ((comparison = memcmp(_items, data.items,
	    minCount * _itemSize)) == 0) {
		if (_count > count)
			return OF_ORDERED_DESCENDING;
		if (_count < count)
			return OF_ORDERED_ASCENDING;

		return OF_ORDERED_SAME;
	}

	if (comparison > 0)
		return OF_ORDERED_DESCENDING;
	else
		return OF_ORDERED_ASCENDING;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

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

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFData *)subdataWithRange: (of_range_t)range
{
	OFData *ret;

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

	ret = [OFData dataWithItemsNoCopy: _items + (range.location * _itemSize)
				 itemSize: _itemSize
				    count: range.length

			     freeWhenDone: false];
	ret->_parentData = [(_parentData != nil ? _parentData : self) copy];

	return ret;
}

- (OFString *)description







|

<



|


<
<









|

|

|



|

|




|

|


|

|




|








<

>







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
		return false;
	if (memcmp(data.items, _items, _count * _itemSize) != 0)
		return false;

	return true;
}

- (OFComparisonResult)compare: (OFData *)data
{

	int comparison;
	size_t count, minCount;

	if (![data isKindOfClass: [OFData class]])
		@throw [OFInvalidArgumentException exception];



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

	count = data.count;
	minCount = (_count > count ? count : _count);

	if ((comparison = memcmp(_items, data.items,
	    minCount * _itemSize)) == 0) {
		if (_count > count)
			return OFOrderedDescending;
		if (_count < count)
			return OFOrderedAscending;

		return OFOrderedSame;
	}

	if (comparison > 0)
		return OFOrderedDescending;
	else
		return OFOrderedAscending;
}

- (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
{
	OFData *ret;

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

	ret = [OFData dataWithItemsNoCopy: _items + (range.location * _itemSize)

				    count: range.length
				 itemSize: _itemSize
			     freeWhenDone: false];
	ret->_parentData = [(_parentData != nil ? _parentData : self) copy];

	return ret;
}

- (OFString *)description
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

	[ret makeImmutable];
	return ret;
}

- (OFString *)stringByBase64Encoding
{
	return of_base64_encode(_items, _count * _itemSize);
}

- (of_range_t)rangeOfData: (OFData *)data
		  options: (int)options
		    range: (of_range_t)range
{
	const char *search;
	size_t searchLength;

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

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

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

	if (searchLength > range.length)
		return of_range(OF_NOT_FOUND, 0);

	search = data.items;

	if (options & OF_DATA_SEARCH_BACKWARDS) {
		for (size_t i = range.length - searchLength;; i--) {
			if (memcmp(_items + i * _itemSize, search,
			    searchLength * _itemSize) == 0)
				return of_range(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 of_range(i, searchLength);
	}

	return of_range(OF_NOT_FOUND, 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;
	OFStream *stream;

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

	stream = [URLHandler openItemAtURL: URL
				      mode: @"w"];
	[stream 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: OF_SERIALIZATION_NS
		stringValue: of_base64_encode(_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 dataWithItemSize: 1
					      capacity: _count + 2];

		[data addItem: &type];
		[data addItem: &tmp];
	} else if (_count <= UINT16_MAX) {
		uint8_t type = 0xC5;
		uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)_count);

		data = [OFMutableData dataWithItemSize: 1
					      capacity: _count + 3];

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else if (_count <= UINT32_MAX) {
		uint8_t type = 0xC6;
		uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)_count);

		data = [OFMutableData dataWithItemSize: 1
					      capacity: _count + 5];

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else
		@throw [OFOutOfRangeException exception];

	[data addItems: _items
		 count: _count];

	[data makeImmutable];

	return data;
}
@end







|


|
|
|












|


|



|



|










|


|





|
<
<

|
<










<




|
<
<















|
|



















|
<
<




|

|
<
<

|
<


|

|
<
<

|
<



|
<
<





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

	[ret makeImmutable];
	return ret;
}

- (OFString *)stringByBase64Encoding
{
	return OFBase64Encode(_items, _count * _itemSize);
}

- (OFRange)rangeOfData: (OFData *)data
	       options: (OFDataSearchOptions)options
		 range: (OFRange)range
{
	const char *search;
	size_t searchLength;

	if (range.length > SIZE_MAX - range.location ||
	    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];
		[data addItem: &tmp];
	} else if (_count <= UINT16_MAX) {
		uint8_t type = 0xC5;
		uint16_t tmp = OFToBigEndian16((uint16_t)_count);

		data = [OFMutableData dataWithCapacity: _count + 3];


		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else if (_count <= UINT32_MAX) {
		uint8_t type = 0xC6;
		uint32_t tmp = OFToBigEndian32((uint32_t)_count);

		data = [OFMutableData dataWithCapacity: _count + 5];


		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else
		@throw [OFOutOfRangeException exception];

	[data addItems: _items count: _count];


	[data makeImmutable];

	return data;
}
@end

Modified src/OFDatagramSocket.h from [d41735acda] to [c9d6b20376].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFKernelEventObserver.h"
#import "OFRunLoop.h"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFData;
@class OFDatagramSocket;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when a packet has been received.
 *
 * @param length The length of the packet
 * @param sender The address of the sender of the packet
 * @param exception An exception which occurred while receiving or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next receive
 */
typedef bool (^of_datagram_socket_async_receive_block_t)(
    size_t length, const of_socket_address_t *_Nonnull sender,
    id _Nullable exception);

/**
 * @brief A block which is called when a packet has been sent.
 *
 * @param data The data which was sent
 * @param receiver The receiver for the packet
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return The data to repeat the send with or nil if it should not repeat
 */
typedef OFData *_Nullable (^of_datagram_socket_async_send_data_block_t)(
    OFData *_Nonnull data, const of_socket_address_t *_Nonnull receiver,
    id _Nullable exception);
#endif

/**
 * @protocol OFDatagramSocketDelegate OFDatagramSocket.h \
 *	     ObjFW/OFDatagramSocket.h
 *

<
<
|
















<
|


















|
<
|




<
<




|
<







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


 * 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 "OFKernelEventObserver.h"
#import "OFRunLoop.h"

#import "OFSocket.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFData;
@class OFDatagramSocket;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when a packet has been received.
 *
 * @param length The length of the packet
 * @param sender The address of the sender of the packet
 * @param exception An exception which occurred while receiving or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next receive
 */
typedef bool (^OFDatagramSocketAsyncReceiveBlock)(size_t length,

    const OFSocketAddress *_Nonnull sender, id _Nullable exception);

/**
 * @brief A block which is called when a packet has been sent.
 *


 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return The data to repeat the send with or nil if it should not repeat
 */
typedef OFData *_Nullable (^OFDatagramSocketAsyncSendDataBlock)(

    id _Nullable exception);
#endif

/**
 * @protocol OFDatagramSocketDelegate OFDatagramSocket.h \
 *	     ObjFW/OFDatagramSocket.h
 *
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
 * @param exception An exception that occurred while receiving, or nil on
 *		    success
 * @return A bool whether the same block should be used for the next receive
 */
-	  (bool)socket: (OFDatagramSocket *)socket
  didReceiveIntoBuffer: (void *)buffer
		length: (size_t)length
		sender: (const of_socket_address_t *_Nonnull)sender
	     exception: (nullable id)exception;

/**
 * @brief This method is called when a packet has been sent.
 *
 * @param socket The datagram socket which sent a packet
 * @param data The data which was sent
 * @param receiver The receiver for the packet
 * @param exception An exception that occurred while sending, or nil on success
 * @return The data to repeat the send with or nil if it should not repeat
 */
- (nullable OFData *)socket: (OFDatagramSocket *)socket
		didSendData: (OFData *)data
		   receiver: (const of_socket_address_t *_Nonnull)receiver
		  exception: (nullable id)exception;
@end

/**
 * @class OFDatagramSocket OFDatagramSocket.h ObjFW/OFDatagramSocket.h
 *
 * @brief A base class for datagram sockets.
 *
 * @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 OFDatagramSocket: OFObject <OFCopying, OFReadyForReadingObserving,
    OFReadyForWritingObserving>
{
	of_socket_t _socket;
	bool _canBlock;
#ifdef OF_WII
	bool _canSendToBroadcastAddresses;
#endif
	id <OFDatagramSocketDelegate> _Nullable _delegate;
	OF_RESERVE_IVARS(OFDatagramSocket, 4)
}







|













|


















|







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
 * @param exception An exception that occurred while receiving, or nil on
 *		    success
 * @return A bool whether the same block should be used for the next receive
 */
-	  (bool)socket: (OFDatagramSocket *)socket
  didReceiveIntoBuffer: (void *)buffer
		length: (size_t)length
		sender: (const OFSocketAddress *_Nonnull)sender
	     exception: (nullable id)exception;

/**
 * @brief This method is called when a packet has been sent.
 *
 * @param socket The datagram socket which sent a packet
 * @param data The data which was sent
 * @param receiver The receiver for the packet
 * @param exception An exception that occurred while sending, or nil on success
 * @return The data to repeat the send with or nil if it should not repeat
 */
- (nullable OFData *)socket: (OFDatagramSocket *)socket
		didSendData: (OFData *)data
		   receiver: (const OFSocketAddress *_Nonnull)receiver
		  exception: (nullable id)exception;
@end

/**
 * @class OFDatagramSocket OFDatagramSocket.h ObjFW/OFDatagramSocket.h
 *
 * @brief A base class for datagram sockets.
 *
 * @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 OFDatagramSocket: OFObject <OFCopying, OFReadyForReadingObserving,
    OFReadyForWritingObserving>
{
	OFSocketHandle _socket;
	bool _canBlock;
#ifdef OF_WII
	bool _canSendToBroadcastAddresses;
#endif
	id <OFDatagramSocketDelegate> _Nullable _delegate;
	OF_RESERVE_IVARS(OFDatagramSocket, 4)
}
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
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
/**
 * @brief Receives a datagram and stores it into the specified buffer.
 *
 * 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 of_socket_address_t, 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: (of_socket_address_t *)sender;

/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * 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
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length;

/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * 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 runLoopMode The run loop mode in which to perform the async receive
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * 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 block The block to call when the datagram has been received. If the
 *		block returns true, it will be called again with the same
 *		buffer and maximum length when more datagrams have been
 *		received. If you want the next method in the queue to handle
 *		the datagram received next, you need to return false from the
 *		method.
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
			 block: (of_datagram_socket_async_receive_block_t)block;

/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * 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 runLoopMode The run loop mode in which to perform the async receive
 * @param block The block to call when the datagram has been received. If the
 *		block returns true, it will be called again with the same
 *		buffer and maximum length when more datagrams have been
 *		received. If you want the next method in the queue to handle
 *		the datagram received next, you need to return false from the
 *		method.
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (of_run_loop_mode_t)runLoopMode
			 block: (of_datagram_socket_async_receive_block_t)block;
#endif

/**
 * @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 of_socket_address_t to which the
 *		   datagram should be sent
 */
- (void)sendBuffer: (const void *)buffer
	    length: (size_t)length
	  receiver: (const of_socket_address_t *)receiver;

/**
 * @brief Asynchronously sends the specified datagram to the specified address.
 *
 * @param data The data to send as a datagram
 * @param receiver A pointer to an @ref of_socket_address_t to which the
 *		   datagram should be sent. The receiver is copied.
 */
- (void)asyncSendData: (OFData *)data
	     receiver: (const of_socket_address_t *)receiver;

/**
 * @brief Asynchronously sends the specified datagram to the specified address.
 *
 * @param data The data to send as a datagram
 * @param receiver A pointer to an @ref of_socket_address_t to which the
 *		   datagram should be sent. The receiver is copied.
 * @param runLoopMode The run loop mode in which to perform the async send
 */
- (void)asyncSendData: (OFData *)data
	     receiver: (const of_socket_address_t *)receiver
	  runLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously sends the specified datagram to the specified address.
 *
 * @param data The data to send as a datagram
 * @param receiver A pointer to an @ref of_socket_address_t to which the
 *		   datagram should be sent. The receiver is copied.
 * @param block The block to call when the packet has been sent. It should
 *		return the data for the next send with the same callback or nil
 *		if it should not repeat.
 */
- (void)asyncSendData: (OFData *)data
	     receiver: (const of_socket_address_t *)receiver
		block: (of_datagram_socket_async_send_data_block_t)block;

/**
 * @brief Asynchronously sends the specified datagram to the specified address.
 *
 * @param data The data to send as a datagram
 * @param receiver A pointer to an @ref of_socket_address_t to which the
 *		   datagram should be sent. The receiver is copied.
 * @param runLoopMode The run loop mode in which to perform the async send
 * @param block The block to call when the packet has been sent. It should
 *		return the data for the next send with the same callback or nil
 *		if it should not repeat.
 */
- (void)asyncSendData: (OFData *)data
	     receiver: (const of_socket_address_t *)receiver
	  runLoopMode: (of_run_loop_mode_t)runLoopMode
		block: (of_datagram_socket_async_send_data_block_t)block;
#endif

/**
 * @brief Cancels all pending asynchronous requests on the socket.
 */
- (void)cancelAsyncRequests;








|
|




|










|
<













|



















|



















|
|







|
|



|





|
|


|





|
|



|
|






|
|





|
|





|
|






|
|
|







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
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
/**
 * @brief Receives a datagram and stores it into the specified buffer.
 *
 * 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.
 *
 * @param buffer The buffer to write the datagram to
 * @param length The length of the buffer
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer length: (size_t)length;


/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * 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 runLoopMode The run loop mode in which to perform the async receive
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (OFRunLoopMode)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * 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 block The block to call when the datagram has been received. If the
 *		block returns true, it will be called again with the same
 *		buffer and maximum length when more datagrams have been
 *		received. If you want the next method in the queue to handle
 *		the datagram received next, you need to return false from the
 *		method.
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
			 block: (OFDatagramSocketAsyncReceiveBlock)block;

/**
 * @brief Asynchronously receives a datagram and stores it into the specified
 *	  buffer.
 *
 * 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 runLoopMode The run loop mode in which to perform the async receive
 * @param block The block to call when the datagram has been received. If the
 *		block returns true, it will be called again with the same
 *		buffer and maximum length when more datagrams have been
 *		received. If you want the next method in the queue to handle
 *		the datagram received next, you need to return false from the
 *		method.
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (OFRunLoopMode)runLoopMode
			 block: (OFDatagramSocketAsyncReceiveBlock)block;
#endif

/**
 * @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.
 *
 * @param data The data to send as a datagram
 * @param receiver A pointer to an @ref OFSocketAddress to which the datagram
 *		   should be sent. The receiver is copied.
 */
- (void)asyncSendData: (OFData *)data
	     receiver: (const OFSocketAddress *)receiver;

/**
 * @brief Asynchronously sends the specified datagram to the specified address.
 *
 * @param data The data to send as a datagram
 * @param receiver A pointer to an @ref OFSocketAddress to which the datgram
 *		   should be sent. The receiver is copied.
 * @param runLoopMode The run loop mode in which to perform the async send
 */
- (void)asyncSendData: (OFData *)data
	     receiver: (const OFSocketAddress *)receiver
	  runLoopMode: (OFRunLoopMode)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously sends the specified datagram to the specified address.
 *
 * @param data The data to send as a datagram
 * @param receiver A pointer to an @ref OFSocketAddress to which the datagram
 *		   should be sent. The receiver is copied.
 * @param block The block to call when the packet has been sent. It should
 *		return the data for the next send with the same callback or nil
 *		if it should not repeat.
 */
- (void)asyncSendData: (OFData *)data
	     receiver: (const OFSocketAddress *)receiver
		block: (OFDatagramSocketAsyncSendDataBlock)block;

/**
 * @brief Asynchronously sends the specified datagram to the specified address.
 *
 * @param data The data to send as a datagram
 * @param receiver A pointer to an @ref OFSocketAddress to which the datagram
 *		   should be sent. The receiver is copied.
 * @param runLoopMode The run loop mode in which to perform the async send
 * @param block The block to call when the packet has been sent. It should
 *		return the data for the next send with the same callback or nil
 *		if it should not repeat.
 */
- (void)asyncSendData: (OFData *)data
	     receiver: (const OFSocketAddress *)receiver
	  runLoopMode: (OFRunLoopMode)runLoopMode
		block: (OFDatagramSocketAsyncSendDataBlock)block;
#endif

/**
 * @brief Cancels all pending asynchronous requests on the socket.
 */
- (void)cancelAsyncRequests;

Modified src/OFDatagramSocket.m from [3479c8733b] to [57abed2c44].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFDatagramSocket.h"
#import "OFData.h"
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"



#import "OFGetOptionFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFWriteFailedException.h"

#import "socket.h"
#import "socket_helpers.h"

@implementation OFDatagramSocket
@synthesize delegate = _delegate;

+ (void)initialize
{
	if (self != [OFDatagramSocket class])
		return;

	if (!of_socket_init())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)socket
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	@try {
		if (self.class == [OFDatagramSocket class]) {
			[self doesNotRecognizeSelector: _cmd];
			abort();
		}

		_socket = INVALID_SOCKET;
		_canBlock = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_socket != INVALID_SOCKET)
		[self close];

	[super dealloc];
}

- (id)copy
{

<
<
|















>
>
>
>
>








|
|
>
>










<
<
<








|



















|











|







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

#include "config.h"

#ifndef _XOPEN_SOURCE_EXTENDED
# define _XOPEN_SOURCE_EXTENDED
#endif
#define _HPUX_ALT_XOPEN_SOCKET_API

#include <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFDatagramSocket.h"
#import "OFData.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"

#import "OFGetOptionFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFWriteFailedException.h"




@implementation OFDatagramSocket
@synthesize delegate = _delegate;

+ (void)initialize
{
	if (self != [OFDatagramSocket class])
		return;

	if (!OFSocketInit())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)socket
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	@try {
		if (self.class == [OFDatagramSocket class]) {
			[self doesNotRecognizeSelector: _cmd];
			abort();
		}

		_socket = OFInvalidSocketHandle;
		_canBlock = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_socket != OFInvalidSocketHandle)
		[self close];

	[super dealloc];
}

- (id)copy
{
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
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

	if (fcntl(_socket, F_SETFL, flags) == -1)
		@throw [OFSetOptionFailedException exceptionWithObject: self
								 errNo: errno];

	_canBlock = canBlock;
#elif defined(OF_WINDOWS)
	u_long v = canBlock;

	if (ioctlsocket(_socket, FIONBIO, &v) == SOCKET_ERROR)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	_canBlock = canBlock;
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (void)setCanSendToBroadcastAddresses: (bool)canSendToBroadcastAddresses
{
	int v = canSendToBroadcastAddresses;

	if (setsockopt(_socket, SOL_SOCKET, SO_BROADCAST,
	    (char *)&v, (socklen_t)sizeof(v)) != 0)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

#ifdef OF_WII
	_canSendToBroadcastAddresses = canSendToBroadcastAddresses;
#endif
}

- (bool)canSendToBroadcastAddresses
{
#ifndef OF_WII
	int v;
	socklen_t len = sizeof(v);

	if (getsockopt(_socket, SOL_SOCKET, SO_BROADCAST,
	    (char *)&v, &len) != 0 || len != sizeof(v))
		@throw [OFGetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	return v;
#else
	return _canSendToBroadcastAddresses;
#endif
}

- (size_t)receiveIntoBuffer: (void *)buffer
		     length: (size_t)length
		     sender: (of_socket_address_t *)sender
{
	ssize_t ret;

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

	sender->length = (socklen_t)sizeof(sender->sockaddr);

#ifndef OF_WINDOWS
	if ((ret = recvfrom(_socket, buffer, length, 0,
	    &sender->sockaddr.sockaddr, &sender->length)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
#else
	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((ret = recvfrom(_socket, buffer, (int)length, 0,
	    &sender->sockaddr.sockaddr, &sender->length)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
#endif

	switch (sender->sockaddr.sockaddr.sa_family) {
	case AF_INET:
		sender->family = OF_SOCKET_ADDRESS_FAMILY_IPV4;
		break;
#ifdef OF_HAVE_IPV6
	case AF_INET6:
		sender->family = OF_SOCKET_ADDRESS_FAMILY_IPV6;
		break;
#endif
#ifdef OF_HAVE_IPX
	case AF_IPX:
		sender->family = OF_SOCKET_ADDRESS_FAMILY_IPX;





		break;
#endif
	default:
		sender->family = OF_SOCKET_ADDRESS_FAMILY_UNKNOWN;
		break;
	}

	return ret;
}

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
{
	[self asyncReceiveIntoBuffer: buffer
			      length: length
			 runLoopMode: of_run_loop_mode_default];
}

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	[OFRunLoop of_addAsyncReceiveForDatagramSocket: self
						buffer: buffer
						length: length
						  mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
						 block: NULL
# endif
					      delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
			 block: (of_datagram_socket_async_receive_block_t)block
{
	[self asyncReceiveIntoBuffer: buffer
			      length: length
			 runLoopMode: of_run_loop_mode_default
			       block: block];
}

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (of_run_loop_mode_t)runLoopMode
			 block: (of_datagram_socket_async_receive_block_t)block
{
	[OFRunLoop of_addAsyncReceiveForDatagramSocket: self
						buffer: buffer
						length: length
						  mode: runLoopMode
						 block: block
					      delegate: nil];
}
#endif

- (void)sendBuffer: (const void *)buffer
	    length: (size_t)length
	  receiver: (const of_socket_address_t *)receiver
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

#ifndef OF_WINDOWS
	ssize_t bytesWritten;

	if (length > SSIZE_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = sendto(_socket, (void *)buffer, length, 0,
	    (struct sockaddr *)&receiver->sockaddr.sockaddr,
	    receiver->length)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: of_socket_errno()];
#else
	int bytesWritten;

	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = sendto(_socket, buffer, (int)length, 0,
	    &receiver->sockaddr.sockaddr, receiver->length)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: of_socket_errno()];
#endif

	if ((size_t)bytesWritten != length)
		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: bytesWritten
							     errNo: 0];
}

- (void)asyncSendData: (OFData *)data
	     receiver: (const of_socket_address_t *)receiver
{
	[self asyncSendData: data
		   receiver: receiver
		runLoopMode: of_run_loop_mode_default];
}

- (void)asyncSendData: (OFData *)data
	     receiver: (const of_socket_address_t *)receiver
	  runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	[OFRunLoop of_addAsyncSendForDatagramSocket: self
					       data: data
					   receiver: receiver
					       mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
					      block: NULL
# endif
					   delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncSendData: (OFData *)data
	     receiver: (const of_socket_address_t *)receiver
		block: (of_datagram_socket_async_send_data_block_t)block
{
	[self asyncSendData: data
		   receiver: receiver
		runLoopMode: of_run_loop_mode_default
		      block: block];
}

- (void)asyncSendData: (OFData *)data
	     receiver: (const of_socket_address_t *)receiver
	  runLoopMode: (of_run_loop_mode_t)runLoopMode
		block: (of_datagram_socket_async_send_data_block_t)block
{
	[OFRunLoop of_addAsyncSendForDatagramSocket: self
					       data: data
					   receiver: receiver
					       mode: runLoopMode
					      block: block
					   delegate: nil];
}
#endif

- (void)cancelAsyncRequests
{
	[OFRunLoop of_cancelAsyncRequestsForObject: self
					      mode: of_run_loop_mode_default];
}

- (int)fileDescriptorForReading
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == INVALID_SOCKET)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (int)fileDescriptorForWriting
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == INVALID_SOCKET)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (void)close
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	closesocket(_socket);
	_socket = INVALID_SOCKET;
}
@end







|




|















|
















|









|



|










|









|




|



|




|
>
>
>
>
>



|






|
<



|




|














|



|





|
|












|

|















|












|










|



|



|
|













|
|



|




|
|
|













|







|














|











|



|


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

	if (fcntl(_socket, F_SETFL, flags) == -1)
		@throw [OFSetOptionFailedException exceptionWithObject: self
								 errNo: errno];

	_canBlock = canBlock;
#elif defined(OF_WINDOWS)
	u_long v = !canBlock;

	if (ioctlsocket(_socket, FIONBIO, &v) == SOCKET_ERROR)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];

	_canBlock = canBlock;
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (void)setCanSendToBroadcastAddresses: (bool)canSendToBroadcastAddresses
{
	int v = canSendToBroadcastAddresses;

	if (setsockopt(_socket, SOL_SOCKET, SO_BROADCAST,
	    (char *)&v, (socklen_t)sizeof(v)) != 0)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];

#ifdef OF_WII
	_canSendToBroadcastAddresses = canSendToBroadcastAddresses;
#endif
}

- (bool)canSendToBroadcastAddresses
{
#ifndef OF_WII
	int v;
	socklen_t len = sizeof(v);

	if (getsockopt(_socket, SOL_SOCKET, SO_BROADCAST,
	    (char *)&v, &len) != 0 || len != sizeof(v))
		@throw [OFGetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];

	return v;
#else
	return _canSendToBroadcastAddresses;
#endif
}

- (size_t)receiveIntoBuffer: (void *)buffer
		     length: (size_t)length
		     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,
	    &sender->sockaddr.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,
	    &sender->sockaddr.sockaddr, &sender->length)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: OFSocketErrNo()];
#endif

	switch (sender->sockaddr.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_IPX
	case AF_IPX:
		sender->family = OFSocketAddressFamilyIPX;
		break;
#endif
#ifdef OF_HAVE_UNIX_SOCKETS
	case AF_UNIX:
		sender->family = OFSocketAddressFamilyUNIX;
		break;
#endif
	default:
		sender->family = OFSocketAddressFamilyUnknown;
		break;
	}

	return ret;
}

- (void)asyncReceiveIntoBuffer: (void *)buffer length: (size_t)length

{
	[self asyncReceiveIntoBuffer: buffer
			      length: length
			 runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (OFRunLoopMode)runLoopMode
{
	[OFRunLoop of_addAsyncReceiveForDatagramSocket: self
						buffer: buffer
						length: length
						  mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
						 block: NULL
# endif
					      delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
			 block: (OFDatagramSocketAsyncReceiveBlock)block
{
	[self asyncReceiveIntoBuffer: buffer
			      length: length
			 runLoopMode: OFDefaultRunLoopMode
			       block: block];
}

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (OFRunLoopMode)runLoopMode
			 block: (OFDatagramSocketAsyncReceiveBlock)block
{
	[OFRunLoop of_addAsyncReceiveForDatagramSocket: self
						buffer: buffer
						length: length
						  mode: runLoopMode
						 block: block
					      delegate: nil];
}
#endif

- (void)sendBuffer: (const void *)buffer
	    length: (size_t)length
	  receiver: (const OFSocketAddress *)receiver
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

#ifndef OF_WINDOWS
	ssize_t bytesWritten;

	if (length > SSIZE_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = sendto(_socket, (void *)buffer, length, 0,
	    (struct sockaddr *)&receiver->sockaddr.sockaddr,
	    receiver->length)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: OFSocketErrNo()];
#else
	int bytesWritten;

	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = sendto(_socket, buffer, (int)length, 0,
	    &receiver->sockaddr.sockaddr, receiver->length)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: OFSocketErrNo()];
#endif

	if ((size_t)bytesWritten != length)
		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: bytesWritten
							     errNo: 0];
}

- (void)asyncSendData: (OFData *)data
	     receiver: (const OFSocketAddress *)receiver
{
	[self asyncSendData: data
		   receiver: receiver
		runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncSendData: (OFData *)data
	     receiver: (const OFSocketAddress *)receiver
	  runLoopMode: (OFRunLoopMode)runLoopMode
{
	[OFRunLoop of_addAsyncSendForDatagramSocket: self
					       data: data
					   receiver: receiver
					       mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
					      block: NULL
# endif
					   delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncSendData: (OFData *)data
	     receiver: (const OFSocketAddress *)receiver
		block: (OFDatagramSocketAsyncSendDataBlock)block
{
	[self asyncSendData: data
		   receiver: receiver
		runLoopMode: OFDefaultRunLoopMode
		      block: block];
}

- (void)asyncSendData: (OFData *)data
	     receiver: (const OFSocketAddress *)receiver
	  runLoopMode: (OFRunLoopMode)runLoopMode
		block: (OFDatagramSocketAsyncSendDataBlock)block
{
	[OFRunLoop of_addAsyncSendForDatagramSocket: self
					       data: data
					   receiver: receiver
					       mode: runLoopMode
					      block: block
					   delegate: nil];
}
#endif

- (void)cancelAsyncRequests
{
	[OFRunLoop of_cancelAsyncRequestsForObject: self
					      mode: OFDefaultRunLoopMode];
}

- (int)fileDescriptorForReading
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == OFInvalidSocketHandle)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (int)fileDescriptorForWriting
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == OFInvalidSocketHandle)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (void)close
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	closesocket(_socket);
	_socket = OFInvalidSocketHandle;
}
@end

Modified src/OFDate.h from [b56f401ff6] to [f33ded2414].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 */
#ifndef OF_DATE_M
OF_SUBCLASSING_RESTRICTED
#endif
@interface OFDate: OFObject <OFCopying, OFComparing, OFSerialization,
    OFMessagePackRepresentation>
{
	of_time_interval_t _seconds;
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) OFDate *distantFuture;
@property (class, readonly, nonatomic) OFDate *distantPast;
#endif








|







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 */
#ifndef OF_DATE_M
OF_SUBCLASSING_RESTRICTED
#endif
@interface OFDate: OFObject <OFCopying, OFComparing, OFSerialization,
    OFMessagePackRepresentation>
{
	OFTimeInterval _seconds;
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) OFDate *distantFuture;
@property (class, readonly, nonatomic) OFDate *distantPast;
#endif

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
 * @brief The day of the year of the date in local time.
 */
@property (readonly, nonatomic) unsigned short localDayOfYear;

/**
 * @brief The seconds since 1970-01-01T00:00:00Z.
 */
@property (readonly, nonatomic) of_time_interval_t timeIntervalSince1970;

/**
 * @brief The seconds the date is in the future.
 */
@property (readonly, nonatomic) of_time_interval_t timeIntervalSinceNow;

/**
 * @brief Creates a new OFDate with the current date and time.
 *
 * @return A new, autoreleased OFDate with the current date and time
 */
+ (instancetype)date;

/**
 * @brief Creates a new OFDate with the specified date and time since
 *	  1970-01-01T00:00:00Z.
 *
 * @param seconds The seconds since 1970-01-01T00:00:00Z
 * @return A new, autoreleased OFDate with the specified date and time
 */
+ (instancetype)dateWithTimeIntervalSince1970: (of_time_interval_t)seconds;

/**
 * @brief Creates a new OFDate with the specified date and time since now.
 *
 * @param seconds The seconds since now
 * @return A new, autoreleased OFDate with the specified date and time
 */
+ (instancetype)dateWithTimeIntervalSinceNow: (of_time_interval_t)seconds;

/**
 * @brief Creates a new OFDate with the specified string in the specified
 *	  format.
 *
 * The time zone used is UTC. See @ref dateWithLocalDateString:format: if you
 * want local time.







|




|















|







|







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
 * @brief The day of the year of the date in local time.
 */
@property (readonly, nonatomic) unsigned short localDayOfYear;

/**
 * @brief The seconds since 1970-01-01T00:00:00Z.
 */
@property (readonly, nonatomic) OFTimeInterval timeIntervalSince1970;

/**
 * @brief The seconds the date is in the future.
 */
@property (readonly, nonatomic) OFTimeInterval timeIntervalSinceNow;

/**
 * @brief Creates a new OFDate with the current date and time.
 *
 * @return A new, autoreleased OFDate with the current date and time
 */
+ (instancetype)date;

/**
 * @brief Creates a new OFDate with the specified date and time since
 *	  1970-01-01T00:00:00Z.
 *
 * @param seconds The seconds since 1970-01-01T00:00:00Z
 * @return A new, autoreleased OFDate with the specified date and time
 */
+ (instancetype)dateWithTimeIntervalSince1970: (OFTimeInterval)seconds;

/**
 * @brief Creates a new OFDate with the specified date and time since now.
 *
 * @param seconds The seconds since now
 * @return A new, autoreleased OFDate with the specified date and time
 */
+ (instancetype)dateWithTimeIntervalSinceNow: (OFTimeInterval)seconds;

/**
 * @brief Creates a new OFDate with the specified string in the specified
 *	  format.
 *
 * The time zone used is UTC. See @ref dateWithLocalDateString:format: if you
 * want local time.
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
/**
 * @brief Initializes an already allocated OFDate with the specified date and
 *	  time since 1970-01-01T00:00:00Z.
 *
 * @param seconds The seconds since 1970-01-01T00:00:00Z
 * @return An initialized OFDate with the specified date and time
 */
- (instancetype)initWithTimeIntervalSince1970: (of_time_interval_t)seconds
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated OFDate with the specified date and
 *	  time since now.
 *
 * @param seconds The seconds since now
 * @return An initialized OFDate with the specified date and time
 */
- (instancetype)initWithTimeIntervalSinceNow: (of_time_interval_t)seconds;

/**
 * @brief Initializes an already allocated OFDate with the specified string in
 *	  the specified format.
 *
 * The time zone used is UTC. If a time zone is specified anyway, an
 * OFInvalidFormatException is thrown. See @ref initWithLocalDateString:format:







|









|







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
/**
 * @brief Initializes an already allocated OFDate with the specified date and
 *	  time since 1970-01-01T00:00:00Z.
 *
 * @param seconds The seconds since 1970-01-01T00:00:00Z
 * @return An initialized OFDate with the specified date and time
 */
- (instancetype)initWithTimeIntervalSince1970: (OFTimeInterval)seconds
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Initializes an already allocated OFDate with the specified date and
 *	  time since now.
 *
 * @param seconds The seconds since now
 * @return An initialized OFDate with the specified date and time
 */
- (instancetype)initWithTimeIntervalSinceNow: (OFTimeInterval)seconds;

/**
 * @brief Initializes an already allocated OFDate with the specified string in
 *	  the specified format.
 *
 * The time zone used is UTC. If a time zone is specified anyway, an
 * OFInvalidFormatException is thrown. See @ref initWithLocalDateString:format:
265
266
267
268
269
270
271








272
273
274
275
276
277
278
 * @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 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







>
>
>
>
>
>
>
>







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
 * @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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

/**
 * @brief Returns the seconds the receiver is after the date.
 *
 * @param otherDate Date date to generate the difference with receiver
 * @return The seconds the receiver is after the date.
 */
- (of_time_interval_t)timeIntervalSinceDate: (OFDate *)otherDate;

/**
 * @brief Creates a new date with the specified time interval added.
 *
 * @param seconds The seconds after the date
 * @return A new, autoreleased OFDate
 */
- (OFDate *)dateByAddingTimeInterval: (of_time_interval_t)seconds;
@end

OF_ASSUME_NONNULL_END







|







|



317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

/**
 * @brief Returns the seconds the receiver is after the date.
 *
 * @param otherDate Date date to generate the difference with receiver
 * @return The seconds the receiver is after the date.
 */
- (OFTimeInterval)timeIntervalSinceDate: (OFDate *)otherDate;

/**
 * @brief Creates a new date with the specified time interval added.
 *
 * @param seconds The seconds after the date
 * @return A new, autoreleased OFDate
 */
- (OFDate *)dateByAddingTimeInterval: (OFTimeInterval)seconds;
@end

OF_ASSUME_NONNULL_END

Modified src/OFDate.m from [b60c2c0014] to [ebc414dd12].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFDate.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFMessagePackExtension.h"
#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif

#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFXMLElement.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#import "of_strptime.h"

#ifdef OF_AMIGAOS_M68K
/* amiga-gcc does not have trunc() */
# define trunc(x) ((int64_t)(x))
#endif

@interface OFDate ()
+ (instancetype)of_alloc;
@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
#import "OFDate.h"
#import "OFData.h"
#import "OFDictionary.h"
#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"



#if defined(OF_AMIGAOS_M68K) || defined(OF_MINT)
/* amiga-gcc and freemint-gcc do not have trunc() */
# define trunc(x) ((int64_t)(x))
#endif

@interface OFDate ()
+ (instancetype)of_alloc;
@end

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
static void
initDistantPast(void)
{
	distantPast = [[OFDateSingleton alloc]
	    initWithTimeIntervalSince1970: -62167219200.0];
}

static of_time_interval_t
now(void)
{
	struct timeval tv;
	of_time_interval_t seconds;

	OF_ENSURE(gettimeofday(&tv, NULL) == 0);

	seconds = tv.tv_sec;
	seconds += (of_time_interval_t)tv.tv_usec / 1000000;

	return seconds;
}

#if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \
    defined(OF_HAVE_THREADS)
static OFMutex *mutex;






#endif

#ifdef OF_WINDOWS
static __time64_t (*func__mktime64)(struct tm *);
#endif

#ifdef HAVE_GMTIME_R
# define GMTIME_RET(field)						\
	of_time_interval_t timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	if (gmtime_r(&seconds, &tm) == NULL)				\
		@throw [OFOutOfRangeException exception];		\
									\
	return tm.field;
# define LOCALTIME_RET(field)						\
	of_time_interval_t timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	if (localtime_r(&seconds, &tm) == NULL)				\
		@throw [OFOutOfRangeException exception];		\
									\
	return tm.field;
#else
# ifdef OF_HAVE_THREADS
#  define GMTIME_RET(field)						\
	of_time_interval_t timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm *tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	[mutex lock];							\
									\
	@try {								\
		if ((tm = gmtime(&seconds)) == NULL)			\
			@throw [OFOutOfRangeException exception];	\
									\
		return tm->field;					\
	} @finally {							\
		[mutex unlock];						\
	}
#  define LOCALTIME_RET(field)						\
	of_time_interval_t timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm *tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	[mutex lock];							\
									\
	@try {								\
		if ((tm = localtime(&seconds)) == NULL)			\
			@throw [OFOutOfRangeException exception];	\
									\
		return tm->field;					\
	} @finally {							\
		[mutex unlock];						\
	}
# else
#  define GMTIME_RET(field)						\
	of_time_interval_t timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm *tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	if ((tm = gmtime(&seconds)) == NULL)				\
		@throw [OFOutOfRangeException exception];		\
									\
	return tm->field;
#  define LOCALTIME_RET(field)						\
	of_time_interval_t timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm *tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	if ((tm = localtime(&seconds)) == NULL)				\







|



|

|


|







>
>
>
>
>
>



|




|











|













|

















|


















|











|







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
static void
initDistantPast(void)
{
	distantPast = [[OFDateSingleton alloc]
	    initWithTimeIntervalSince1970: -62167219200.0];
}

static OFTimeInterval
now(void)
{
	struct timeval tv;
	OFTimeInterval seconds;

	OFEnsure(gettimeofday(&tv, NULL) == 0);

	seconds = tv.tv_sec;
	seconds += (OFTimeInterval)tv.tv_usec / 1000000;

	return seconds;
}

#if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \
    defined(OF_HAVE_THREADS)
static OFMutex *mutex;

static void
releaseMutex(void)
{
	[mutex release];
}
#endif

#ifdef OF_WINDOWS
static __time64_t (*_mktime64FuncPtr)(struct tm *);
#endif

#ifdef HAVE_GMTIME_R
# define GMTIME_RET(field)						\
	OFTimeInterval timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	if (gmtime_r(&seconds, &tm) == NULL)				\
		@throw [OFOutOfRangeException exception];		\
									\
	return tm.field;
# define LOCALTIME_RET(field)						\
	OFTimeInterval timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	if (localtime_r(&seconds, &tm) == NULL)				\
		@throw [OFOutOfRangeException exception];		\
									\
	return tm.field;
#else
# ifdef OF_HAVE_THREADS
#  define GMTIME_RET(field)						\
	OFTimeInterval timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm *tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	[mutex lock];							\
									\
	@try {								\
		if ((tm = gmtime(&seconds)) == NULL)			\
			@throw [OFOutOfRangeException exception];	\
									\
		return tm->field;					\
	} @finally {							\
		[mutex unlock];						\
	}
#  define LOCALTIME_RET(field)						\
	OFTimeInterval timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm *tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	[mutex lock];							\
									\
	@try {								\
		if ((tm = localtime(&seconds)) == NULL)			\
			@throw [OFOutOfRangeException exception];	\
									\
		return tm->field;					\
	} @finally {							\
		[mutex unlock];						\
	}
# else
#  define GMTIME_RET(field)						\
	OFTimeInterval timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm *tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	if ((tm = gmtime(&seconds)) == NULL)				\
		@throw [OFOutOfRangeException exception];		\
									\
	return tm->field;
#  define LOCALTIME_RET(field)						\
	OFTimeInterval timeInterval = self.timeIntervalSince1970;	\
	time_t seconds = (time_t)timeInterval;				\
	struct tm *tm;							\
									\
	if (seconds != trunc(timeInterval))				\
		@throw [OFOutOfRangeException exception];		\
									\
	if ((tm = localtime(&seconds)) == NULL)				\
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

- (void)release
{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}
@end

@implementation OFDatePlaceholder
#ifdef __clang__
/* We intentionally don't call into super, so silence the warning. */
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunknown-pragmas"
# pragma clang diagnostic ignored "-Wobjc-designated-initializers"
#endif
- (instancetype)initWithTimeIntervalSince1970: (of_time_interval_t)seconds
{
#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX
	uint64_t value;
#endif

	if (seconds == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, initZeroDate);
		return (id)zeroDate;
	}

#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX
	value = OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE(
	    seconds)));

	/* Almost all dates fall into this range. */
	if (value & (UINT64_C(4) << 60)) {
		id ret = objc_createTaggedPointer(dateTag,
		    value & ~(UINT64_C(4) << 60));








|










|






|
|




|







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

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}
@end

@implementation OFDatePlaceholder
#ifdef __clang__
/* We intentionally don't call into super, so silence the warning. */
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunknown-pragmas"
# pragma clang diagnostic ignored "-Wobjc-designated-initializers"
#endif
- (instancetype)initWithTimeIntervalSince1970: (OFTimeInterval)seconds
{
#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX
	uint64_t value;
#endif

	if (seconds == 0) {
		static OFOnceControl once = OFOnceControlInitValue;
		OFOnce(&once, initZeroDate);
		return (id)zeroDate;
	}

#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX
	value = OFFromBigEndian64(OFDoubleToRawUInt64(OFToBigEndianDouble(
	    seconds)));

	/* Almost all dates fall into this range. */
	if (value & (UINT64_C(4) << 60)) {
		id ret = objc_createTaggedPointer(dateTag,
		    value & ~(UINT64_C(4) << 60));

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
#ifdef __clang__
# pragma clang diagnostic pop
#endif
@end

#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX
@implementation OFTaggedPointerDate
- (of_time_interval_t)timeIntervalSince1970
{
	uint64_t value = (uint64_t)object_getTaggedPointerValue(self);

	value |= UINT64_C(4) << 60;

	return OF_BSWAP_DOUBLE_IF_LE(OF_INT_TO_DOUBLE_RAW(OF_BSWAP64_IF_LE(
	    value)));
}
@end
#endif

@implementation OFDate
+ (void)initialize
{
#ifdef OF_WINDOWS
	HMODULE module;
#endif

	if (self != [OFDate class])
		return;

	placeholder.isa = [OFDatePlaceholder class];

#if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \
    defined(OF_HAVE_THREADS)
	mutex = [[OFMutex alloc] init];

#endif

#ifdef OF_WINDOWS
	if ((module = LoadLibrary("msvcrt.dll")) != NULL)
		func__mktime64 = (__time64_t (*)(struct tm *))
		    GetProcAddress(module, "_mktime64");
#endif

#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX
	dateTag = objc_registerTaggedPointerClass([OFTaggedPointerDate class]);
#endif
}







|





|




















>




|







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
#ifdef __clang__
# pragma clang diagnostic pop
#endif
@end

#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX
@implementation OFTaggedPointerDate
- (OFTimeInterval)timeIntervalSince1970
{
	uint64_t value = (uint64_t)object_getTaggedPointerValue(self);

	value |= UINT64_C(4) << 60;

	return OFFromBigEndianDouble(OFRawUInt64ToDouble(OFToBigEndian64(
	    value)));
}
@end
#endif

@implementation OFDate
+ (void)initialize
{
#ifdef OF_WINDOWS
	HMODULE module;
#endif

	if (self != [OFDate class])
		return;

	placeholder.isa = [OFDatePlaceholder class];

#if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \
    defined(OF_HAVE_THREADS)
	mutex = [[OFMutex alloc] init];
	atexit(releaseMutex);
#endif

#ifdef OF_WINDOWS
	if ((module = LoadLibrary("msvcrt.dll")) != NULL)
		_mktime64FuncPtr = (__time64_t (*)(struct tm *))
		    GetProcAddress(module, "_mktime64");
#endif

#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX
	dateTag = objc_registerTaggedPointerClass([OFTaggedPointerDate class]);
#endif
}
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
}

+ (instancetype)date
{
	return [[[self alloc] init] autorelease];
}

+ (instancetype)dateWithTimeIntervalSince1970: (of_time_interval_t)seconds
{
	return [[[self alloc]
	    initWithTimeIntervalSince1970: seconds] autorelease];
}

+ (instancetype)dateWithTimeIntervalSinceNow: (of_time_interval_t)seconds
{
	return [[[self alloc]
	    initWithTimeIntervalSinceNow: seconds] autorelease];
}

+ (instancetype)dateWithDateString: (OFString *)string
			    format: (OFString *)format







|





|







377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
}

+ (instancetype)date
{
	return [[[self alloc] init] autorelease];
}

+ (instancetype)dateWithTimeIntervalSince1970: (OFTimeInterval)seconds
{
	return [[[self alloc]
	    initWithTimeIntervalSince1970: seconds] autorelease];
}

+ (instancetype)dateWithTimeIntervalSinceNow: (OFTimeInterval)seconds
{
	return [[[self alloc]
	    initWithTimeIntervalSinceNow: seconds] autorelease];
}

+ (instancetype)dateWithDateString: (OFString *)string
			    format: (OFString *)format
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
{
	return [[[self alloc] initWithLocalDateString: string
					       format: format] autorelease];
}

+ (instancetype)distantFuture
{
	static of_once_t once = OF_ONCE_INIT;
	of_once(&once, initDistantFuture);
	return distantFuture;
}

+ (instancetype)distantPast
{
	static of_once_t once = OF_ONCE_INIT;
	of_once(&once, initDistantPast);
	return distantPast;
}

- (instancetype)init
{
	return [self initWithTimeIntervalSince1970: now()];
}

- (instancetype)initWithTimeIntervalSince1970: (of_time_interval_t)seconds
{
	self = [super init];

	_seconds = seconds;

	return self;
}

- (instancetype)initWithTimeIntervalSinceNow: (of_time_interval_t)seconds
{
	return [self initWithTimeIntervalSince1970: now() + seconds];
}

- (instancetype)initWithDateString: (OFString *)string
			    format: (OFString *)format
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = string.UTF8String;
	struct tm tm = { .tm_isdst = -1 };
	short tz = 0;

	if (of_strptime(UTF8String, format.UTF8String, &tm, &tz) !=
	    UTF8String + string.UTF8StringLength)
		@throw [OFInvalidFormatException exception];

	objc_autoreleasePoolPop(pool);

	return [self initWithTimeIntervalSince1970: tmAndTzToTime(&tm, tz)];
}

- (instancetype)initWithLocalDateString: (OFString *)string
				 format: (OFString *)format
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = string.UTF8String;
	struct tm tm = { .tm_isdst = -1 };
	/*
	 * of_strptime() can never set this to SHRT_MAX, no matter what is
	 * passed to it, so this is a safe way to figure out if the date
	 * contains a time zone.
	 */
	short tz = SHRT_MAX;
	of_time_interval_t seconds;

	if (of_strptime(UTF8String, format.UTF8String, &tm, &tz) !=
	    UTF8String + string.UTF8StringLength)
		@throw [OFInvalidFormatException exception];

	if (tz == SHRT_MAX) {
#ifdef OF_WINDOWS
		if (func__mktime64 != NULL) {
			if ((seconds = func__mktime64(&tm)) == -1)
				@throw [OFInvalidFormatException exception];
		} else {
#endif
			if ((seconds = mktime(&tm)) == -1)
				@throw [OFInvalidFormatException exception];
#ifdef OF_WINDOWS
		}
#endif
	} else
		seconds = tmAndTzToTime(&tm, tz);

	objc_autoreleasePoolPop(pool);

	return [self initWithTimeIntervalSince1970: seconds];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	of_time_interval_t seconds;

	@try {
		void *pool = objc_autoreleasePoolPush();
		unsigned long long value;

		if (![element.name isEqual: @"OFDate"] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		value = [element unsignedLongLongValueWithBase: 16];

		if (value > UINT64_MAX)
			@throw [OFOutOfRangeException exception];

		seconds = OF_BSWAP_DOUBLE_IF_LE(OF_INT_TO_DOUBLE_RAW(
		    OF_BSWAP64_IF_LE(value)));

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}








|
|





|
|








|








|












|















|




|

|





|
|


















|






|







|
|







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
{
	return [[[self alloc] initWithLocalDateString: string
					       format: format] autorelease];
}

+ (instancetype)distantFuture
{
	static OFOnceControl once = OFOnceControlInitValue;
	OFOnce(&once, initDistantFuture);
	return distantFuture;
}

+ (instancetype)distantPast
{
	static OFOnceControl once = OFOnceControlInitValue;
	OFOnce(&once, initDistantPast);
	return distantPast;
}

- (instancetype)init
{
	return [self initWithTimeIntervalSince1970: now()];
}

- (instancetype)initWithTimeIntervalSince1970: (OFTimeInterval)seconds
{
	self = [super init];

	_seconds = seconds;

	return self;
}

- (instancetype)initWithTimeIntervalSinceNow: (OFTimeInterval)seconds
{
	return [self initWithTimeIntervalSince1970: now() + seconds];
}

- (instancetype)initWithDateString: (OFString *)string
			    format: (OFString *)format
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = string.UTF8String;
	struct tm tm = { .tm_isdst = -1 };
	short tz = 0;

	if (OFStrPTime(UTF8String, format.UTF8String, &tm, &tz) !=
	    UTF8String + string.UTF8StringLength)
		@throw [OFInvalidFormatException exception];

	objc_autoreleasePoolPop(pool);

	return [self initWithTimeIntervalSince1970: tmAndTzToTime(&tm, tz)];
}

- (instancetype)initWithLocalDateString: (OFString *)string
				 format: (OFString *)format
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = string.UTF8String;
	struct tm tm = { .tm_isdst = -1 };
	/*
	 * OFStrPTime() can never set this to SHRT_MAX, no matter what is
	 * passed to it, so this is a safe way to figure out if the date
	 * contains a time zone.
	 */
	short tz = SHRT_MAX;
	OFTimeInterval seconds;

	if (OFStrPTime(UTF8String, format.UTF8String, &tm, &tz) !=
	    UTF8String + string.UTF8StringLength)
		@throw [OFInvalidFormatException exception];

	if (tz == SHRT_MAX) {
#ifdef OF_WINDOWS
		if (_mktime64FuncPtr != NULL) {
			if ((seconds = _mktime64FuncPtr(&tm)) == -1)
				@throw [OFInvalidFormatException exception];
		} else {
#endif
			if ((seconds = mktime(&tm)) == -1)
				@throw [OFInvalidFormatException exception];
#ifdef OF_WINDOWS
		}
#endif
	} else
		seconds = tmAndTzToTime(&tm, tz);

	objc_autoreleasePoolPop(pool);

	return [self initWithTimeIntervalSince1970: seconds];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	OFTimeInterval seconds;

	@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(
		    OFToBigEndian64(value)));

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;
	double tmp;

	OF_HASH_INIT(hash);

	tmp = OF_BSWAP_DOUBLE_IF_BE(self.timeIntervalSince1970);

	for (size_t i = 0; i < sizeof(double); i++)
		OF_HASH_ADD(hash, ((char *)&tmp)[i]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
	return [self retain];
}

- (of_comparison_result_t)compare: (id <OFComparing>)object
{
	OFDate *otherDate;

	if (![(id)object isKindOfClass: [OFDate class]])
		@throw [OFInvalidArgumentException exception];

	otherDate = (OFDate *)object;

	if (self.timeIntervalSince1970 < otherDate.timeIntervalSince1970)
		return OF_ORDERED_ASCENDING;
	if (self.timeIntervalSince1970 > otherDate.timeIntervalSince1970)
		return OF_ORDERED_DESCENDING;

	return OF_ORDERED_SAME;
}

- (OFString *)description
{
	return [self dateStringWithFormat: @"%Y-%m-%dT%H:%M:%SZ"];
}

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

	element = [OFXMLElement elementWithName: @"OFDate"
				      namespace: OF_SERIALIZATION_NS];

	element.stringValue = [OFString stringWithFormat: @"%016" PRIx64,
	    OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE(
	    self.timeIntervalSince1970)))];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFData *)messagePackRepresentation
{
	void *pool = objc_autoreleasePoolPush();
	of_time_interval_t timeInterval = self.timeIntervalSince1970;
	int64_t seconds = (int64_t)timeInterval;
	uint32_t nanoseconds =
	    (uint32_t)((timeInterval - trunc(timeInterval)) * 1000000000);
	OFData *ret;

	if (seconds >= 0 && seconds < 0x400000000) {
		if (seconds <= UINT32_MAX && nanoseconds == 0) {
			uint32_t seconds32 = (uint32_t)seconds;
			OFData *data;

			seconds32 = OF_BSWAP32_IF_LE(seconds32);
			data = [OFData dataWithItems: &seconds32
					       count: sizeof(seconds32)];

			ret = [[OFMessagePackExtension
			    extensionWithType: -1
					 data: data] messagePackRepresentation];
		} else {
			uint64_t combined = ((uint64_t)nanoseconds << 34) |
			    (uint64_t)seconds;
			OFData *data;

			combined = OF_BSWAP64_IF_LE(combined);
			data = [OFData dataWithItems: &combined
					       count: sizeof(combined)];

			ret = [[OFMessagePackExtension
			    extensionWithType: -1
					 data: data] messagePackRepresentation];
		}
	} else {
		OFMutableData *data = [OFMutableData dataWithCapacity: 12];

		seconds = OF_BSWAP64_IF_LE(seconds);
		nanoseconds = OF_BSWAP32_IF_LE(nanoseconds);

		[data addItems: &nanoseconds
			 count: sizeof(nanoseconds)];
		[data addItems: &seconds
			 count: sizeof(seconds)];

		ret = [[OFMessagePackExtension
		    extensionWithType: -1
				 data: data] messagePackRepresentation];
	}

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (unsigned long)microsecond
{
	of_time_interval_t timeInterval = self.timeIntervalSince1970;

	return (unsigned long)((timeInterval - trunc(timeInterval)) * 1000000);
}

- (unsigned char)second
{
	GMTIME_RET(tm_sec)







|


|

|


|

|









<
<
|
|
|


<
<
|
|
|
|

|













|


|












|










|











|










<
|
<
|
|
|
<















|







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
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;
	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
{
	return [self retain];
}



- (OFComparisonResult)compare: (OFDate *)date
{
	if (![date isKindOfClass: [OFDate class]])
		@throw [OFInvalidArgumentException exception];



	if (self.timeIntervalSince1970 < date.timeIntervalSince1970)
		return OFOrderedAscending;
	if (self.timeIntervalSince1970 > date.timeIntervalSince1970)
		return OFOrderedDescending;

	return OFOrderedSame;
}

- (OFString *)description
{
	return [self dateStringWithFormat: @"%Y-%m-%dT%H:%M:%SZ"];
}

- (OFXMLElement *)XMLElementBySerializing
{
	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);

	return [element autorelease];
}

- (OFData *)messagePackRepresentation
{
	void *pool = objc_autoreleasePoolPush();
	OFTimeInterval timeInterval = self.timeIntervalSince1970;
	int64_t seconds = (int64_t)timeInterval;
	uint32_t nanoseconds =
	    (uint32_t)((timeInterval - trunc(timeInterval)) * 1000000000);
	OFData *ret;

	if (seconds >= 0 && seconds < 0x400000000) {
		if (seconds <= UINT32_MAX && nanoseconds == 0) {
			uint32_t seconds32 = (uint32_t)seconds;
			OFData *data;

			seconds32 = OFToBigEndian32(seconds32);
			data = [OFData dataWithItems: &seconds32
					       count: sizeof(seconds32)];

			ret = [[OFMessagePackExtension
			    extensionWithType: -1
					 data: data] messagePackRepresentation];
		} else {
			uint64_t combined = ((uint64_t)nanoseconds << 34) |
			    (uint64_t)seconds;
			OFData *data;

			combined = OFToBigEndian64(combined);
			data = [OFData dataWithItems: &combined
					       count: sizeof(combined)];

			ret = [[OFMessagePackExtension
			    extensionWithType: -1
					 data: data] messagePackRepresentation];
		}
	} else {
		OFMutableData *data = [OFMutableData dataWithCapacity: 12];


		nanoseconds = OFToBigEndian32(nanoseconds);

		[data addItems: &nanoseconds count: sizeof(nanoseconds)];
		seconds = OFToBigEndian64(seconds);
		[data addItems: &seconds count: sizeof(seconds)];


		ret = [[OFMessagePackExtension
		    extensionWithType: -1
				 data: data] messagePackRepresentation];
	}

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (unsigned long)microsecond
{
	OFTimeInterval timeInterval = self.timeIntervalSince1970;

	return (unsigned long)((timeInterval - trunc(timeInterval)) * 1000000);
}

- (unsigned char)second
{
	GMTIME_RET(tm_sec)
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
{
	LOCALTIME_RET(tm_yday + 1)
}

- (OFString *)dateStringWithFormat: (OFConstantString *)format
{
	OFString *ret;
	of_time_interval_t timeInterval = self.timeIntervalSince1970;
	time_t seconds = (time_t)timeInterval;
	struct tm tm;
	size_t pageSize;
#ifndef OF_WINDOWS
	char *buffer;
#else
	wchar_t *buffer;







|







736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
{
	LOCALTIME_RET(tm_yday + 1)
}

- (OFString *)dateStringWithFormat: (OFConstantString *)format
{
	OFString *ret;
	OFTimeInterval timeInterval = self.timeIntervalSince1970;
	time_t seconds = (time_t)timeInterval;
	struct tm tm;
	size_t pageSize;
#ifndef OF_WINDOWS
	char *buffer;
#else
	wchar_t *buffer;
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
	} @finally {
		[mutex unlock];
	}
# endif
#endif

	pageSize = [OFSystemInfo pageSize];
	buffer = of_malloc(1, pageSize);
	@try {
#ifndef OF_WINDOWS
		if (strftime(buffer, pageSize, format.UTF8String, &tm) == 0)
			@throw [OFOutOfRangeException exception];

		ret = [OFString stringWithUTF8String: buffer];
#else
		if (wcsftime(buffer, pageSize / sizeof(wchar_t),
		    format.UTF16String, &tm) == 0)
			@throw [OFOutOfRangeException exception];

		ret = [OFString stringWithUTF16String: buffer];
#endif
	} @finally {
		of_free(buffer);
	}

	return ret;
}

- (OFString *)localDateStringWithFormat: (OFConstantString *)format
{
	OFString *ret;
	of_time_interval_t timeInterval = self.timeIntervalSince1970;
	time_t seconds = (time_t)timeInterval;
	struct tm tm;
	size_t pageSize;
#ifndef OF_WINDOWS
	char *buffer;
#else
	wchar_t *buffer;







|














|








|







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
	} @finally {
		[mutex unlock];
	}
# endif
#endif

	pageSize = [OFSystemInfo pageSize];
	buffer = OFAllocMemory(1, pageSize);
	@try {
#ifndef OF_WINDOWS
		if (strftime(buffer, pageSize, format.UTF8String, &tm) == 0)
			@throw [OFOutOfRangeException exception];

		ret = [OFString stringWithUTF8String: buffer];
#else
		if (wcsftime(buffer, pageSize / sizeof(wchar_t),
		    format.UTF16String, &tm) == 0)
			@throw [OFOutOfRangeException exception];

		ret = [OFString stringWithUTF16String: buffer];
#endif
	} @finally {
		OFFreeMemory(buffer);
	}

	return ret;
}

- (OFString *)localDateStringWithFormat: (OFConstantString *)format
{
	OFString *ret;
	OFTimeInterval timeInterval = self.timeIntervalSince1970;
	time_t seconds = (time_t)timeInterval;
	struct tm tm;
	size_t pageSize;
#ifndef OF_WINDOWS
	char *buffer;
#else
	wchar_t *buffer;
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
	} @finally {
		[mutex unlock];
	}
# endif
#endif

	pageSize = [OFSystemInfo pageSize];
	buffer = of_malloc(1, pageSize);
	@try {
#ifndef OF_WINDOWS
		if (strftime(buffer, pageSize, format.UTF8String, &tm) == 0)
			@throw [OFOutOfRangeException exception];

		ret = [OFString stringWithUTF8String: buffer];
#else
		if (wcsftime(buffer, pageSize / sizeof(wchar_t),
		    format.UTF16String, &tm) == 0)
			@throw [OFOutOfRangeException exception];

		ret = [OFString stringWithUTF16String: buffer];
#endif
	} @finally {
		of_free(buffer);
	}

	return ret;
}

- (OFDate *)earlierDate: (OFDate *)otherDate
{
	if (otherDate == nil)
		return self;

	if ([self compare: otherDate] == OF_ORDERED_DESCENDING)
		return otherDate;

	return self;
}

- (OFDate *)laterDate: (OFDate *)otherDate
{
	if (otherDate == nil)
		return self;

	if ([self compare: otherDate] == OF_ORDERED_ASCENDING)
		return otherDate;

	return self;
}

- (of_time_interval_t)timeIntervalSince1970
{
	return _seconds;
}

- (of_time_interval_t)timeIntervalSinceDate: (OFDate *)otherDate
{
	return self.timeIntervalSince1970 - otherDate.timeIntervalSince1970;
}

- (of_time_interval_t)timeIntervalSinceNow
{
	struct timeval t;
	of_time_interval_t seconds;

	OF_ENSURE(gettimeofday(&t, NULL) == 0);

	seconds = t.tv_sec;
	seconds += (of_time_interval_t)t.tv_usec / 1000000;

	return self.timeIntervalSince1970 - seconds;
}

- (OFDate *)dateByAddingTimeInterval: (of_time_interval_t)seconds
{
	return [OFDate dateWithTimeIntervalSince1970:
	    self.timeIntervalSince1970 + seconds];
}
@end







|














|










|










|





|




|




|


|

|


|




|





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
	} @finally {
		[mutex unlock];
	}
# endif
#endif

	pageSize = [OFSystemInfo pageSize];
	buffer = OFAllocMemory(1, pageSize);
	@try {
#ifndef OF_WINDOWS
		if (strftime(buffer, pageSize, format.UTF8String, &tm) == 0)
			@throw [OFOutOfRangeException exception];

		ret = [OFString stringWithUTF8String: buffer];
#else
		if (wcsftime(buffer, pageSize / sizeof(wchar_t),
		    format.UTF16String, &tm) == 0)
			@throw [OFOutOfRangeException exception];

		ret = [OFString stringWithUTF16String: buffer];
#endif
	} @finally {
		OFFreeMemory(buffer);
	}

	return ret;
}

- (OFDate *)earlierDate: (OFDate *)otherDate
{
	if (otherDate == nil)
		return self;

	if ([self compare: otherDate] == OFOrderedDescending)
		return otherDate;

	return self;
}

- (OFDate *)laterDate: (OFDate *)otherDate
{
	if (otherDate == nil)
		return self;

	if ([self compare: otherDate] == OFOrderedAscending)
		return otherDate;

	return self;
}

- (OFTimeInterval)timeIntervalSince1970
{
	return _seconds;
}

- (OFTimeInterval)timeIntervalSinceDate: (OFDate *)otherDate
{
	return self.timeIntervalSince1970 - otherDate.timeIntervalSince1970;
}

- (OFTimeInterval)timeIntervalSinceNow
{
	struct timeval t;
	OFTimeInterval seconds;

	OFEnsure(gettimeofday(&t, NULL) == 0);

	seconds = t.tv_sec;
	seconds += (OFTimeInterval)t.tv_usec / 1000000;

	return self.timeIntervalSince1970 - seconds;
}

- (OFDate *)dateByAddingTimeInterval: (OFTimeInterval)seconds
{
	return [OFDate dateWithTimeIntervalSince1970:
	    self.timeIntervalSince1970 + seconds];
}
@end

Modified src/OFDictionary.h from [ce711756a9] to [a605581e45].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
32
33
34
35
36
37
38








39
40







41








42
43
44
45
46
47
48
49
#import "OFMessagePackRepresentation.h"

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);

#ifdef OF_HAVE_BLOCKS








typedef void (^of_dictionary_enumeration_block_t)(id key, id object,
     bool *stop);







typedef bool (^of_dictionary_filter_block_t)(id key, id object);








typedef id _Nonnull (^of_dictionary_map_block_t)(id key, id object);
#endif

/**
 * @class OFDictionary OFDictionary.h ObjFW/OFDictionary.h
 *
 * @brief An abstract class for storing objects in a dictionary.
 *







>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
|







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
#import "OFMessagePackRepresentation.h"

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block for enumerating an OFDictionary.
 *
 * @param key The current key
 * @param object The object for the current key
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration.
 */
typedef void (^OFDictionaryEnumerationBlock)(id key, id object, bool *stop);

/**
 * @brief A block for filtering an OFDictionary.
 *
 * @param key The key to inspect
 * @param object The object for the key to inspect
 * @return Whether the object should be in the filtered dictionary.
 */
typedef bool (^OFDictionaryFilterBlock)(id key, id object);

/**
 * @brief A block for mapping keys to objects in an OFDictionary.
 *
 * @param key The key to map
 * @param object The current object for the key
 * @return The object to map the key to
 */
typedef id _Nonnull (^OFDictionaryMapBlock)(id key, id object);
#endif

/**
 * @class OFDictionary OFDictionary.h ObjFW/OFDictionary.h
 *
 * @brief An abstract class for storing objects in a dictionary.
 *
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
/**
 * @brief Creates a new OFDictionary with the specified key and object.
 *
 * @param key The key
 * @param object The object
 * @return A new autoreleased OFDictionary
 */
+ (instancetype)dictionaryWithObject: (ObjectType)object
			      forKey: (KeyType)key;

/**
 * @brief Creates a new OFDictionary with the specified keys and objects.
 *
 * @param keys An array of keys
 * @param objects An array of objects
 * @return A new autoreleased OFDictionary
 */
+ (instancetype)
    dictionaryWithObjects: (OFArray OF_GENERIC(ObjectType) *)objects
		  forKeys: (OFArray OF_GENERIC(KeyType) *)keys;

/**
 * @brief Creates a new OFDictionary with the specified keys and objects.
 *
 * @param keys An array of keys
 * @param objects An array of objects
 * @param count The number of objects in the arrays







|
<








<
|
|







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 Creates a new OFDictionary with the specified key and object.
 *
 * @param key The key
 * @param object The object
 * @return A new autoreleased OFDictionary
 */
+ (instancetype)dictionaryWithObject: (ObjectType)object forKey: (KeyType)key;


/**
 * @brief Creates a new OFDictionary with the specified keys and objects.
 *
 * @param keys An array of keys
 * @param objects An array of objects
 * @return A new autoreleased OFDictionary
 */

+ (instancetype)dictionaryWithObjects: (OFArray OF_GENERIC(ObjectType) *)objects
			      forKeys: (OFArray OF_GENERIC(KeyType) *)keys;

/**
 * @brief Creates a new OFDictionary with the specified keys and objects.
 *
 * @param keys An array of keys
 * @param objects An array of objects
 * @param count The number of objects in the arrays
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
 * @brief Initializes an already allocated OFDictionary with the specified key
 *	  and object.
 *
 * @param key The key
 * @param object The object
 * @return An initialized OFDictionary
 */
- (instancetype)initWithObject: (ObjectType)object
			forKey: (KeyType)key;

/**
 * @brief Initializes an already allocated OFDictionary with the specified keys
 *	  and objects.
 *
 * @param keys An array of keys
 * @param objects An array of objects







|
<







169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
 * @brief Initializes an already allocated OFDictionary with the specified key
 *	  and object.
 *
 * @param key The key
 * @param object The object
 * @return An initialized OFDictionary
 */
- (instancetype)initWithObject: (ObjectType)object forKey: (KeyType)key;


/**
 * @brief Initializes an already allocated OFDictionary with the specified keys
 *	  and objects.
 *
 * @param keys An array of keys
 * @param objects An array of objects
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
 * @brief Initializes an already allocated OFDictionary with the specified key
 *	  and va_list.
 *
 * @param firstKey The first key
 * @param arguments A va_list of the other arguments
 * @return An initialized OFDictionary
 */
- (instancetype)initWithKey: (KeyType)firstKey
		  arguments: (va_list)arguments;

/**
 * @brief Returns the object for the given key or `nil` if the key was not
 *	  found.
 *
 * @warning The returned object is *not* retained and autoreleased for
 *	    performance reasons!







|
<







212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
 * @brief Initializes an already allocated OFDictionary with the specified key
 *	  and va_list.
 *
 * @param firstKey The first key
 * @param arguments A va_list of the other arguments
 * @return An initialized OFDictionary
 */
- (instancetype)initWithKey: (KeyType)firstKey arguments: (va_list)arguments;


/**
 * @brief Returns the object for the given key or `nil` if the key was not
 *	  found.
 *
 * @warning The returned object is *not* retained and autoreleased for
 *	    performance reasons!
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
 *
 * 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.
 *
 * @param object The object which is checked for being in the dictionary
 * @return A boolean whether the dictionary contains the specified object







|
<







249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
 *
 * 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.
 *
 * @param object The object which is checked for being in the dictionary
 * @return A boolean whether the dictionary contains the specified object
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

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each key / object pair.
 *
 * @param block The block to execute for each key / object pair.
 */
- (void)enumerateKeysAndObjectsUsingBlock:
    (of_dictionary_enumeration_block_t)block;

/**
 * @brief Creates a new dictionary, mapping each object using the specified
 *	  block.
 *
 * @param block A block which maps an object for each object
 * @return A new autoreleased OFDictionary
 */
- (OFDictionary OF_GENERIC(KeyType, id) *)mappedDictionaryUsingBlock:
    (of_dictionary_map_block_t)block;

/**
 * @brief Creates a new dictionary, only containing the objects for which the
 *	  block returns true.
 *
 * @param block A block which determines if the object should be in the new
 *		dictionary
 * @return A new autoreleased OFDictionary
 */
- (OFDictionary OF_GENERIC(KeyType, ObjectType) *)filteredDictionaryUsingBlock:
    (of_dictionary_filter_block_t)block;
#endif
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef KeyType
# undef ObjectType
#endif
@end








|
<








|
|









|
|







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

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each key / object pair.
 *
 * @param block The block to execute for each key / object pair.
 */
- (void)enumerateKeysAndObjectsUsingBlock: (OFDictionaryEnumerationBlock)block;


/**
 * @brief Creates a new dictionary, mapping each object using the specified
 *	  block.
 *
 * @param block A block which maps an object for each object
 * @return A new autoreleased OFDictionary
 */
- (OFDictionary OF_GENERIC(KeyType, id) *)
    mappedDictionaryUsingBlock: (OFDictionaryMapBlock)block;

/**
 * @brief Creates a new dictionary, only containing the objects for which the
 *	  block returns true.
 *
 * @param block A block which determines if the object should be in the new
 *		dictionary
 * @return A new autoreleased OFDictionary
 */
- (OFDictionary OF_GENERIC(KeyType, ObjectType) *)
    filteredDictionaryUsingBlock: (OFDictionaryFilterBlock)block;
#endif
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef KeyType
# undef ObjectType
#endif
@end

Modified src/OFDictionary.m from [505aa3e10f] to [a48510c034].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
static struct {
	Class isa;
} placeholder;

static OFCharacterSet *URLQueryPartAllowedCharacterSet = nil;

@interface OFDictionary ()
- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth;
@end

@interface OFDictionaryPlaceholder: OFDictionary
@end

OF_DIRECT_MEMBERS
@interface OFDictionaryObjectEnumerator: OFEnumerator







|
>
|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
static struct {
	Class isa;
} placeholder;

static OFCharacterSet *URLQueryPartAllowedCharacterSet = nil;

@interface OFDictionary ()
- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth;
@end

@interface OFDictionaryPlaceholder: OFDictionary
@end

OF_DIRECT_MEMBERS
@interface OFDictionaryObjectEnumerator: OFEnumerator
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

- (instancetype)initWithDictionary: (OFDictionary *)dictionary
{
	return (id)[[OFMapTableDictionary alloc]
	    initWithDictionary: dictionary];
}

- (instancetype)initWithObject: (id)object
			forKey: (id)key
{
	return (id)[[OFMapTableDictionary alloc] initWithObject: object
							 forKey: key];
}

- (instancetype)initWithObjects: (OFArray *)objects
			forKeys: (OFArray *)keys
{
	return (id)[[OFMapTableDictionary alloc] initWithObjects: objects
							 forKeys: keys];
}

- (instancetype)initWithObjects: (id const *)objects
			forKeys: (id const *)keys







|
<





|
<







70
71
72
73
74
75
76
77

78
79
80
81
82
83

84
85
86
87
88
89
90

- (instancetype)initWithDictionary: (OFDictionary *)dictionary
{
	return (id)[[OFMapTableDictionary alloc]
	    initWithDictionary: dictionary];
}

- (instancetype)initWithObject: (id)object forKey: (id)key

{
	return (id)[[OFMapTableDictionary alloc] initWithObject: object
							 forKey: key];
}

- (instancetype)initWithObjects: (OFArray *)objects forKeys: (OFArray *)keys

{
	return (id)[[OFMapTableDictionary alloc] initWithObjects: objects
							 forKeys: keys];
}

- (instancetype)initWithObjects: (id const *)objects
			forKeys: (id const *)keys
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

- (void)release
{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':







|


|

|







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

- (bool)characterIsMember: (OFUnichar)character
{
	if (character < CHAR_MAX && OFASCIIIsAlnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':
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

+ (instancetype)dictionaryWithDictionary: (OFDictionary *)dictionary
{
	return [[(OFDictionary *)[self alloc]
	    initWithDictionary: dictionary] autorelease];
}

+ (instancetype)dictionaryWithObject: (id)object
			      forKey: (id)key
{
	return [[[self alloc] initWithObject: object
				      forKey: key] autorelease];
}

+ (instancetype)dictionaryWithObjects: (OFArray *)objects
			      forKeys: (OFArray *)keys
{
	return [[[self alloc] initWithObjects: objects
				      forKeys: keys] autorelease];
}

+ (instancetype)dictionaryWithObjects: (id const *)objects
			      forKeys: (id const *)keys
		  count: (size_t)count
{
	return [[[self alloc] initWithObjects: objects
				      forKeys: keys
					count: count] autorelease];
}

+ (instancetype)dictionaryWithKeysAndObjects: (id)firstKey, ...







|
<

|
<











|







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

+ (instancetype)dictionaryWithDictionary: (OFDictionary *)dictionary
{
	return [[(OFDictionary *)[self alloc]
	    initWithDictionary: dictionary] autorelease];
}

+ (instancetype)dictionaryWithObject: (id)object forKey: (id)key

{
	return [[[self alloc] initWithObject: object forKey: key] autorelease];

}

+ (instancetype)dictionaryWithObjects: (OFArray *)objects
			      forKeys: (OFArray *)keys
{
	return [[[self alloc] initWithObjects: objects
				      forKeys: keys] autorelease];
}

+ (instancetype)dictionaryWithObjects: (id const *)objects
			      forKeys: (id const *)keys
				count: (size_t)count
{
	return [[[self alloc] initWithObjects: objects
				      forKeys: keys
					count: count] autorelease];
}

+ (instancetype)dictionaryWithKeysAndObjects: (id)firstKey, ...
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
}

- (instancetype)initWithDictionary: (OFDictionary *)dictionary
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object
			forKey: (id)key
{
	if (key == nil || object == nil)
		@throw [OFInvalidArgumentException exception];

	return [self initWithKeysAndObjects: key, object, nil];
}

- (instancetype)initWithObjects: (OFArray *)objects_
			forKeys: (OFArray *)keys_
{
	id const *objects, *keys;
	size_t count;

	@try {
		count = objects_.count;

		if (count != keys_.count)
			@throw [OFInvalidArgumentException exception];

		objects = objects_.objects;
		keys = keys_.objects;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return [self initWithObjects: objects
			     forKeys: keys
			       count: count];
}

- (instancetype)initWithObjects: (id const *)objects
			forKeys: (id const *)keys
			  count: (size_t)count
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithKeysAndObjects: (id)firstKey, ...
{
	id ret;
	va_list arguments;

	va_start(arguments, firstKey);
	ret = [self initWithKey: firstKey
		      arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithKey: (id)firstKey
		  arguments: (va_list)arguments
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	OF_INVALID_INIT_METHOD







|
<







|
<

















|
<
<















|
<





|
<







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
}

- (instancetype)initWithDictionary: (OFDictionary *)dictionary
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object forKey: (id)key

{
	if (key == nil || object == nil)
		@throw [OFInvalidArgumentException exception];

	return [self initWithKeysAndObjects: key, object, nil];
}

- (instancetype)initWithObjects: (OFArray *)objects_ forKeys: (OFArray *)keys_

{
	id const *objects, *keys;
	size_t count;

	@try {
		count = objects_.count;

		if (count != keys_.count)
			@throw [OFInvalidArgumentException exception];

		objects = objects_.objects;
		keys = keys_.objects;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return [self initWithObjects: objects forKeys: keys count: count];


}

- (instancetype)initWithObjects: (id const *)objects
			forKeys: (id const *)keys
			  count: (size_t)count
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithKeysAndObjects: (id)firstKey, ...
{
	id ret;
	va_list arguments;

	va_start(arguments, firstKey);
	ret = [self initWithKey: firstKey arguments: arguments];

	va_end(arguments);

	return ret;
}

- (instancetype)initWithKey: (id)firstKey arguments: (va_list)arguments

{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	OF_INVALID_INIT_METHOD
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
{
	if ([key isEqual: @"@count"])
		return [super valueForKey: @"count"];

	return [self objectForKey: key];
}

- (void)setValue: (id)value
	  forKey: (OFString *)key
{
	if (![self isKindOfClass: [OFMutableDictionary class]])
		@throw [OFUndefinedKeyException exceptionWithObject: self
								key: key
							      value: value];

	[(OFMutableDictionary *)self setObject: value
					forKey: key];
}

- (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}








|
<






|
<







354
355
356
357
358
359
360
361

362
363
364
365
366
367
368

369
370
371
372
373
374
375
{
	if ([key isEqual: @"@count"])
		return [super valueForKey: @"count"];

	return [self objectForKey: key];
}

- (void)setValue: (id)value forKey: (OFString *)key

{
	if (![self isKindOfClass: [OFMutableDictionary class]])
		@throw [OFUndefinedKeyException exceptionWithObject: self
								key: key
							      value: value];

	[(OFMutableDictionary *)self setObject: value forKey: key];

}

- (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

517
518
519
520
521
522
523
524
525
526
527
528
529
530
531

- (OFEnumerator *)objectEnumerator
{
	return [[[OFDictionaryObjectEnumerator alloc]
	    initWithDictionary: self] autorelease];
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	OFEnumerator *enumerator;
	int i;

	memcpy(&enumerator, state->extra, sizeof(enumerator));







|







504
505
506
507
508
509
510
511
512
513
514
515
516
517
518

- (OFEnumerator *)objectEnumerator
{
	return [[[OFDictionaryObjectEnumerator alloc]
	    initWithDictionary: self] autorelease];
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	OFEnumerator *enumerator;
	int i;

	memcpy(&enumerator, state->extra, sizeof(enumerator));
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
		objects[i] = object;
	}

	return i;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateKeysAndObjectsUsingBlock:
    (of_dictionary_enumeration_block_t)block
{
	bool stop = false;

	for (id key in self) {
		block(key, [self objectForKey: key], &stop);

		if (stop)
			break;
	}
}

- (OFDictionary *)mappedDictionaryUsingBlock: (of_dictionary_map_block_t)block
{
	OFMutableDictionary *new = [OFMutableDictionary dictionary];

	[self enumerateKeysAndObjectsUsingBlock: ^ (id key, id object,
	    bool *stop) {
		[new setObject: block(key, object)
			forKey: key];
	}];

	[new makeImmutable];

	return new;
}

- (OFDictionary *)filteredDictionaryUsingBlock:
    (of_dictionary_filter_block_t)block
{
	OFMutableDictionary *new = [OFMutableDictionary dictionary];

	[self enumerateKeysAndObjectsUsingBlock: ^ (id key, id object,
	    bool *stop) {
		if (block(key, object))
			[new setObject: object
				forKey: key];
	}];

	[new makeImmutable];

	return new;
}
#endif







|
<











|





|
<







|
<






|
<







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
		objects[i] = object;
	}

	return i;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateKeysAndObjectsUsingBlock: (OFDictionaryEnumerationBlock)block

{
	bool stop = false;

	for (id key in self) {
		block(key, [self objectForKey: key], &stop);

		if (stop)
			break;
	}
}

- (OFDictionary *)mappedDictionaryUsingBlock: (OFDictionaryMapBlock)block
{
	OFMutableDictionary *new = [OFMutableDictionary dictionary];

	[self enumerateKeysAndObjectsUsingBlock: ^ (id key, id object,
	    bool *stop) {
		[new setObject: block(key, object) forKey: key];

	}];

	[new makeImmutable];

	return new;
}

- (OFDictionary *)filteredDictionaryUsingBlock: (OFDictionaryFilterBlock)block

{
	OFMutableDictionary *new = [OFMutableDictionary dictionary];

	[self enumerateKeysAndObjectsUsingBlock: ^ (id key, id object,
	    bool *stop) {
		if (block(key, object))
			[new setObject: object forKey: key];

	}];

	[new makeImmutable];

	return new;
}
#endif
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
		[ret appendString: object.description];

		if (++i < count)
			[ret appendString: @";\n"];

		objc_autoreleasePoolPop(pool2);
	}
	[ret replaceOccurrencesOfString: @"\n"
			     withString: @"\n\t"];
	[ret appendString: @";\n}"];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;







|
<







625
626
627
628
629
630
631
632

633
634
635
636
637
638
639
		[ret appendString: object.description];

		if (++i < count)
			[ret appendString: @";\n"];

		objc_autoreleasePoolPop(pool2);
	}
	[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];

	[ret appendString: @";\n}"];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
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
	void *pool = objc_autoreleasePoolPush();
	OFXMLElement *element;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	id <OFSerialization> key, object;

	if ([self isKindOfClass: [OFMutableDictionary class]])
		element = [OFXMLElement elementWithName: @"OFMutableDictionary"
					      namespace: OF_SERIALIZATION_NS];
	else
		element = [OFXMLElement elementWithName: @"OFDictionary"
					      namespace: OF_SERIALIZATION_NS];

	keyEnumerator = [self keyEnumerator];
	objectEnumerator = [self objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	       (object = [objectEnumerator nextObject]) != nil) {
		void *pool2 = objc_autoreleasePoolPush();
		OFXMLElement *keyElement, *objectElement;

		keyElement = [OFXMLElement
		    elementWithName: @"key"
			  namespace: OF_SERIALIZATION_NS];
		[keyElement addChild: key.XMLElementBySerializing];

		objectElement = [OFXMLElement
		    elementWithName: @"object"
			  namespace: OF_SERIALIZATION_NS];
		[objectElement addChild: object.XMLElementBySerializing];

		[element addChild: keyElement];
		[element addChild: objectElement];

		objc_autoreleasePoolPop(pool2);
	}

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0
						depth: 0];
}

- (OFString *)JSONRepresentationWithOptions: (int)options

{
	return [self of_JSONRepresentationWithOptions: options
						depth: 0];
}

- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth
{
	OFMutableString *JSON = [OFMutableString stringWithString: @"{"];
	void *pool = objc_autoreleasePoolPush();
	OFEnumerator *keyEnumerator = [self keyEnumerator];
	OFEnumerator *objectEnumerator = [self objectEnumerator];
	size_t i, count = self.count;
	id key, object;

	if (options & OF_JSON_REPRESENTATION_PRETTY) {
		OFMutableString *indentation = [OFMutableString string];

		for (i = 0; i < depth; i++)
			[indentation appendString: @"\t"];

		[JSON appendString: @"\n"];

		i = 0;
		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil) {
			void *pool2 = objc_autoreleasePoolPush();
			int identifierOptions =
			    options | OF_JSON_REPRESENTATION_IDENTIFIER;

			if (![key isKindOfClass: [OFString class]])
				@throw [OFInvalidArgumentException exception];

			[JSON appendString: indentation];
			[JSON appendString: @"\t"];
			[JSON appendString: [key







|


|










|




|

















|
<


|
>

|
<


|
>
|








|












|







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
	void *pool = objc_autoreleasePoolPush();
	OFXMLElement *element;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	id <OFSerialization> key, object;

	if ([self isKindOfClass: [OFMutableDictionary class]])
		element = [OFXMLElement elementWithName: @"OFMutableDictionary"
					      namespace: OFSerializationNS];
	else
		element = [OFXMLElement elementWithName: @"OFDictionary"
					      namespace: OFSerializationNS];

	keyEnumerator = [self keyEnumerator];
	objectEnumerator = [self objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	       (object = [objectEnumerator nextObject]) != nil) {
		void *pool2 = objc_autoreleasePoolPush();
		OFXMLElement *keyElement, *objectElement;

		keyElement = [OFXMLElement
		    elementWithName: @"key"
			  namespace: OFSerializationNS];
		[keyElement addChild: key.XMLElementBySerializing];

		objectElement = [OFXMLElement
		    elementWithName: @"object"
			  namespace: OFSerializationNS];
		[objectElement addChild: object.XMLElementBySerializing];

		[element addChild: keyElement];
		[element addChild: objectElement];

		objc_autoreleasePoolPop(pool2);
	}

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0 depth: 0];

}

- (OFString *)JSONRepresentationWithOptions:
    (OFJSONRepresentationOptions)options
{
	return [self of_JSONRepresentationWithOptions: options depth: 0];

}

- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth
{
	OFMutableString *JSON = [OFMutableString stringWithString: @"{"];
	void *pool = objc_autoreleasePoolPush();
	OFEnumerator *keyEnumerator = [self keyEnumerator];
	OFEnumerator *objectEnumerator = [self objectEnumerator];
	size_t i, count = self.count;
	id key, object;

	if (options & OFJSONRepresentationOptionPretty) {
		OFMutableString *indentation = [OFMutableString string];

		for (i = 0; i < depth; i++)
			[indentation appendString: @"\t"];

		[JSON appendString: @"\n"];

		i = 0;
		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil) {
			void *pool2 = objc_autoreleasePoolPush();
			int identifierOptions =
			    options | OFJSONRepresentationOptionIsIdentifier;

			if (![key isKindOfClass: [OFString class]])
				@throw [OFInvalidArgumentException exception];

			[JSON appendString: indentation];
			[JSON appendString: @"\t"];
			[JSON appendString: [key
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
		[JSON appendString: indentation];
	} else {
		i = 0;
		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil) {
			void *pool2 = objc_autoreleasePoolPush();
			int identifierOptions =
			    options | OF_JSON_REPRESENTATION_IDENTIFIER;

			if (![key isKindOfClass: [OFString class]])
				@throw [OFInvalidArgumentException exception];

			[JSON appendString: [key
			    of_JSONRepresentationWithOptions: identifierOptions
						       depth: depth + 1]];







|







776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
		[JSON appendString: indentation];
	} else {
		i = 0;
		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil) {
			void *pool2 = objc_autoreleasePoolPush();
			int identifierOptions =
			    options | OFJSONRepresentationOptionIsIdentifier;

			if (![key isKindOfClass: [OFString class]])
				@throw [OFInvalidArgumentException exception];

			[JSON appendString: [key
			    of_JSONRepresentationWithOptions: identifierOptions
						       depth: depth + 1]];
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
	count = self.count;

	if (count <= 15) {
		uint8_t tmp = 0x80 | ((uint8_t)count & 0xF);
		[data addItem: &tmp];
	} else if (count <= UINT16_MAX) {
		uint8_t type = 0xDE;
		uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)count);

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else if (count <= UINT32_MAX) {
		uint8_t type = 0xDF;
		uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)count);

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else
		@throw [OFOutOfRangeException exception];

	pool = objc_autoreleasePoolPush();

	i = 0;
	keyEnumerator = [self keyEnumerator];
	objectEnumerator = [self objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil) {
		void *pool2 = objc_autoreleasePoolPush();
		OFData *child;

		i++;

		child = key.messagePackRepresentation;
		[data addItems: child.items
			 count: child.count];

		child = object.messagePackRepresentation;
		[data addItems: child.items
			 count: child.count];

		objc_autoreleasePoolPop(pool2);
	}

	assert(i == count);

	[data makeImmutable];







|


|
<


|


|
<
















|
<


|
<







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
	count = self.count;

	if (count <= 15) {
		uint8_t tmp = 0x80 | ((uint8_t)count & 0xF);
		[data addItem: &tmp];
	} else if (count <= UINT16_MAX) {
		uint8_t type = 0xDE;
		uint16_t tmp = OFToBigEndian16((uint16_t)count);

		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else if (count <= UINT32_MAX) {
		uint8_t type = 0xDF;
		uint32_t tmp = OFToBigEndian32((uint32_t)count);

		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else
		@throw [OFOutOfRangeException exception];

	pool = objc_autoreleasePoolPush();

	i = 0;
	keyEnumerator = [self keyEnumerator];
	objectEnumerator = [self objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil) {
		void *pool2 = objc_autoreleasePoolPush();
		OFData *child;

		i++;

		child = key.messagePackRepresentation;
		[data addItems: child.items count: child.count];


		child = object.messagePackRepresentation;
		[data addItems: child.items count: child.count];


		objc_autoreleasePoolPop(pool2);
	}

	assert(i == count);

	[data makeImmutable];

Modified src/OFEnumerator.h from [234c3f2385] to [514f228b0c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFEnumerator OF_GENERIC(ObjectType);

/**
 * @protocol OFEnumerating OFEnumerator.h ObjFW/OFEnumerator.h
 *
 * @brief A protocol for getting an enumerator for the object.




 */
@protocol OFEnumerating
/**
 * @brief Returns an OFEnumerator to enumerate through all objects of the
 *	  collection.
 *
 * @return An OFEnumerator to enumerate through all objects of the collection
 */
- (OFEnumerator *)objectEnumerator;
@end

/*
 * This needs to be exactly like this because it's hard-coded in the compiler.
 *
 * We need this bad check to see if we already imported Cocoa, which defines
 * this as well.
 */
/**
 * @struct of_fast_enumeration_state_t OFEnumerator.h ObjFW/OFEnumerator.h
 *
 * @brief State information for fast enumerations.
 */
#define of_fast_enumeration_state_t NSFastEnumerationState
#ifndef NSINTEGER_DEFINED
typedef struct {
	/** Arbitrary state information for the enumeration */
	unsigned long state;
	/** Pointer to a C array of objects to return */
	id __unsafe_unretained _Nullable *_Nullable itemsPtr;
	/** Arbitrary state information to detect mutations */
	unsigned long *_Nullable mutationsPtr;
	/** Additional arbitrary state information */
	unsigned long extra[5];
} of_fast_enumeration_state_t;


#endif

/**
 * @protocol OFFastEnumeration OFEnumerator.h ObjFW/OFEnumerator.h
 *
 * @brief A protocol for fast enumeration.
 *







|


>
>
>
>

|
















|



<
<









|
>
>







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

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFEnumerator OF_GENERIC(ObjectType);

/**
 * @protocol OFEnumeration OFEnumerator.h ObjFW/OFEnumerator.h
 *
 * @brief A protocol for getting an enumerator for the object.
 *
 * If the class conforming to OFEnumeration is using lightweight generics, the
 * only method, @ref objectEnumerator, should be overridden to use lightweight
 * generics.
 */
@protocol OFEnumeration
/**
 * @brief Returns an OFEnumerator to enumerate through all objects of the
 *	  collection.
 *
 * @return An OFEnumerator to enumerate through all objects of the collection
 */
- (OFEnumerator *)objectEnumerator;
@end

/*
 * This needs to be exactly like this because it's hard-coded in the compiler.
 *
 * We need this bad check to see if we already imported Cocoa, which defines
 * this as well.
 */
/**
 * @struct OFFastEnumerationState OFEnumerator.h ObjFW/OFEnumerator.h
 *
 * @brief State information for fast enumerations.
 */


typedef struct {
	/** Arbitrary state information for the enumeration */
	unsigned long state;
	/** Pointer to a C array of objects to return */
	id __unsafe_unretained _Nullable *_Nullable itemsPtr;
	/** Arbitrary state information to detect mutations */
	unsigned long *_Nullable mutationsPtr;
	/** Additional arbitrary state information */
	unsigned long extra[5];
} OFFastEnumerationState;
#ifndef NSINTEGER_DEFINED
typedef OFFastEnumerationState NSFastEnumerationState;
#endif

/**
 * @protocol OFFastEnumeration OFEnumerator.h ObjFW/OFEnumerator.h
 *
 * @brief A protocol for fast enumeration.
 *
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
 *
 * @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: (of_fast_enumeration_state_t *)state
			   objects: (id __unsafe_unretained _Nonnull *_Nonnull)
					objects
			     count: (int)count;
@end

/**
 * @class OFEnumerator OFEnumerator.h ObjFW/OFEnumerator.h







|







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
 *
 * @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

Modified src/OFEnumerator.m from [47ad878ce8] to [bb10b4629e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	int i;

	state->itemsPtr = objects;
	state->mutationsPtr = (unsigned long *)self;







|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	int i;

	state->itemsPtr = objects;
	state->mutationsPtr = (unsigned long *)self;

Modified src/OFEpollKernelEventObserver.h from [10f634c6df] to [d3c4bbd7da].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFEpollKernelEventObserver.m from [5306d06560] to [ddd2075d1e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#import "OFArray.h"
#import "OFMapTable.h"
#import "OFNull.h"

#import "OFInitializationFailedException.h"
#import "OFObserveFailedException.h"

#define EVENTLIST_SIZE 64

static const of_map_table_functions_t mapFunctions = { NULL };

@implementation OFEpollKernelEventObserver
- (instancetype)init
{
	self = [super init];

	@try {







|

|







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#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
{
	self = [super init];

	@try {
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
	[self of_removeObject: object
	       fileDescriptor: object.fileDescriptorForWriting
		       events: EPOLLOUT];

	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (of_time_interval_t)timeInterval
{
	OFNull *nullObject = [OFNull null];
	struct epoll_event eventList[EVENTLIST_SIZE];
	int events;

	if ([self of_processReadBuffers])
		return;

	events = epoll_wait(_epfd, eventList, EVENTLIST_SIZE,
	    (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;
				OF_ENSURE(read(_cancelFD[0], &buffer, 1) == 1);
				continue;
			}

			if ([_delegate respondsToSelector:
			    @selector(objectIsReadyForReading:)])
				[_delegate objectIsReadyForReading:
				    eventList[i].data.ptr];







|


|





|












|







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
	[self of_removeObject: object
	       fileDescriptor: object.fileDescriptorForWriting
		       events: EPOLLOUT];

	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (OFTimeInterval)timeInterval
{
	OFNull *nullObject = [OFNull null];
	struct epoll_event eventList[eventListSize];
	int events;

	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;
				OFEnsure(read(_cancelFD[0], &buffer, 1) == 1);
				continue;
			}

			if ([_delegate respondsToSelector:
			    @selector(objectIsReadyForReading:)])
				[_delegate objectIsReadyForReading:
				    eventList[i].data.ptr];

Modified src/OFFile.h from [d4ed29ae4f] to [70f5f785c7].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFSeekableStream.h"
#import "OFKernelEventObserver.h"

#ifndef OF_AMIGAOS
# define OF_FILE_HANDLE_IS_FD
# define OF_INVALID_FILE_HANDLE (-1)
typedef int of_file_handle_t;

#else
# define OF_INVALID_FILE_HANDLE NULL
typedef struct of_file_handle *of_file_handle_t;

#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
@interface OFFile: OFSeekableStream
#ifdef OF_FILE_HANDLE_IS_FD
    <OFReadyForReadingObserving, OFReadyForWritingObserving>
#endif
{
	of_file_handle_t _handle;
	bool _atEndOfStream;
}

/**
 * @brief Creates a new OFFile with the specified path and mode.
 *
 * @param path The path to the file to open as a string

<
<
|


















<
|
>

<
|
>

















|







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


 * 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 "OFSeekableStream.h"
#import "OFKernelEventObserver.h"

#ifndef OF_AMIGAOS
# define OF_FILE_HANDLE_IS_FD

typedef int OFFileHandle;
static const OFFileHandle OFInvalidFileHandle = -1;
#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
@interface OFFile: OFSeekableStream
#ifdef OF_FILE_HANDLE_IS_FD
    <OFReadyForReadingObserving, OFReadyForWritingObserving>
#endif
{
	OFFileHandle _handle;
	bool _atEndOfStream;
}

/**
 * @brief Creates a new OFFile with the specified path and mode.
 *
 * @param path The path to the file to open as a string
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
 *	       `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 URL and mode.
 *
 * @param URL The URL to the file to open
 * @param mode The mode in which the file should be opened.@n
 *	       Possible modes are:
 *	       Mode           | Description
 *	       ---------------|-------------------------------------
 *	       `r`            | Read-only
 *	       `r+`           | Read-write
 *	       `w`            | Write-only, create or truncate
 *	       `wx`           | Write-only, create or fail, exclusive
 *	       `w+`           | Read-write, create or truncate
 *	       `w+x`          | Read-write, create or fail, exclusive
 *	       `a`            | Write-only, create or append
 *	       `a+`           | Read-write, create or append
 * @return A new autoreleased OFFile
 */
+ (instancetype)fileWithURL: (OFURL *)URL
		       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
 *		 is a file descriptor. The handle is closed when the OFFile
 *		 object is deallocated!
 * @return A new autoreleased OFFile
 */
+ (instancetype)fileWithHandle: (of_file_handle_t)handle;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFFile.
 *
 * @param path The path to the file to open as a string
 * @param mode The mode in which the file should be opened.@n
 *	       Possible modes are:







|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<









|
<
<







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
 *	       `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
 *		 is a file descriptor. The handle is closed when the OFFile
 *		 object is deallocated!
 * @return A new autoreleased OFFile
 */
+ (instancetype)fileWithHandle: (OFFileHandle)handle;



/**
 * @brief Initializes an already allocated OFFile.
 *
 * @param path The path to the file to open as a string
 * @param mode The mode in which the file should be opened.@n
 *	       Possible modes are:
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
 *	       `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 URL The URL to the file to open
 * @param mode The mode in which the file should be opened.@n
 *	       Possible modes are:
 *	       Mode           | Description
 *	       ---------------|-------------------------------------
 *	       `r`            | read-only
 *	       `rb`           | read-only, binary
 *	       `r+`           | read-write
 *	       `rb+` or `r+b` | read-write, binary
 *	       `w`            | write-only, create, truncate
 *	       `wb`           | write-only, create, truncate, binary
 *	       `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)initWithURL: (OFURL *)URL
		       mode: (OFString *)mode;

/**
 * @brief Initializes an already allocated OFFile.
 *
 * @param handle A native file handle. If OF_FILE_HANDLE_IS_FD is defined, this
 *		 is a file descriptor. The handle is closed when the OFFile
 *		 object is deallocated!
 * @return An initialized OFFile
 */
- (instancetype)initWithHandle: (of_file_handle_t)handle
    OF_DESIGNATED_INITIALIZER;

@end

OF_ASSUME_NONNULL_END







|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<









|
|
>



93
94
95
96
97
98
99
100


























101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
 *	       `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
 *		 is a file descriptor. The handle is closed when the OFFile
 *		 object is deallocated!
 * @return An initialized OFFile
 */
- (instancetype)initWithHandle: (OFFileHandle)handle OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/OFFile.m from [211e9ba002] to [0786a18c44].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <assert.h>
#include <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

<
<
|














>
>







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


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

#define _LARGEFILE64_SOURCE

#include <assert.h>
#include <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
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
#ifndef O_EXLOCK
# define O_EXLOCK 0
#endif

#ifndef OF_AMIGAOS
# define closeHandle(h) close(h)
#else
static struct of_file_handle {
	of_file_handle_t previous, next;
	BPTR handle;
	bool append;
} *firstHandle = NULL;

static void
closeHandle(of_file_handle_t handle)
{
	Close(handle->handle);



	if (handle->previous != NULL)
		handle->previous->next = handle->next;
	if (handle->next != NULL)
		handle->next->previous = handle->previous;

	if (firstHandle == handle)
		firstHandle = handle->next;



	of_free(handle);
}

OF_DESTRUCTOR()
{
	for (of_file_handle_t iter = firstHandle; iter != NULL;
	    iter = iter->next)
		Close(iter->handle);
}
#endif

#ifndef OF_AMIGAOS
static int







|
|





|


>
>









>
>
|




|







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
#ifndef O_EXLOCK
# define O_EXLOCK 0
#endif

#ifndef OF_AMIGAOS
# define closeHandle(h) close(h)
#else
static struct _OFFileHandle {
	struct _OFFileHandle *previous, *next;
	BPTR handle;
	bool append;
} *firstHandle = NULL;

static void
closeHandle(OFFileHandle handle)
{
	Close(handle->handle);

	Forbid();

	if (handle->previous != NULL)
		handle->previous->next = handle->next;
	if (handle->next != NULL)
		handle->next->previous = handle->previous;

	if (firstHandle == handle)
		firstHandle = handle->next;

	Permit();

	OFFreeMemory(handle);
}

OF_DESTRUCTOR()
{
	for (OFFileHandle iter = firstHandle; iter != NULL;
	    iter = iter->next)
		Close(iter->handle);
}
#endif

#ifndef OF_AMIGAOS
static int
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
#ifdef OF_NINTENDO_DS
	if (!nitroFSInit(NULL))
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
#endif
}

+ (instancetype)fileWithPath: (OFString *)path
			mode: (OFString *)mode
{
	return [[[self alloc] initWithPath: path
				      mode: mode] autorelease];
}

+ (instancetype)fileWithURL: (OFURL *)URL
		       mode: (OFString *)mode
{
	return [[[self alloc] initWithURL: URL
				     mode: mode] autorelease];
}

+ (instancetype)fileWithHandle: (of_file_handle_t)handle
{
	return [[[self alloc] initWithHandle: handle] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPath: (OFString *)path
			mode: (OFString *)mode
{
	of_file_handle_t handle;

	@try {
		void *pool = objc_autoreleasePoolPush();
		int flags;

#ifndef OF_AMIGAOS
		if ((flags = parseMode(mode.UTF8String)) == -1)







|
<

|
<


<
<
<
<
<
<
<
|




<
<
<
<
<
|
<

|







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
#ifdef OF_NINTENDO_DS
	if (!nitroFSInit(NULL))
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
#endif
}

+ (instancetype)fileWithPath: (OFString *)path mode: (OFString *)mode

{
	return [[[self alloc] initWithPath: path mode: mode] autorelease];

}








+ (instancetype)fileWithHandle: (OFFileHandle)handle
{
	return [[[self alloc] initWithHandle: handle] autorelease];
}






- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode

{
	OFFileHandle handle;

	@try {
		void *pool = objc_autoreleasePoolPush();
		int flags;

#ifndef OF_AMIGAOS
		if ((flags = parseMode(mode.UTF8String)) == -1)
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

		if (handle == -1)
			@throw [OFOpenItemFailedException
			    exceptionWithPath: path
					 mode: mode
					errNo: errno];
#else
		handle = of_malloc(1, sizeof(*handle));
		@try {
			if ((flags = parseMode(mode.UTF8String,
			    &handle->append)) == -1)
				@throw [OFInvalidArgumentException exception];

			if ((handle->handle = Open([path cStringWithEncoding:
			    [OFLocale encoding]], flags)) == 0) {







|







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

		if (handle == -1)
			@throw [OFOpenItemFailedException
			    exceptionWithPath: path
					 mode: mode
					errNo: errno];
#else
		handle = OFAllocMemory(1, sizeof(*handle));
		@try {
			if ((flags = parseMode(mode.UTF8String,
			    &handle->append)) == -1)
				@throw [OFInvalidArgumentException exception];

			if ((handle->handle = Open([path cStringWithEncoding:
			    [OFLocale encoding]], flags)) == 0) {
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
					Close(handle->handle);
					@throw [OFOpenItemFailedException
					    exceptionWithPath: path
							 mode: mode
							errNo: EIO];
				}
			}



			handle->previous = NULL;
			handle->next = firstHandle;

			if (firstHandle != NULL)
				firstHandle->previous = handle;

			firstHandle = handle;


		} @catch (id e) {
			of_free(handle);
			@throw e;
		}
#endif

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	@try {
		self = [self initWithHandle: handle];
	} @catch (id e) {
		closeHandle(handle);
		@throw e;
	}

	return self;
}

- (instancetype)initWithURL: (OFURL *)URL
		       mode: (OFString *)mode
{
	void *pool = objc_autoreleasePoolPush();
	OFString *fileSystemRepresentation;

	@try {
		fileSystemRepresentation = URL.fileSystemRepresentation;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithPath: fileSystemRepresentation
			     mode: mode];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (instancetype)initWithHandle: (of_file_handle_t)handle
{
	self = [super init];

	_handle = handle;

	return self;
}






- (bool)lowlevelIsAtEndOfStream
{
	if (_handle == OF_INVALID_FILE_HANDLE)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	ssize_t ret;

	if (_handle == OF_INVALID_FILE_HANDLE)
		@throw [OFNotOpenException exceptionWithObject: self];

#if defined(OF_WINDOWS)
	if (length > UINT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((ret = read(_handle, buffer, (unsigned int)length)) < 0)







>
>








>
>

|




















<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|







>
>
>
>
>



|





|
<



|







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
					Close(handle->handle);
					@throw [OFOpenItemFailedException
					    exceptionWithPath: path
							 mode: mode
							errNo: EIO];
				}
			}

			Forbid();

			handle->previous = NULL;
			handle->next = firstHandle;

			if (firstHandle != NULL)
				firstHandle->previous = handle;

			firstHandle = handle;

			Permit();
		} @catch (id e) {
			OFFreeMemory(handle);
			@throw e;
		}
#endif

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	@try {
		self = [self initWithHandle: handle];
	} @catch (id e) {
		closeHandle(handle);
		@throw e;
	}

	return self;
}






















- (instancetype)initWithHandle: (OFFileHandle)handle
{
	self = [super init];

	_handle = handle;

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_handle == OFInvalidFileHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	ssize_t ret;

	if (_handle == OFInvalidFileHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

#if defined(OF_WINDOWS)
	if (length > UINT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((ret = read(_handle, buffer, (unsigned int)length)) < 0)
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	if (_handle == OF_INVALID_FILE_HANDLE)
		@throw [OFNotOpenException exceptionWithObject: self];

#if defined(OF_WINDOWS)
	int bytesWritten;

	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];







|
<

|







373
374
375
376
377
378
379
380

381
382
383
384
385
386
387
388
389

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{
	if (_handle == OFInvalidFileHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

#if defined(OF_WINDOWS)
	int bytesWritten;

	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
						      bytesWritten: 0
							     errNo: errno];
#endif

	return (size_t)bytesWritten;
}

- (of_offset_t)lowlevelSeekToOffset: (of_offset_t)offset
			     whence: (int)whence
{
	of_offset_t ret;

	if (_handle == OF_INVALID_FILE_HANDLE)
		@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);







<
|

|

|







431
432
433
434
435
436
437

438
439
440
441
442
443
444
445
446
447
448
449
						      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);
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
{
	return _handle;
}
#endif

- (void)close
{
	if (_handle == OF_INVALID_FILE_HANDLE)
		@throw [OFNotOpenException exceptionWithObject: self];

	closeHandle(_handle);
	_handle = OF_INVALID_FILE_HANDLE;

	[super close];
}

- (void)dealloc
{
	if (_handle != OF_INVALID_FILE_HANDLE)
		[self close];

	[super dealloc];
}
@end







|



|






|





505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
{
	return _handle;
}
#endif

- (void)close
{
	if (_handle == OFInvalidFileHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	closeHandle(_handle);
	_handle = OFInvalidFileHandle;

	[super close];
}

- (void)dealloc
{
	if (_handle != OFInvalidFileHandle)
		[self close];

	[super dealloc];
}
@end

Modified src/OFFileManager.h from [f8bf172385] to [e259c02d73].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@class OFURL;

/**
 * @brief A key for a file attribute in the file attributes dictionary.
 *
 * Possible keys for file URLs are:
 *
 *  * @ref of_file_attribute_key_size
 *  * @ref of_file_attribute_key_type
 *  * @ref of_file_attribute_key_posix_permissions
 *  * @ref of_file_attribute_key_posix_uid
 *  * @ref of_file_attribute_key_posix_gid
 *  * @ref of_file_attribute_key_owner
 *  * @ref of_file_attribute_key_group
 *  * @ref of_file_attribute_key_last_access_date
 *  * @ref of_file_attribute_key_modification_date
 *  * @ref of_file_attribute_key_status_change_date
 *  * @ref of_file_attribute_key_creation_date
 *  * @ref of_file_attribute_key_symbolic_link_destination
 *
 * Other URL schemes might not have all keys and might have keys not listed.
 */
typedef OFConstantString *of_file_attribute_key_t;

/**
 * @brief The type of a file.
 *
 * Possibles values for file URLs are:
 *
 *  * @ref of_file_type_regular
 *  * @ref of_file_type_directory
 *  * @ref of_file_type_symbolic_link
 *  * @ref of_file_type_fifo

 *  * @ref of_file_type_character_special
 *  * @ref of_file_type_block_special
 *  * @ref of_file_type_socket
 *
 * Other URL schemes might not have all types and might have types not listed.
 */
typedef OFConstantString *of_file_type_t;

/**
 * @brief A dictionary mapping keys of type @ref of_file_attribute_key_t
 *	  to their attribute values.
 */
typedef OFDictionary OF_GENERIC(of_file_attribute_key_t, id)
    *of_file_attributes_t;

/**
 * @brief A mutable dictionary mapping keys of type
 *	  @ref of_file_attribute_key_t to their attribute values.
 */
typedef OFMutableDictionary OF_GENERIC(of_file_attribute_key_t, id)
    *of_mutable_file_attributes_t;

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief The size of the file as an @ref OFNumber.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileSize.
 */
extern const of_file_attribute_key_t of_file_attribute_key_size;

/**
 * @brief The type of the file.
 *
 * The corresponding value is of type @ref of_file_type_t.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileType.
 */
extern const of_file_attribute_key_t of_file_attribute_key_type;

/**
 * @brief The POSIX permissions of the file as an @ref OFNumber.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#filePOSIXPermissions.
 */
extern const of_file_attribute_key_t of_file_attribute_key_posix_permissions;

/**
 * @brief The POSIX UID of the file as an @ref OFNumber.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#filePOSIXUID.
 */
extern const of_file_attribute_key_t of_file_attribute_key_posix_uid;

/**
 * @brief The POSIX GID of the file as an @ref OFNumber.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#filePOSIXGID.
 */
extern const of_file_attribute_key_t of_file_attribute_key_posix_gid;

/**
 * @brief The owner of the file as an OFString.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileOwner.
 */
extern const of_file_attribute_key_t of_file_attribute_key_owner;

/**
 * @brief The group of the file as an OFString.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileGroup.
 */
extern const of_file_attribute_key_t of_file_attribute_key_group;

/**
 * @brief The last access date of the file as an @ref OFDate.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileLastAccessDate.
 */
extern const of_file_attribute_key_t of_file_attribute_key_last_access_date;

/**
 * @brief The last modification date of the file as an @ref OFDate.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileModificationDate.
 */
extern const of_file_attribute_key_t of_file_attribute_key_modification_date;

/**
 * @brief The last status change date of the file as an @ref OFDate.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileStatusChangeDate.
 */
extern const of_file_attribute_key_t of_file_attribute_key_status_change_date;

/**
 * @brief The creation date of the file as an @ref OFDate.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileCreationDate.
 */
extern const of_file_attribute_key_t of_file_attribute_key_creation_date;

/**
 * @brief The destination of a symbolic link as an OFString.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileSymbolicLinkDestination.
 */
extern const of_file_attribute_key_t
    of_file_attribute_key_symbolic_link_destination;

/**
 * @brief A regular file.
 */
extern const of_file_type_t of_file_type_regular;

/**
 * @brief A directory.
 */
extern const of_file_type_t of_file_type_directory;

/**
 * @brief A symbolic link.
 */
extern const of_file_type_t of_file_type_symbolic_link;

/**
 * @brief A FIFO.
 */
extern const of_file_type_t of_file_type_fifo;

/**
 * @brief A character special file.
 */
extern const of_file_type_t of_file_type_character_special;

/**
 * @brief A block special file.
 */
extern const of_file_type_t of_file_type_block_special;

/**
 * @brief A socket.
 */

extern const of_file_type_t of_file_type_socket;








#ifdef __cplusplus
}
#endif

/**
 * @class OFFileManager OFFileManager.h ObjFW/OFFileManager.h
 *







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



|






|
|
|
|
>
|
|
|



|


|
|

|
<


|
|

|
|










|




|




|







|


|


|

|


|


|

|


|


|

|


|


|

|







|







|







|







|







|
<




|




|




|




|




|




|




>
|
>
>
>
>
>
>
>
>







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
@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.
 */
typedef OFDictionary OF_GENERIC(OFFileAttributeKey, id) *OFFileAttributes;


/**
 * @brief A mutable dictionary mapping keys of type @ref OFFileAttributeKey to
 *	  their attribute values.
 */
typedef OFMutableDictionary OF_GENERIC(OFFileAttributeKey, id)
    *OFMutableFileAttributes;

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief The size of the file as an @ref OFNumber.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileSize.
 */
extern const OFFileAttributeKey OFFileSize;

/**
 * @brief The type of the file.
 *
 * The corresponding value is of type @ref OFFileAttributeType.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileType.
 */
extern const OFFileAttributeKey OFFileType;

/**
 * @brief The POSIX permissions of the file as an @ref OFNumber.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#filePOSIXPermissions.
 */
extern const OFFileAttributeKey OFFilePOSIXPermissions;

/**
 * @brief The account ID of the owner of the file as an @ref OFNumber.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileOwnerAccountID.
 */
extern const OFFileAttributeKey OFFileOwnerAccountID;

/**
 * @brief The account ID of the group owner of the file as an @ref OFNumber.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileGroupOwnerAccountID.
 */
extern const OFFileAttributeKey OFFileGroupOwnerAccountID;

/**
 * @brief The account name of the owner of the file as an OFString.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileOwnerAccountName.
 */
extern const OFFileAttributeKey OFFileOwnerAccountName;

/**
 * @brief The account name of the group owner of the file as an OFString.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileGroupOwnerAccountName.
 */
extern const OFFileAttributeKey OFFileGroupOwnerAccountName;

/**
 * @brief The last access date of the file as an @ref OFDate.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileLastAccessDate.
 */
extern const OFFileAttributeKey OFFileLastAccessDate;

/**
 * @brief The last modification date of the file as an @ref OFDate.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileModificationDate.
 */
extern const OFFileAttributeKey OFFileModificationDate;

/**
 * @brief The last status change date of the file as an @ref OFDate.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileStatusChangeDate.
 */
extern const OFFileAttributeKey OFFileStatusChangeDate;

/**
 * @brief The creation date of the file as an @ref OFDate.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileCreationDate.
 */
extern const OFFileAttributeKey OFFileCreationDate;

/**
 * @brief The destination of a symbolic link as an OFString.
 *
 * For convenience, a category on @ref OFDictionary is provided to access this
 * via @ref OFDictionary#fileSymbolicLinkDestination.
 */
extern const OFFileAttributeKey OFFileSymbolicLinkDestination;


/**
 * @brief A regular file.
 */
extern const OFFileAttributeType OFFileTypeRegular;

/**
 * @brief A directory.
 */
extern const OFFileAttributeType OFFileTypeDirectory;

/**
 * @brief A symbolic link.
 */
extern const OFFileAttributeType OFFileTypeSymbolicLink;

/**
 * @brief A FIFO.
 */
extern const OFFileAttributeType OFFileTypeFIFO;

/**
 * @brief A character special file.
 */
extern const OFFileAttributeType OFFileTypeCharacterSpecial;

/**
 * @brief A block special file.
 */
extern const OFFileAttributeType OFFileTypeBlockSpecial;

/**
 * @brief A socket.
 */
extern const OFFileAttributeType OFFileTypeSocket;

/**
 * @brief An unknown file type.
 *
 * This is different from not having an @ref OFFileType at all in that it means
 * that retrieving file types is supported, but the particular file type is
 * unknown.
 */
extern const OFFileAttributeType OFFileTypeUnknown;
#ifdef __cplusplus
}
#endif

/**
 * @class OFFileManager OFFileManager.h ObjFW/OFFileManager.h
 *
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

#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 of_file_attribute_key_t
 */
- (of_file_attributes_t)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 of_file_attribute_key_t
 */
- (of_file_attributes_t)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: (of_file_attributes_t)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: (of_file_attributes_t)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







|

|







|

|










|











<
|







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

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

/**
 * @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 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 of OFString with the items in the specified directory
 */
- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtURL: (OFURL *)URL;

#ifdef OF_HAVE_FILES











/**
 * @brief Changes the current working directory.
 *
 * @param path The new directory to change to
 */
- (void)changeCurrentDirectoryPath: (OFString *)path;








|
<














|
>




|

|


>
>
>
>
>
>
>
>
>
>
>







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

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

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







|
<















|
<















|
<















|
<







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







|
<













|
<







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
 * 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.
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
 */
- (void)createSymbolicLinkAtURL: (OFURL *)URL
	    withDestinationPath: (OFString *)target;
@end

@interface OFDictionary (FileAttributes)
/**
 * @brief The @ref of_file_attribute_key_size key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) unsigned long long fileSize;

/**
 * @brief The @ref of_file_attribute_key_type key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) of_file_type_t fileType;

/**
 * @brief The @ref of_file_attribute_key_posix_permissions key from the
 *	  dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) unsigned long filePOSIXPermissions;

/**
 * @brief The @ref of_file_attribute_key_posix_uid key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) unsigned long filePOSIXUID;

/**
 * @brief The @ref of_file_attribute_key_posix_gid key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) unsigned long filePOSIXGID;

/**
 * @brief The @ref of_file_attribute_key_owner key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) OFString *fileOwner;

/**
 * @brief The @ref of_file_attribute_key_group key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) OFString *fileGroup;

/**
 * @brief The @ref of_file_attribute_key_last_access_date key from the
 *	  dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) OFDate *fileLastAccessDate;

/**
 * @brief The @ref of_file_attribute_key_modification_date key from the
 *	  dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) OFDate *fileModificationDate;

/**
 * @brief The @ref of_file_attribute_key_status_change_date key from the
 *	  dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) OFDate *fileStatusChangeDate;

/**
 * @brief The @ref of_file_attribute_key_creation_date key from the dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) OFDate *fileCreationDate;

/**
 * @brief The @ref of_file_attribute_key_symbolic_link_destination key from the
 *	  dictionary.
 *
 * Raises an @ref OFUndefinedKeyException if the key is missing.
 */
@property (readonly, nonatomic) OFString *fileSymbolicLinkDestination;
@end

OF_ASSUME_NONNULL_END







|






|



|


|
<






|



|


|



|


|



|


|



|


<
|






<
|






<
|






|






|
<







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

Modified src/OFFileManager.m from [356e95e601] to [8b48822fc3].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
# include <ntdef.h>
#endif

#ifdef OF_AMIGAOS
# include <proto/exec.h>
# include <proto/dos.h>
#endif

@interface OFDefaultFileManager: OFFileManager

@end

const of_file_attribute_key_t of_file_attribute_key_size =
    @"of_file_attribute_key_size";
const of_file_attribute_key_t of_file_attribute_key_type =
    @"of_file_attribute_key_type";
const of_file_attribute_key_t of_file_attribute_key_posix_permissions =
    @"of_file_attribute_key_posix_permissions";
const of_file_attribute_key_t of_file_attribute_key_posix_uid =
    @"of_file_attribute_key_posix_uid";
const of_file_attribute_key_t of_file_attribute_key_posix_gid =
    @"of_file_attribute_key_posix_gid";
const of_file_attribute_key_t of_file_attribute_key_owner =
    @"of_file_attribute_key_owner";
const of_file_attribute_key_t of_file_attribute_key_group =
    @"of_file_attribute_key_group";
const of_file_attribute_key_t of_file_attribute_key_last_access_date =
    @"of_file_attribute_key_last_access_date";
const of_file_attribute_key_t of_file_attribute_key_modification_date =
    @"of_file_attribute_key_modification_date";
const of_file_attribute_key_t of_file_attribute_key_status_change_date =
    @"of_file_attribute_key_status_change_date";
const of_file_attribute_key_t of_file_attribute_key_creation_date =
    @"of_file_attribute_key_creation_date";
const of_file_attribute_key_t of_file_attribute_key_symbolic_link_destination =
    @"of_file_attribute_key_symbolic_link_destination";

const of_file_type_t of_file_type_regular = @"of_file_type_regular";
const of_file_type_t of_file_type_directory = @"of_file_type_directory";
const of_file_type_t of_file_type_symbolic_link = @"of_file_type_symbolic_link";
const of_file_type_t of_file_type_fifo = @"of_file_type_fifo";
const of_file_type_t of_file_type_character_special =
    @"of_file_type_character_special";
const of_file_type_t of_file_type_block_special = @"of_file_type_block_special";
const of_file_type_t of_file_type_socket = @"of_file_type_socket";

#ifdef OF_AMIGAOS4
# define CurrentDir(lock) SetCurrentDir(lock)
#endif



static OFFileManager *defaultManager;

#ifdef OF_AMIGAOS
static bool dirChanged = false;
static BPTR originalDirLock = 0;

OF_DESTRUCTOR()
{
	if (dirChanged)
		UnLock(CurrentDir(originalDirLock));
}
#endif

static id
attributeForKeyOrException(of_file_attributes_t attributes,
    of_file_attribute_key_t key)
{
	id object = [attributes objectForKey: key];

	if (object == nil)
		@throw [OFUndefinedKeyException exceptionWithObject: attributes
								key: key];









|
>
|

<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<




>
>















|
<







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
# include <ntdef.h>
#endif

#ifdef OF_AMIGAOS
# include <proto/exec.h>
# include <proto/dos.h>
#endif

#ifdef OF_MINT
# include <bits/local_lim.h>
#endif


@interface OFDefaultFileManager: OFFileManager






















@end









#ifdef OF_AMIGAOS4
# define CurrentDir(lock) SetCurrentDir(lock)
#endif

#include "OFFileManagerConstants.inc"

static OFFileManager *defaultManager;

#ifdef OF_AMIGAOS
static bool dirChanged = false;
static BPTR originalDirLock = 0;

OF_DESTRUCTOR()
{
	if (dirChanged)
		UnLock(CurrentDir(originalDirLock));
}
#endif

static id
attributeForKeyOrException(OFFileAttributes attributes, OFFileAttributeKey key)

{
	id object = [attributes objectForKey: key];

	if (object == nil)
		@throw [OFUndefinedKeyException exceptionWithObject: attributes
								key: key];

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

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}
#endif

- (of_file_attributes_t)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
- (of_file_attributes_t)attributesOfItemAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	of_file_attributes_t ret;

	ret = [self attributesOfItemAtURL: [OFURL fileURLWithPath: path]];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
#endif

- (void)setAttributes: (of_file_attributes_t)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: (of_file_attributes_t)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;







|













|


|











<
|









|
<



|



<


<







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

	[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;
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
	[self createDirectoryAtURL: [OFURL fileURLWithPath: path]
		     createParents: createParents];

	objc_autoreleasePoolPop(pool);
}
#endif

- (OFArray OF_GENERIC(OFString *) *)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(OFString *) *ret;


	ret = [self contentsOfDirectoryAtURL: [OFURL fileURLWithPath: path]];




































	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}








|
















|
>

|
>

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







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

- (OFArray OF_GENERIC(OFString *) *)subpathsOfDirectoryAtPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray OF_GENERIC(OFString *) *ret =
	    [OFMutableArray arrayWithObject: path];

	for (OFString *subpath in [self contentsOfDirectoryAtPath: path]) {
		void *pool2 = objc_autoreleasePoolPush();
		OFString *fullSubpath =
		    [path stringByAppendingPathComponent: subpath];
		OFFileAttributes attributes =
		    [self attributesOfItemAtPath: fullSubpath];

		if ([attributes.fileType isEqual: OFFileTypeDirectory])
			[ret addObjectsFromArray:
			    [self subpathsOfDirectoryAtPath: fullSubpath]];
		else
			[ret addObject: fullSubpath];

		objc_autoreleasePoolPop(pool2);
	}

	[ret makeImmutable];
	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

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
	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;
	of_file_attributes_t attributes;
	of_file_type_t 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: of_file_type_directory]) {
		OFArray *contents;

		@try {
			[self createDirectoryAtURL: destination];

			@try {
				of_file_attribute_key_t key =
				    of_file_attribute_key_posix_permissions;
				OFNumber *permissions =
				    [attributes objectForKey: key];
				of_file_attributes_t destinationAttributes;

				if (permissions != nil) {
					destinationAttributes = [OFDictionary
					    dictionaryWithObject: permissions
							  forKey: key];
					[self
					    setAttributes: destinationAttributes







|
<










|
<



|
|










|
<



















|
|





|
<


|







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
	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
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
				    exceptionWithSourceURL: source
					    destinationURL: destination
						     errNo: [e errNo]];

			@throw e;
		}

		for (OFString *item in contents) {
			void *pool2 = objc_autoreleasePoolPush();
			OFURL *sourceURL, *destinationURL;

			sourceURL =
			    [source URLByAppendingPathComponent: item];
			destinationURL =
			    [destination URLByAppendingPathComponent: item];

			[self copyItemAtURL: sourceURL
				      toURL: destinationURL];

			objc_autoreleasePoolPop(pool2);
		}
	} else if ([type isEqual: of_file_type_regular]) {
		size_t pageSize = [OFSystemInfo pageSize];
		OFStream *sourceStream = nil;
		OFStream *destinationStream = nil;
		char *buffer;

		buffer = of_malloc(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];
				[destinationStream writeBuffer: buffer
							length: length];
			}

			@try {
				of_file_attribute_key_t key =
				    of_file_attribute_key_posix_permissions;
				OFNumber *permissions = [attributes
				    objectForKey: key];
				of_file_attributes_t destinationAttributes;

				if (permissions != nil) {
					destinationAttributes = [OFDictionary
					    dictionaryWithObject: permissions
							  forKey: key];
					[self
					    setAttributes: destinationAttributes







|

|
<
<
|
<
|

|
<



|





|



















|
<


|







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
				    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];
				[destinationStream writeBuffer: buffer
							length: length];
			}

			@try {
				OFFileAttributeKey key = OFFilePOSIXPermissions;

				OFNumber *permissions = [attributes
				    objectForKey: key];
				OFFileAttributes destinationAttributes;

				if (permissions != nil) {
					destinationAttributes = [OFDictionary
					    dictionaryWithObject: permissions
							  forKey: key];
					[self
					    setAttributes: destinationAttributes
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
					    destinationURL: destination
						     errNo: [e errNo]];

			@throw e;
		} @finally {
			[sourceStream close];
			[destinationStream close];
			of_free(buffer);
		}
	} else if ([type isEqual: of_file_type_symbolic_link]) {
		@try {
			OFString *linkDestination =
			    attributes.fileSymbolicLinkDestination;

			[self createSymbolicLinkAtURL: destination
				  withDestinationPath: linkDestination];
		} @catch (id e) {







|

|







706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
					    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) {
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
			    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];







|
<


<


<




|
<














|
<













|
<







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
			    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];
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
	[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
- (instancetype)autorelease







<

<




|
<
















|
<





|
<


<


<


















|
<









<


<







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
	[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
- (instancetype)autorelease
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

- (void)release
{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}
@end

@implementation OFDictionary (FileAttributes)
- (unsigned long long)fileSize
{
	return [attributeForKeyOrException(self, of_file_attribute_key_size)
	    unsignedLongLongValue];
}

- (of_file_type_t)fileType
{
	return attributeForKeyOrException(self, of_file_attribute_key_type);
}

- (unsigned long)filePOSIXPermissions
{
	return [attributeForKeyOrException(self,
	    of_file_attribute_key_posix_permissions) unsignedLongValue];
}

- (unsigned long)filePOSIXUID
{
	return [attributeForKeyOrException(self,
	    of_file_attribute_key_posix_uid) unsignedLongValue];
}

- (unsigned long)filePOSIXGID
{
	return [attributeForKeyOrException(self,
	    of_file_attribute_key_posix_gid) unsignedLongValue];
}

- (OFString *)fileOwner
{
	return attributeForKeyOrException(self, of_file_attribute_key_owner);
}

- (OFString *)fileGroup
{
	return attributeForKeyOrException(self, of_file_attribute_key_group);
}

- (OFDate *)fileLastAccessDate
{
	return attributeForKeyOrException(self,
	    of_file_attribute_key_last_access_date);
}

- (OFDate *)fileModificationDate
{
	return attributeForKeyOrException(self,
	    of_file_attribute_key_modification_date);
}

- (OFDate *)fileStatusChangeDate
{
	return attributeForKeyOrException(self,
	    of_file_attribute_key_status_change_date);
}

- (OFDate *)fileCreationDate
{
	return attributeForKeyOrException(self,
	    of_file_attribute_key_creation_date);
}

- (OFString *)fileSymbolicLinkDestination
{
	return attributeForKeyOrException(self,
	    of_file_attribute_key_symbolic_link_destination);
}
@end







|






|



|

|





|


|


|


|


|


|

|


|

|




|
<




|
<




|
<




|
<




|
<


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

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}
@end

@implementation OFDictionary (FileAttributes)
- (unsigned long long)fileSize
{
	return [attributeForKeyOrException(self, OFFileSize)
	    unsignedLongLongValue];
}

- (OFFileAttributeType)fileType
{
	return attributeForKeyOrException(self, OFFileType);
}

- (unsigned long)filePOSIXPermissions
{
	return [attributeForKeyOrException(self,
	    OFFilePOSIXPermissions) unsignedLongValue];
}

- (unsigned long)fileOwnerAccountID
{
	return [attributeForKeyOrException(self,
	    OFFileOwnerAccountID) unsignedLongValue];
}

- (unsigned long)fileGroupOwnerAccountID
{
	return [attributeForKeyOrException(self,
	    OFFileGroupOwnerAccountID) unsignedLongValue];
}

- (OFString *)fileOwnerAccountName
{
	return attributeForKeyOrException(self, OFFileOwnerAccountName);
}

- (OFString *)fileGroupOwnerAccountName
{
	return attributeForKeyOrException(self, OFFileGroupOwnerAccountName);
}

- (OFDate *)fileLastAccessDate
{
	return attributeForKeyOrException(self, OFFileLastAccessDate);

}

- (OFDate *)fileModificationDate
{
	return attributeForKeyOrException(self, OFFileModificationDate);

}

- (OFDate *)fileStatusChangeDate
{
	return attributeForKeyOrException(self, OFFileStatusChangeDate);

}

- (OFDate *)fileCreationDate
{
	return attributeForKeyOrException(self, OFFileCreationDate);

}

- (OFString *)fileSymbolicLinkDestination
{
	return attributeForKeyOrException(self, OFFileSymbolicLinkDestination);

}
@end

Added src/OFFileManagerConstants.inc version [7a3eb9bf4f].

















































































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

const OFFileAttributeKey OFFileSize = @"OFFileSize";
const OFFileAttributeKey OFFileType = @"OFFileType";
const OFFileAttributeKey OFFilePOSIXPermissions = @"OFFilePOSIXPermissions";
const OFFileAttributeKey OFFileOwnerAccountID = @"OFFileOwnerAccountID";
const OFFileAttributeKey OFFileGroupOwnerAccountID =
    @"OFFileGroupOwnerAccountID";
const OFFileAttributeKey OFFileOwnerAccountName = @"OFFileOwnerAccountName";
const OFFileAttributeKey OFFileGroupOwnerAccountName =
    @"OFFileGroupOwnerAccountName";
const OFFileAttributeKey OFFileLastAccessDate = @"OFFileLastAccessDate";
const OFFileAttributeKey OFFileModificationDate = @"OFFileModificationDate";
const OFFileAttributeKey OFFileStatusChangeDate = @"OFFileStatusChangeDate";
const OFFileAttributeKey OFFileCreationDate = @"OFFileCreationDate";
const OFFileAttributeKey OFFileSymbolicLinkDestination =
    @"OFFileSymbolicLinkDestination";

const OFFileAttributeType OFFileTypeRegular = @"OFFileTypeRegular";
const OFFileAttributeType OFFileTypeDirectory = @"OFFileTypeDirectory";
const OFFileAttributeType OFFileTypeSymbolicLink = @"OFFileTypeSymbolicLink";
const OFFileAttributeType OFFileTypeFIFO = @"OFFileTypeFIFO";
const OFFileAttributeType OFFileTypeCharacterSpecial =
    @"OFFileTypeCharacterSpecial";
const OFFileAttributeType OFFileTypeBlockSpecial = @"OFFileTypeBlockSpecial";
const OFFileAttributeType OFFileTypeSocket = @"OFFileTypeSocket";
const OFFileAttributeType OFFileTypeUnknown = @"OFFileTypeUnknown";

Modified src/OFFileURLHandler.h from [8275bdd541] to [bc6fd1985b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFFileURLHandler.m from [987947edfd] to [50a5ea9a56].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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>
#include <math.h>

#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif

<
<
|














>
>







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


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

#define _LARGEFILE64_SOURCE

#include <errno.h>
#include <math.h>

#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
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
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
# 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) && !defined(OF_MORPHOS))
typedef struct {
	of_offset_t st_size;
	unsigned int st_mode;
	of_time_interval_t st_atime, st_mtime, st_ctime;
# ifdef OF_WINDOWS
#  define HAVE_STRUCT_STAT_ST_BIRTHTIME
	of_time_interval_t st_birthtime;
	DWORD fileAttributes;
# endif
} of_stat_t;
#elif defined(HAVE_STAT64)
typedef struct stat64 of_stat_t;
#else
typedef struct stat of_stat_t;
#endif

#ifdef OF_WINDOWS
# define S_IFLNK 0x10000
# define S_ISLNK(mode) (mode & S_IFLNK)
#endif

#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS)
static OFMutex *passwdMutex;






#endif
#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS)
static OFMutex *readdirMutex;






#endif

#ifdef OF_WINDOWS
static int (*func__wutime64)(const wchar_t *, struct __utimbuf64 *);
static WINAPI BOOLEAN (*func_CreateSymbolicLinkW)(LPCWSTR, LPCWSTR, DWORD);
static WINAPI BOOLEAN (*func_CreateHardLinkW)(LPCWSTR, LPCWSTR,
    LPSECURITY_ATTRIBUTES);
#endif

#ifdef OF_WINDOWS
static of_time_interval_t
filetimeToTimeInterval(const FILETIME *filetime)
{
	return (double)((int64_t)filetime->dwHighDateTime << 32 |
	    filetime->dwLowDateTime) / 10000000.0 - 11644473600.0;
}

static void
setErrno(void)
{
	switch (GetLastError()) {
	case ERROR_FILE_NOT_FOUND:
	case ERROR_PATH_NOT_FOUND:
	case ERROR_NO_MORE_FILES:
		errno = ENOENT;
		return;
	case ERROR_ACCESS_DENIED:
		errno = EACCES;
		return;
	case ERROR_DIRECTORY:
		errno = ENOTDIR;
		return;
	case ERROR_NOT_READY:
		errno = EBUSY;

		return;
	}

	errno = 0;
}
#endif

#ifdef OF_AMIGAOS
static void
setErrno(void)
{
	switch (IoErr()) {
	case ERROR_DELETE_PROTECTED:
	case ERROR_READ_PROTECTED:
	case ERROR_WRITE_PROTECTED:
		errno = EACCES;
		break;
	case ERROR_DISK_NOT_VALIDATED:
	case ERROR_OBJECT_IN_USE:
		errno = EBUSY;
		break;
	case ERROR_OBJECT_EXISTS:
		errno = EEXIST;
		break;
	case ERROR_DIR_NOT_FOUND:
	case ERROR_NO_MORE_ENTRIES:
	case ERROR_OBJECT_NOT_FOUND:
		errno = ENOENT;
		break;
	case ERROR_NO_FREE_STORE:
		errno = ENOMEM;
		break;
	case ERROR_DISK_FULL:
		errno = ENOSPC;
		break;
	case ERROR_DIRECTORY_NOT_EMPTY:
		errno = ENOTEMPTY;
		break;
	case ERROR_DISK_WRITE_PROTECTED:
		errno = EROFS;
		break;
	case ERROR_RENAME_ACROSS_DEVICES:
		errno = EXDEV;
		break;
	default:
		errno = 0;
		break;
	}
}
#endif

static int
of_stat(OFString *path, of_stat_t *buffer)
{
#if defined(OF_WINDOWS)
	WIN32_FILE_ATTRIBUTE_DATA data;
	bool success;

	if ([OFSystemInfo isWindowsNT])
		success = GetFileAttributesExW(path.UTF16String,
		    GetFileExInfoStandard, &data);
	else
		success = GetFileAttributesExA(
		    [path cStringWithEncoding: [OFLocale encoding]],
		    GetFileExInfoStandard, &data);

	if (!success) {
		setErrno();
		return -1;
	}

	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) {
			setErrno();
			return -1;
		}

		@try {
			if (!(findData.dwFileAttributes &
			    FILE_ATTRIBUTE_REPARSE_POINT)) {
				/* Race? Indicate to try again. */
				errno = EAGAIN;
				return -1;
			}

			buffer->st_mode =
			    (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK
			    ? S_IFLNK : S_IFREG);
		} @finally {
			FindClose(findHandle);
		}
	} else
		buffer->st_mode = S_IFREG;

	buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY
	    ? (S_IRUSR | S_IXUSR) : (S_IRUSR | S_IWUSR | S_IXUSR));

	buffer->st_atime = filetimeToTimeInterval(&data.ftLastAccessTime);
	buffer->st_mtime = filetimeToTimeInterval(&data.ftLastWriteTime);
	buffer->st_ctime = buffer->st_birthtime =
	    filetimeToTimeInterval(&data.ftCreationTime);
	buffer->fileAttributes = data.dwFileAttributes;

	return 0;
#elif defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
	BPTR lock;
# ifdef OF_AMIGAOS4
	struct ExamineData *ed;
# else
	struct FileInfoBlock fib;
# endif
	of_time_interval_t timeInterval;
	struct Locale *locale;
	struct DateStamp *date;

	if ((lock = Lock([path cStringWithEncoding: [OFLocale encoding]],
	    SHARED_LOCK)) == 0) {
		setErrno();
		return -1;
	}


# ifdef OF_AMIGAOS4
	if ((ed = ExamineObjectTags(EX_FileLockInput, lock, TAG_END)) == NULL) {
# else
	if (!Examine(lock, &fib)) {
# endif

		UnLock(lock);

		errno = 0;
		return -1;
	}

	UnLock(lock);



# ifdef OF_AMIGAOS4
	buffer->st_size = ed->FileSize;
	buffer->st_mode = (EXD_IS_DIRECTORY(ed) ? S_IFDIR : S_IFREG);
# else
	buffer->st_size = fib.fib_Size;




	buffer->st_mode = (fib.fib_DirEntryType > 0 ? S_IFDIR : S_IFREG);
# endif

	timeInterval = 252460800;	/* 1978-01-01 */

	locale = OpenLocale(NULL);
	/*







|

|

|


|


|

|

|









>
>
>
>
>
>



>
>
>
>
>
>



|
|
|




|






|
|





<
|

<
|

<
|

|
>
|

<
<




|
|





|
<


|
<

|
<



|
<

|
<

|
<

|
<

|
<

|
<

<
|





|













|
<
|
<















|
<
|
<



|

<
|
<




















|






|




|
<
|
|
|
>
|




>

<
<
|




>
>
|

<


>
>
>
>







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
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
# 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
} Stat;
#elif defined(HAVE_STAT64)
typedef struct stat64 Stat;
#else
typedef struct stat Stat;
#endif

#ifdef OF_WINDOWS
# define S_IFLNK 0x10000
# define S_ISLNK(mode) (mode & S_IFLNK)
#endif

#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS)
static OFMutex *passwdMutex;

static void
releasePasswdMutex(void)
{
	[passwdMutex release];
}
#endif
#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS)
static OFMutex *readdirMutex;

static void
releaseReaddirMutex(void)
{
	[readdirMutex release];
}
#endif

#ifdef OF_WINDOWS
static int (*_wutime64FuncPtr)(const wchar_t *, struct __utimbuf64 *);
static WINAPI BOOLEAN (*createSymbolicLinkWFuncPtr)(LPCWSTR, LPCWSTR, DWORD);
static WINAPI BOOLEAN (*createHardLinkWFuncPtr)(LPCWSTR, LPCWSTR,
    LPSECURITY_ATTRIBUTES);
#endif

#ifdef OF_WINDOWS
static OFTimeInterval
filetimeToTimeInterval(const FILETIME *filetime)
{
	return (double)((int64_t)filetime->dwHighDateTime << 32 |
	    filetime->dwLowDateTime) / 10000000.0 - 11644473600.0;
}

static int
retrieveError(void)
{
	switch (GetLastError()) {
	case ERROR_FILE_NOT_FOUND:
	case ERROR_PATH_NOT_FOUND:
	case ERROR_NO_MORE_FILES:

		return ENOENT;
	case ERROR_ACCESS_DENIED:

		return EACCES;
	case ERROR_DIRECTORY:

		return ENOTDIR;
	case ERROR_NOT_READY:
		return EBUSY;
	default:
		return EIO;
	}


}
#endif

#ifdef OF_AMIGAOS
static int
retrieveError(void)
{
	switch (IoErr()) {
	case ERROR_DELETE_PROTECTED:
	case ERROR_READ_PROTECTED:
	case ERROR_WRITE_PROTECTED:
		return EACCES;

	case ERROR_DISK_NOT_VALIDATED:
	case ERROR_OBJECT_IN_USE:
		return EBUSY;

	case ERROR_OBJECT_EXISTS:
		return EEXIST;

	case ERROR_DIR_NOT_FOUND:
	case ERROR_NO_MORE_ENTRIES:
	case ERROR_OBJECT_NOT_FOUND:
		return ENOENT;

	case ERROR_NO_FREE_STORE:
		return ENOMEM;

	case ERROR_DISK_FULL:
		return ENOSPC;

	case ERROR_DIRECTORY_NOT_EMPTY:
		return ENOTEMPTY;

	case ERROR_DISK_WRITE_PROTECTED:
		return EROFS;

	case ERROR_RENAME_ACROSS_DEVICES:
		return EXDEV;

	default:

		return EIO;
	}
}
#endif

static int
statWrapper(OFString *path, Stat *buffer)
{
#if defined(OF_WINDOWS)
	WIN32_FILE_ATTRIBUTE_DATA data;
	bool success;

	if ([OFSystemInfo isWindowsNT])
		success = GetFileAttributesExW(path.UTF16String,
		    GetFileExInfoStandard, &data);
	else
		success = GetFileAttributesExA(
		    [path cStringWithEncoding: [OFLocale encoding]],
		    GetFileExInfoStandard, &data);

	if (!success)

		return retrieveError();


	buffer->st_size = (uint64_t)data.nFileSizeHigh << 32 |
	    data.nFileSizeLow;

	if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		buffer->st_mode = S_IFDIR;
	else if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
		/*
		 * No need to use A functions in this branch: This is only
		 * available on NTFS (and hence Windows NT) anyway.
		 */
		WIN32_FIND_DATAW findData;
		HANDLE findHandle;

		if ((findHandle = FindFirstFileW(path.UTF16String,
		    &findData)) == INVALID_HANDLE_VALUE)

			return retrieveError();


		@try {
			if (!(findData.dwFileAttributes &
			    FILE_ATTRIBUTE_REPARSE_POINT))
				/* Race? Indicate to try again. */

				return EAGAIN;


			buffer->st_mode =
			    (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK
			    ? S_IFLNK : S_IFREG);
		} @finally {
			FindClose(findHandle);
		}
	} else
		buffer->st_mode = S_IFREG;

	buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY
	    ? (S_IRUSR | S_IXUSR) : (S_IRUSR | S_IWUSR | S_IXUSR));

	buffer->st_atime = filetimeToTimeInterval(&data.ftLastAccessTime);
	buffer->st_mtime = filetimeToTimeInterval(&data.ftLastWriteTime);
	buffer->st_ctime = buffer->st_birthtime =
	    filetimeToTimeInterval(&data.ftCreationTime);
	buffer->fileAttributes = data.dwFileAttributes;

	return 0;
#elif defined(OF_AMIGAOS)
	BPTR lock;
# ifdef OF_AMIGAOS4
	struct ExamineData *ed;
# else
	struct FileInfoBlock fib;
# endif
	OFTimeInterval timeInterval;
	struct Locale *locale;
	struct DateStamp *date;

	if ((lock = Lock([path cStringWithEncoding: [OFLocale encoding]],
	    SHARED_LOCK)) == 0)

		return retrieveError();

# if defined(OF_MORPHOS)
	if (!Examine64(lock, &fib, TAG_DONE)) {
# elif defined(OF_AMIGAOS4)
	if ((ed = ExamineObjectTags(EX_FileLockInput, lock, TAG_END)) == NULL) {
# else
	if (!Examine(lock, &fib)) {
# endif
		int error = retrieveError();
		UnLock(lock);


		return error;
	}

	UnLock(lock);

# if defined(OF_MORPHOS)
	buffer->st_size = fib.fib_Size64;
# elif defined(OF_AMIGAOS4)
	buffer->st_size = ed->FileSize;

# else
	buffer->st_size = fib.fib_Size;
# endif
# ifdef OF_AMIGAOS4
	buffer->st_mode = (EXD_IS_DIRECTORY(ed) ? S_IFDIR : S_IFREG);
# else
	buffer->st_mode = (fib.fib_DirEntryType > 0 ? S_IFDIR : S_IFREG);
# endif

	timeInterval = 252460800;	/* 1978-01-01 */

	locale = OpenLocale(NULL);
	/*
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
# ifdef OF_AMIGAOS4
	date = &ed->Date;
# else
	date = &fib.fib_Date;
# endif
	timeInterval += date->ds_Days * 86400.0;
	timeInterval += date->ds_Minute * 60.0;
	timeInterval += date->ds_Tick / (of_time_interval_t)TICKS_PER_SECOND;

	buffer->st_atime = buffer->st_mtime = buffer->st_ctime = timeInterval;

# ifdef OF_AMIGAOS4
	FreeDosObject(DOS_EXAMINEDATA, ed);
# endif

	return 0;
#elif defined(HAVE_STAT64)
	return stat64([path cStringWithEncoding: [OFLocale encoding]], buffer);




#else
	return stat([path cStringWithEncoding: [OFLocale encoding]], buffer);



#endif
}

static int
of_lstat(OFString *path, of_stat_t *buffer)
{
#if defined(HAVE_LSTAT) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS) && \
    !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
# ifdef HAVE_LSTAT64
	return lstat64([path cStringWithEncoding: [OFLocale encoding]], buffer);


# else
	return lstat([path cStringWithEncoding: [OFLocale encoding]], buffer);

# endif


#else
	return of_stat(path, buffer);
#endif
}

static void
setTypeAttribute(of_mutable_file_attributes_t attributes, of_stat_t *s)
{
	if (S_ISREG(s->st_mode))
		[attributes setObject: of_file_type_regular
			       forKey: of_file_attribute_key_type];
	else if (S_ISDIR(s->st_mode))
		[attributes setObject: of_file_type_directory
			       forKey: of_file_attribute_key_type];
#ifdef S_ISLNK
	else if (S_ISLNK(s->st_mode))
		[attributes setObject: of_file_type_symbolic_link
			       forKey: of_file_attribute_key_type];
#endif
#ifdef S_ISFIFO
	else if (S_ISFIFO(s->st_mode))
		[attributes setObject: of_file_type_fifo
			       forKey: of_file_attribute_key_type];
#endif
#ifdef S_ISCHR
	else if (S_ISCHR(s->st_mode))
		[attributes setObject: of_file_type_character_special
			       forKey: of_file_attribute_key_type];
#endif
#ifdef S_ISBLK
	else if (S_ISBLK(s->st_mode))
		[attributes setObject: of_file_type_block_special
			       forKey: of_file_attribute_key_type];
#endif
#ifdef S_ISSOCK
	else if (S_ISSOCK(s->st_mode))
		[attributes setObject: of_file_type_socket
			       forKey: of_file_attribute_key_type];
#endif


}

static void
setDateAttributes(of_mutable_file_attributes_t attributes, of_stat_t *s)
{
	/* FIXME: We could be more precise on some OSes */
	[attributes
	    setObject: [OFDate dateWithTimeIntervalSince1970: s->st_atime]
	       forKey: of_file_attribute_key_last_access_date];
	[attributes
	    setObject: [OFDate dateWithTimeIntervalSince1970: s->st_mtime]
	       forKey: of_file_attribute_key_modification_date];
	[attributes
	    setObject: [OFDate dateWithTimeIntervalSince1970: s->st_ctime]
	       forKey: of_file_attribute_key_status_change_date];
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
	[attributes
	    setObject: [OFDate dateWithTimeIntervalSince1970: s->st_birthtime]
	       forKey: of_file_attribute_key_creation_date];
#endif
}

static void
setOwnerAndGroupAttributes(of_mutable_file_attributes_t attributes,
    of_stat_t *s)
{
#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
	[attributes setObject: [NSNumber numberWithUnsignedLong: s->st_uid]
		       forKey: of_file_attribute_key_posix_uid];
	[attributes setObject: [NSNumber numberWithUnsignedLong: s->st_gid]
		       forKey: of_file_attribute_key_posix_gid];

# ifdef OF_HAVE_THREADS
	[passwdMutex lock];
	@try {
# endif
		of_string_encoding_t encoding = [OFLocale encoding];
		struct passwd *passwd = getpwuid(s->st_uid);
		struct group *group_ = getgrgid(s->st_gid);

		if (passwd != NULL) {
			OFString *owner = [OFString
			    stringWithCString: passwd->pw_name
				     encoding: encoding];

			[attributes setObject: owner
				       forKey: of_file_attribute_key_owner];
		}

		if (group_ != NULL) {
			OFString *group = [OFString
			    stringWithCString: group_->gr_name
				     encoding: encoding];

			[attributes setObject: group
				       forKey: of_file_attribute_key_group];
		}
# ifdef OF_HAVE_THREADS
	} @finally {
		[passwdMutex unlock];
	}
# endif
#endif
}

#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
static void
setSymbolicLinkDestinationAttribute(of_mutable_file_attributes_t attributes,
    OFURL *URL)
{
	OFString *path = URL.fileSystemRepresentation;
# ifndef OF_WINDOWS
	of_string_encoding_t encoding = [OFLocale encoding];
	char destinationC[PATH_MAX];
	ssize_t length;
	OFString *destination;
	of_file_attribute_key_t key;

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

	key = of_file_attribute_key_symbolic_link_destination;
	[attributes setObject: destination
		       forKey: key];
# else
	HANDLE handle;
	OFString *destination;

	if (func_CreateSymbolicLinkW == 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: 0];

	@try {
		union {
			char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
			REPARSE_DATA_BUFFER data;
		} buffer;
		DWORD size;
		wchar_t *tmp;
		of_file_attribute_key_t key;

		if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
		    buffer.bytes, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size,
		    NULL))
			@throw [OFRetrieveItemAttributesFailedException
			    exceptionWithURL: URL
				       errNo: 0];

		if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK)
			@throw [OFRetrieveItemAttributesFailedException
			    exceptionWithURL: URL
				       errNo: 0];

#  define slrb buffer.data.SymbolicLinkReparseBuffer
		tmp = slrb.PathBuffer +
		    (slrb.SubstituteNameOffset / sizeof(wchar_t));

		destination = [OFString
		    stringWithUTF16String: tmp
				   length: slrb.SubstituteNameLength /
					   sizeof(wchar_t)];

		[attributes setObject: of_file_type_symbolic_link
			       forKey: of_file_attribute_key_type];
		key = of_file_attribute_key_symbolic_link_destination;
		[attributes setObject: destination
			       forKey: key];
#  undef slrb
	} @finally {
		CloseHandle(handle);
	}
# endif
}
#endif

@implementation OFFileURLHandler
+ (void)initialize
{
#ifdef OF_WINDOWS
	HMODULE module;
#endif

	if (self != [OFFileURLHandler class])
		return;

#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS)
	passwdMutex = [[OFMutex alloc] init];

#endif
#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS)
	readdirMutex = [[OFMutex alloc] init];

#endif

#ifdef OF_WINDOWS
	if ((module = LoadLibrary("msvcrt.dll")) != NULL)
		func__wutime64 = (int (*)(const wchar_t *,
		    struct __utimbuf64 *))GetProcAddress(module, "_wutime64");

	if ((module = LoadLibrary("kernel32.dll")) != NULL) {
		func_CreateSymbolicLinkW =
		    (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD))
		    GetProcAddress(module, "CreateSymbolicLinkW");
		func_CreateHardLinkW =
		    (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR,
		    LPSECURITY_ATTRIBUTES))
		    GetProcAddress(module, "CreateHardLinkW");
	}
#endif

	/*
	 * Make sure OFFile is initialized.
	 * On some systems, this is needed to initialize the file system driver.
	 */
	[OFFile class];
}

+ (bool)of_directoryExistsAtPath: (OFString *)path
{
	of_stat_t s;

	if (of_stat(path, &s) == -1)
		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];
}

- (of_file_attributes_t)attributesOfItemAtURL: (OFURL *)URL
{
	of_mutable_file_attributes_t ret = [OFMutableDictionary dictionary];
	void *pool = objc_autoreleasePoolPush();
	OFString *path;

	of_stat_t s;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if (![[URL scheme] isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	path = URL.fileSystemRepresentation;

	if (of_lstat(path, &s) == -1)
		@throw [OFRetrieveItemAttributesFailedException
		    exceptionWithURL: URL
			       errNo: errno];

	if (s.st_size < 0)
		@throw [OFOutOfRangeException exception];

	[ret setObject: [NSNumber numberWithUnsignedLongLong: s.st_size]
		forKey: of_file_attribute_key_size];

	setTypeAttribute(ret, &s);

	[ret setObject: [NSNumber numberWithUnsignedLong: s.st_mode]
		forKey: of_file_attribute_key_posix_permissions];

	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: (of_file_attributes_t)attributes OF_DIRECT
{
	OFString *path = URL.fileSystemRepresentation;
	of_file_attribute_key_t attributeKey = (modificationDate != nil
	    ? of_file_attribute_key_modification_date
	    : of_file_attribute_key_last_access_date);

	if (lastAccessDate == nil)
		lastAccessDate = modificationDate;
	if (modificationDate == nil)
		modificationDate = lastAccessDate;

#if defined(OF_WINDOWS)
	if (func__wutime64 != NULL) {
		struct __utimbuf64 times = {
			.actime =
			    (__time64_t)lastAccessDate.timeIntervalSince1970,
			.modtime =
			    (__time64_t)modificationDate.timeIntervalSince1970
		};

		if (func__wutime64([path UTF16String], &times) != 0)
			@throw [OFSetItemAttributesFailedException
			    exceptionWithURL: URL
				  attributes: attributes
			     failedAttribute: attributeKey
				       errNo: errno];
	} else {
		struct _utimbuf times = {







|









|
>
>
>
>

|
>
>
>




|




|
>
>

|
>

>
>

|




|


|
<

|
<


|
|



|
<



|
|



|
|



|
<

>
>



|




|


|


|



|




|
<



|

|





|









|








|











|




|



<













<

|




|







|








<






|




|










|
|
<

|




















>



>




|



|


|















|

|





|
<











|

|


>
|









|


|





|




|

















|


|
|
<







|







|







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
# ifdef OF_AMIGAOS4
	date = &ed->Date;
# else
	date = &fib.fib_Date;
# endif
	timeInterval += date->ds_Days * 86400.0;
	timeInterval += date->ds_Minute * 60.0;
	timeInterval += date->ds_Tick / (OFTimeInterval)TICKS_PER_SECOND;

	buffer->st_atime = buffer->st_mtime = buffer->st_ctime = timeInterval;

# ifdef OF_AMIGAOS4
	FreeDosObject(DOS_EXAMINEDATA, ed);
# endif

	return 0;
#elif defined(HAVE_STAT64)
	if (stat64([path cStringWithEncoding: [OFLocale encoding]],
	    buffer) != 0)
		return errno;

	return 0;
#else
	if (stat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0)
		return errno;

	return 0;
#endif
}

static int
lstatWrapper(OFString *path, Stat *buffer)
{
#if defined(HAVE_LSTAT) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS) && \
    !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
# ifdef HAVE_LSTAT64
	if (lstat64([path cStringWithEncoding: [OFLocale encoding]],
	    buffer) != 0)
		return errno;
# else
	if (lstat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0)
		return errno;
# endif

	return 0;
#else
	return statWrapper(path, buffer);
#endif
}

static void
setTypeAttribute(OFMutableFileAttributes attributes, Stat *s)
{
	if (S_ISREG(s->st_mode))
		[attributes setObject: OFFileTypeRegular forKey: OFFileType];

	else if (S_ISDIR(s->st_mode))
		[attributes setObject: OFFileTypeDirectory forKey: OFFileType];

#ifdef S_ISLNK
	else if (S_ISLNK(s->st_mode))
		[attributes setObject: OFFileTypeSymbolicLink
			       forKey: OFFileType];
#endif
#ifdef S_ISFIFO
	else if (S_ISFIFO(s->st_mode))
		[attributes setObject: OFFileTypeFIFO forKey: OFFileType];

#endif
#ifdef S_ISCHR
	else if (S_ISCHR(s->st_mode))
		[attributes setObject: OFFileTypeCharacterSpecial
			       forKey: OFFileType];
#endif
#ifdef S_ISBLK
	else if (S_ISBLK(s->st_mode))
		[attributes setObject: OFFileTypeBlockSpecial
			       forKey: OFFileType];
#endif
#ifdef S_ISSOCK
	else if (S_ISSOCK(s->st_mode))
		[attributes setObject: OFFileTypeSocket forKey: OFFileType];

#endif
	else
		[attributes setObject: OFFileTypeUnknown forKey: OFFileType];
}

static void
setDateAttributes(OFMutableFileAttributes attributes, Stat *s)
{
	/* FIXME: We could be more precise on some OSes */
	[attributes
	    setObject: [OFDate dateWithTimeIntervalSince1970: s->st_atime]
	       forKey: OFFileLastAccessDate];
	[attributes
	    setObject: [OFDate dateWithTimeIntervalSince1970: s->st_mtime]
	       forKey: OFFileModificationDate];
	[attributes
	    setObject: [OFDate dateWithTimeIntervalSince1970: s->st_ctime]
	       forKey: OFFileStatusChangeDate];
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
	[attributes
	    setObject: [OFDate dateWithTimeIntervalSince1970: s->st_birthtime]
	       forKey: OFFileCreationDate];
#endif
}

static void
setOwnerAndGroupAttributes(OFMutableFileAttributes attributes, Stat *s)

{
#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
	[attributes setObject: [NSNumber numberWithUnsignedLong: s->st_uid]
		       forKey: OFFileOwnerAccountID];
	[attributes setObject: [NSNumber numberWithUnsignedLong: s->st_gid]
		       forKey: OFFileGroupOwnerAccountID];

# ifdef OF_HAVE_THREADS
	[passwdMutex lock];
	@try {
# endif
		OFStringEncoding encoding = [OFLocale encoding];
		struct passwd *passwd = getpwuid(s->st_uid);
		struct group *group_ = getgrgid(s->st_gid);

		if (passwd != NULL) {
			OFString *owner = [OFString
			    stringWithCString: passwd->pw_name
				     encoding: encoding];

			[attributes setObject: owner
				       forKey: OFFileOwnerAccountName];
		}

		if (group_ != NULL) {
			OFString *group = [OFString
			    stringWithCString: group_->gr_name
				     encoding: encoding];

			[attributes setObject: group
				       forKey: OFFileGroupOwnerAccountName];
		}
# ifdef OF_HAVE_THREADS
	} @finally {
		[passwdMutex unlock];
	}
# endif
#endif
}

#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
static void
setSymbolicLinkDestinationAttribute(OFMutableFileAttributes attributes,
    OFURL *URL)
{
	OFString *path = URL.fileSystemRepresentation;
# ifndef OF_WINDOWS
	OFStringEncoding encoding = [OFLocale encoding];
	char destinationC[PATH_MAX];
	ssize_t length;
	OFString *destination;


	length = readlink([path cStringWithEncoding: encoding], destinationC,
	    PATH_MAX);

	if (length < 0)
		@throw [OFRetrieveItemAttributesFailedException
		    exceptionWithURL: URL
			       errNo: errno];

	destination = [OFString stringWithCString: destinationC
					 encoding: encoding
					   length: length];


	[attributes setObject: destination
		       forKey: OFFileSymbolicLinkDestination];
# else
	HANDLE handle;
	OFString *destination;

	if (createSymbolicLinkWFuncPtr == NULL)
		return;

	if ((handle = CreateFileW(path.UTF16String, 0, (FILE_SHARE_READ |
	    FILE_SHARE_WRITE), NULL, OPEN_EXISTING,
	    FILE_FLAG_OPEN_REPARSE_POINT, NULL)) == INVALID_HANDLE_VALUE)
		@throw [OFRetrieveItemAttributesFailedException
		    exceptionWithURL: URL
			       errNo: retrieveError()];

	@try {
		union {
			char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
			REPARSE_DATA_BUFFER data;
		} buffer;
		DWORD size;
		wchar_t *tmp;


		if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
		    buffer.bytes, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size,
		    NULL))
			@throw [OFRetrieveItemAttributesFailedException
			    exceptionWithURL: URL
				       errNo: retrieveError()];

		if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK)
			@throw [OFRetrieveItemAttributesFailedException
			    exceptionWithURL: URL
				       errNo: retrieveError()];

#  define slrb buffer.data.SymbolicLinkReparseBuffer
		tmp = slrb.PathBuffer +
		    (slrb.SubstituteNameOffset / sizeof(wchar_t));

		destination = [OFString
		    stringWithUTF16String: tmp
				   length: slrb.SubstituteNameLength /
					   sizeof(wchar_t)];

		[attributes setObject: OFFileTypeSymbolicLink
			       forKey: OFFileType];

		[attributes setObject: destination
			       forKey: OFFileSymbolicLinkDestination];
#  undef slrb
	} @finally {
		CloseHandle(handle);
	}
# endif
}
#endif

@implementation OFFileURLHandler
+ (void)initialize
{
#ifdef OF_WINDOWS
	HMODULE module;
#endif

	if (self != [OFFileURLHandler class])
		return;

#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS)
	passwdMutex = [[OFMutex alloc] init];
	atexit(releasePasswdMutex);
#endif
#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS)
	readdirMutex = [[OFMutex alloc] init];
	atexit(releaseReaddirMutex);
#endif

#ifdef OF_WINDOWS
	if ((module = LoadLibrary("msvcrt.dll")) != NULL)
		_wutime64FuncPtr = (int (*)(const wchar_t *,
		    struct __utimbuf64 *))GetProcAddress(module, "_wutime64");

	if ((module = LoadLibrary("kernel32.dll")) != NULL) {
		createSymbolicLinkWFuncPtr =
		    (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD))
		    GetProcAddress(module, "CreateSymbolicLinkW");
		createHardLinkWFuncPtr =
		    (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR,
		    LPSECURITY_ATTRIBUTES))
		    GetProcAddress(module, "CreateHardLinkW");
	}
#endif

	/*
	 * Make sure OFFile is initialized.
	 * On some systems, this is needed to initialize the file system driver.
	 */
	[OFFile class];
}

+ (bool)of_directoryExistsAtPath: (OFString *)path
{
	Stat s;

	if (statWrapper(path, &s) != 0)
		return false;

	return S_ISDIR(s.st_mode);
}

- (OFStream *)openItemAtURL: (OFURL *)URL mode: (OFString *)mode

{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file = [[OFFile alloc]
	    initWithPath: URL.fileSystemRepresentation
		    mode: mode];

	objc_autoreleasePoolPop(pool);

	return [file autorelease];
}

- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL
{
	OFMutableFileAttributes ret = [OFMutableDictionary dictionary];
	void *pool = objc_autoreleasePoolPush();
	OFString *path;
	int error;
	Stat s;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if (![[URL scheme] isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	path = URL.fileSystemRepresentation;

	if ((error = lstatWrapper(path, &s)) != 0)
		@throw [OFRetrieveItemAttributesFailedException
		    exceptionWithURL: URL
			       errNo: error];

	if (s.st_size < 0)
		@throw [OFOutOfRangeException exception];

	[ret setObject: [NSNumber numberWithUnsignedLongLong: s.st_size]
		forKey: OFFileSize];

	setTypeAttribute(ret, &s);

	[ret setObject: [NSNumber numberWithUnsignedLong: s.st_mode]
		forKey: OFFilePOSIXPermissions];

	setOwnerAndGroupAttributes(ret, &s);
	setDateAttributes(ret, &s);

#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
	if (S_ISLNK(s.st_mode))
		setSymbolicLinkDestinationAttribute(ret, URL);
#endif

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (void)of_setLastAccessDate: (OFDate *)lastAccessDate
	 andModificationDate: (OFDate *)modificationDate
		 ofItemAtURL: (OFURL *)URL
		  attributes: (OFFileAttributes)attributes OF_DIRECT
{
	OFString *path = URL.fileSystemRepresentation;
	OFFileAttributeKey attributeKey = (modificationDate != nil
	    ? OFFileModificationDate : OFFileLastAccessDate);


	if (lastAccessDate == nil)
		lastAccessDate = modificationDate;
	if (modificationDate == nil)
		modificationDate = lastAccessDate;

#if defined(OF_WINDOWS)
	if (_wutime64FuncPtr != NULL) {
		struct __utimbuf64 times = {
			.actime =
			    (__time64_t)lastAccessDate.timeIntervalSince1970,
			.modtime =
			    (__time64_t)modificationDate.timeIntervalSince1970
		};

		if (_wutime64FuncPtr([path UTF16String], &times) != 0)
			@throw [OFSetItemAttributesFailedException
			    exceptionWithURL: URL
				  attributes: attributes
			     failedAttribute: attributeKey
				       errNo: errno];
	} else {
		struct _utimbuf times = {
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
			    exceptionWithURL: URL
				  attributes: attributes
			     failedAttribute: attributeKey
				       errNo: errno];
	}
#elif defined(OF_AMIGAOS)
	/* AmigaOS does not support access time. */
	of_time_interval_t modificationTime =
	    modificationDate.timeIntervalSince1970;
	struct Locale *locale;
	struct DateStamp date;

	modificationTime -= 252460800;	/* 1978-01-01 */

	if (modificationTime < 0)







|







693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
			    exceptionWithURL: URL
				  attributes: attributes
			     failedAttribute: attributeKey
				       errNo: errno];
	}
#elif defined(OF_AMIGAOS)
	/* AmigaOS does not support access time. */
	OFTimeInterval modificationTime =
	    modificationDate.timeIntervalSince1970;
	struct Locale *locale;
	struct DateStamp date;

	modificationTime -= 252460800;	/* 1978-01-01 */

	if (modificationTime < 0)
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
	modificationTime -= locale->loc_GMTOffset * 60.0;
	CloseLocale(locale);

	date.ds_Days = modificationTime / 86400;
	date.ds_Minute = ((LONG)modificationTime % 86400) / 60;
	date.ds_Tick = fmod(modificationTime, 60) * TICKS_PER_SECOND;


	if (!SetFileDate([path cStringWithEncoding: [OFLocale encoding]],
	    &date) != 0) {
		setErrno();



		@throw [OFSetItemAttributesFailedException
		    exceptionWithURL: URL
			  attributes: attributes
		     failedAttribute: attributeKey
			       errNo: errno];
	}
#else
	of_time_interval_t lastAccessTime =
	    lastAccessDate.timeIntervalSince1970;
	of_time_interval_t modificationTime =
	    modificationDate.timeIntervalSince1970;
	struct timeval times[2] = {
		{
			.tv_sec = (time_t)lastAccessTime,
			.tv_usec =
			    (int)((lastAccessTime - times[0].tv_sec) * 1000)
		},
		{
			.tv_sec = (time_t)modificationTime,
			.tv_usec =
			    (int)((modificationTime - times[1].tv_sec) * 1000)

		},
	};

	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: (of_file_attributes_t)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: of_file_attribute_key_posix_permissions
			       errNo: errno];
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (void)of_setOwner: (OFString *)owner
	   andGroup: (OFString *)group
	ofItemAtURL: (OFURL *)URL
       attributeKey: (of_file_attribute_key_t)attributeKey
	 attributes: (of_file_attributes_t)attributes OF_DIRECT
{
#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
	OFString *path = URL.fileSystemRepresentation;
	uid_t uid = -1;
	gid_t gid = -1;
	of_string_encoding_t encoding;

	if (owner == nil && group == nil)
		@throw [OFInvalidArgumentException exception];

	encoding = [OFLocale encoding];

# ifdef OF_HAVE_THREADS







>
|
|
|
|
>
>




|
<

<
|
|





|



<
|
>














|


















|






|
|
|
|
|





|







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
	modificationTime -= locale->loc_GMTOffset * 60.0;
	CloseLocale(locale);

	date.ds_Days = modificationTime / 86400;
	date.ds_Minute = ((LONG)modificationTime % 86400) / 60;
	date.ds_Tick = fmod(modificationTime, 60) * TICKS_PER_SECOND;

# ifdef OF_AMIGAOS4
	if (!SetDate([path cStringWithEncoding: [OFLocale encoding]],
	    &date) != 0)
# else
	if (!SetFileDate([path cStringWithEncoding: [OFLocale encoding]],
	    &date) != 0)
# endif
		@throw [OFSetItemAttributesFailedException
		    exceptionWithURL: URL
			  attributes: attributes
		     failedAttribute: attributeKey
			       errNo: retrieveError()];

#else

	OFTimeInterval lastAccessTime = lastAccessDate.timeIntervalSince1970;
	OFTimeInterval modificationTime =
	    modificationDate.timeIntervalSince1970;
	struct timeval times[2] = {
		{
			.tv_sec = (time_t)lastAccessTime,
			.tv_usec =
			    (int)((lastAccessTime - times[0].tv_sec) * 1000000)
		},
		{
			.tv_sec = (time_t)modificationTime,

			.tv_usec = (int)((modificationTime - times[1].tv_sec) *
			    1000000)
		},
	};

	if (utimes([path cStringWithEncoding: [OFLocale encoding]], times) != 0)
		@throw [OFSetItemAttributesFailedException
		    exceptionWithURL: URL
			  attributes: attributes
		     failedAttribute: attributeKey
			       errNo: errno];
#endif
}

- (void)of_setPOSIXPermissions: (OFNumber *)permissions
		   ofItemAtURL: (OFURL *)URL
		    attributes: (OFFileAttributes)attributes OF_DIRECT
{
#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
	mode_t mode = (mode_t)permissions.unsignedLongValue;
	OFString *path = URL.fileSystemRepresentation;
	int status;

# ifdef OF_WINDOWS
	if ([OFSystemInfo isWindowsNT])
		status = _wchmod(path.UTF16String, mode);
	else
# endif
		status = chmod(
		    [path cStringWithEncoding: [OFLocale encoding]], mode);

	if (status != 0)
		@throw [OFSetItemAttributesFailedException
		    exceptionWithURL: URL
			  attributes: attributes
		     failedAttribute: OFFilePOSIXPermissions
			       errNo: errno];
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (void)of_setOwnerAccountName: (OFString *)owner
      andGroupOwnerAccountName: (OFString *)group
		   ofItemAtURL: (OFURL *)URL
		  attributeKey: (OFFileAttributeKey)attributeKey
		    attributes: (OFFileAttributes)attributes OF_DIRECT
{
#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
	OFString *path = URL.fileSystemRepresentation;
	uid_t uid = -1;
	gid_t gid = -1;
	OFStringEncoding encoding;

	if (owner == nil && group == nil)
		@throw [OFInvalidArgumentException exception];

	encoding = [OFLocale encoding];

# ifdef OF_HAVE_THREADS
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
		     failedAttribute: attributeKey
			       errNo: errno];
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (void)setAttributes: (of_file_attributes_t)attributes
	  ofItemAtURL: (OFURL *)URL
{
	void *pool = objc_autoreleasePoolPush();
	OFEnumerator OF_GENERIC(of_file_attribute_key_t) *keyEnumerator;
	OFEnumerator *objectEnumerator;
	of_file_attribute_key_t 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: of_file_attribute_key_modification_date] ||
		    [key isEqual: of_file_attribute_key_last_access_date])
			continue;
		else if ([key isEqual: of_file_attribute_key_posix_permissions])
			[self of_setPOSIXPermissions: object
					 ofItemAtURL: URL
					  attributes: attributes];
		else if ([key isEqual: of_file_attribute_key_owner])
			[self of_setOwner: object
				 andGroup: nil
			      ofItemAtURL: URL
			     attributeKey: key
			       attributes: attributes];
		else if ([key isEqual: of_file_attribute_key_group])
			[self of_setOwner: nil
				 andGroup: object
			      ofItemAtURL: URL
			     attributeKey: key
			       attributes: attributes];
		else
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];
	}

	lastAccessDate = [attributes
	    objectForKey: of_file_attribute_key_last_access_date];
	modificationDate = [attributes
	    objectForKey: of_file_attribute_key_modification_date];

	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();
	of_stat_t s;
	bool ret;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if (![URL.scheme isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	if (of_stat(URL.fileSystemRepresentation, &s) == -1) {
		objc_autoreleasePoolPop(pool);
		return false;
	}

	ret = S_ISREG(s.st_mode);

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)directoryExistsAtURL: (OFURL *)URL
{
	void *pool = objc_autoreleasePoolPush();
	of_stat_t s;
	bool ret;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if (![URL.scheme isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	if (of_stat(URL.fileSystemRepresentation, &s) == -1) {
		objc_autoreleasePoolPop(pool);
		return false;
	}

	ret = S_ISDIR(s.st_mode);

	objc_autoreleasePoolPop(pool);







<
|


|

|














|
|

|



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






|
<
|
<













|








|














|








|







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
		     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);
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
		@throw [OFCreateDirectoryFailedException
		    exceptionWithURL: URL
			       errNo: errno];
#elif defined(OF_AMIGAOS)
	BPTR lock;

	if ((lock = CreateDir(
	    [path cStringWithEncoding: [OFLocale encoding]])) == 0) {
		setErrno();

		@throw [OFCreateDirectoryFailedException
		    exceptionWithURL: URL
			       errNo: errno];
	}

	UnLock(lock);
#else
	if (mkdir([path cStringWithEncoding: [OFLocale encoding]], 0777) != 0)
		@throw [OFCreateDirectoryFailedException
		    exceptionWithURL: URL
			       errNo: errno];
#endif

	objc_autoreleasePoolPop(pool);
}

- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtURL: (OFURL *)URL
{
	OFMutableArray *files = [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) {
			int errNo = 0;

			if (GetLastError() == ERROR_FILE_NOT_FOUND)
				errNo = ENOENT;

			@throw [OFOpenItemFailedException
			    exceptionWithURL: URL
					mode: nil
				       errNo: errNo];
		}

		@try {
			do {
				OFString *file;

				if (wcscmp(fd.cFileName, L".") == 0 ||
				    wcscmp(fd.cFileName, L"..") == 0)
					continue;

				file = [[OFString alloc]
				    initWithUTF16String: fd.cFileName];
				@try {
					[files addObject: file];

				} @finally {
					[file release];
				}
			} while (FindNextFileW(handle, &fd));

			if (GetLastError() != ERROR_NO_MORE_FILES)
				@throw [OFReadFailedException
				    exceptionWithObject: self
					requestedLength: 0
						  errNo: EIO];
		} @finally {
			FindClose(handle);
		}
	} else {
		of_string_encoding_t encoding = [OFLocale encoding];
		WIN32_FIND_DATA fd;

		if ((handle = FindFirstFileA(
		    [path cStringWithEncoding: encoding], &fd)) ==
		    INVALID_HANDLE_VALUE) {
			int errNo = 0;

			if (GetLastError() == ERROR_FILE_NOT_FOUND)
				errNo = ENOENT;

			@throw [OFOpenItemFailedException
			    exceptionWithURL: URL
					mode: nil
				       errNo: errNo];
		}

		@try {
			do {
				OFString *file;

				if (strcmp(fd.cFileName, ".") == 0 ||
				    strcmp(fd.cFileName, "..") == 0)
					continue;

				file = [[OFString alloc]
				    initWithCString: fd.cFileName
					   encoding: encoding];
				@try {
					[files addObject: file];

				} @finally {
					[file release];
				}
			} while (FindNextFileA(handle, &fd));

			if (GetLastError() != ERROR_NO_MORE_FILES)
				@throw [OFReadFailedException
				    exceptionWithObject: self
					requestedLength: 0
						  errNo: EIO];
		} @finally {
			FindClose(handle);
		}
	}
#elif defined(OF_AMIGAOS)
	of_string_encoding_t encoding = [OFLocale encoding];
	BPTR lock;

	if ((lock = Lock([path cStringWithEncoding: encoding],
	    SHARED_LOCK)) == 0) {
		setErrno();

		@throw [OFOpenItemFailedException exceptionWithURL: URL

							      mode: nil
							     errNo: errno];
	}

	@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: 0];

		@try {
			while ((ed = ExamineDir(context)) != NULL) {
				OFString *file = [[OFString alloc]
				    initWithCString: ed->Name
					   encoding: encoding];

				@try {
					[files addObject: file];

				} @finally {
					[file release];
				}
			}
		} @finally {
			ReleaseDirContext(context);
		}
# else
		struct FileInfoBlock fib;

		if (!Examine(lock, &fib))
			@throw [OFOpenItemFailedException
			    exceptionWithURL: URL
					mode: nil
				       errNo: 0];

		while (ExNext(lock, &fib)) {
			OFString *file = [[OFString alloc]
			    initWithCString: fib.fib_FileName
				   encoding: encoding];

			@try {
				[files addObject: file];

			} @finally {
				[file release];
			}
		}
# endif

		if (IoErr() != ERROR_NO_MORE_ENTRIES)
			@throw [OFReadFailedException
			    exceptionWithObject: self
				requestedLength: 0
					  errNo: EIO];
	} @finally {
		UnLock(lock);
	}
#else
	of_string_encoding_t 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 {







|
<
<


|
<












|

|




















|
<
<
<
<
<



|
<












|
>









|




|




|
<
<
<
<
<



|
<













|
>









|





|



|
<
<
|
>
|
|
<












|








|
>














|





<

|
>










|




|

<







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
		@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 {
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
			if (strcmp(dirent->d_name, ".") == 0 ||
			    strcmp(dirent->d_name, "..") == 0)
				continue;

			file = [[OFString alloc] initWithCString: dirent->d_name
							encoding: encoding];
			@try {
				[files addObject: file];

			} @finally {
				[file release];
			}
		}
	} @finally {
		closedir(dir);
# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS)
		[readdirMutex unlock];
# endif
	}
#endif

	[files makeImmutable];

	objc_autoreleasePoolPop(pool);

	return files;
}

- (void)removeItemAtURL: (OFURL *)URL
{
	void *pool = objc_autoreleasePoolPush();
	OFString *path;

	of_stat_t s;

	if (URL == nil)
		@throw [OFInvalidArgumentException exception];

	if (![URL.scheme isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	path = URL.fileSystemRepresentation;

	if (of_lstat(path, &s) != 0)
		@throw [OFRemoveItemFailedException exceptionWithURL: URL
							       errNo: errno];

	if (S_ISDIR(s.st_mode)) {
		OFArray *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 (OFString *item in contents) {
			void *pool2 = objc_autoreleasePoolPush();

			[self removeItemAtURL: [OFURL fileURLWithPath:
			    [path stringByAppendingPathComponent: item]]];

			objc_autoreleasePoolPop(pool2);
		}

#ifndef OF_AMIGAOS
		int status;








|
>












|



|






>
|









|

|


|



















|


|
<







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

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
			@throw [OFRemoveItemFailedException
			    exceptionWithURL: URL
				       errNo: errno];
#endif
	}

#ifdef OF_AMIGAOS
	if (!DeleteFile([path cStringWithEncoding: [OFLocale encoding]])) {
		setErrno();

		@throw [OFRemoveItemFailedException exceptionWithURL: URL

							       errNo: errno];
	}
#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
	of_string_encoding_t encoding = [OFLocale encoding];

	if (link([sourcePath cStringWithEncoding: encoding],
	    [destinationPath cStringWithEncoding: encoding]) != 0)
		@throw [OFLinkFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: errno];
# else
	if (func_CreateHardLinkW == NULL)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	if (!func_CreateHardLinkW(destinationPath.UTF16String,
	    sourcePath.UTF16String, NULL))
		@throw [OFLinkFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: 0];
# endif

	objc_autoreleasePoolPop(pool);
}
#endif

#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS







|
<
<
|
>
|
<






|
<















|








|



|




|







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
			@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
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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482

	if (![URL.scheme isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	path = URL.fileSystemRepresentation;

# ifndef OF_WINDOWS
	of_string_encoding_t encoding = [OFLocale encoding];

	if (symlink([target cStringWithEncoding: encoding],
	    [path cStringWithEncoding: encoding]) != 0)
		@throw [OFCreateSymbolicLinkFailedException
		    exceptionWithURL: URL
			      target: target
			       errNo: errno];
# else
	if (func_CreateSymbolicLinkW == NULL)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	if (!func_CreateSymbolicLinkW(path.UTF16String, target.UTF16String, 0))

		@throw [OFCreateSymbolicLinkFailedException
		    exceptionWithURL: URL
			      target: target
			       errNo: 0];
# 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
	of_string_encoding_t encoding = [OFLocale encoding];

	if (!Rename([source.fileSystemRepresentation
	    cStringWithEncoding: encoding],
	    [destination.fileSystemRepresentation
	    cStringWithEncoding: encoding])) {
		setErrno();

		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: errno];
	}
#else
	int status;

# ifdef OF_WINDOWS
	if ([OFSystemInfo isWindowsNT])
		status = _wrename(source.fileSystemRepresentation.UTF16String,
		    destination.fileSystemRepresentation.UTF16String);
	else {
# endif
		of_string_encoding_t encoding = [OFLocale encoding];

		status = rename([source.fileSystemRepresentation
		    cStringWithEncoding: encoding],
		    [destination.fileSystemRepresentation
		    cStringWithEncoding: encoding]);
# ifdef OF_WINDOWS
	}







|








|



|
>



|






|
<
















|




|
<
<



|
<









|







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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462

	if (![URL.scheme isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	path = URL.fileSystemRepresentation;

# ifndef OF_WINDOWS
	OFStringEncoding encoding = [OFLocale encoding];

	if (symlink([target cStringWithEncoding: encoding],
	    [path cStringWithEncoding: encoding]) != 0)
		@throw [OFCreateSymbolicLinkFailedException
		    exceptionWithURL: URL
			      target: target
			       errNo: errno];
# else
	if (createSymbolicLinkWFuncPtr == NULL)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	if (!createSymbolicLinkWFuncPtr(path.UTF16String, target.UTF16String,
	    0))
		@throw [OFCreateSymbolicLinkFailedException
		    exceptionWithURL: URL
			      target: target
			       errNo: retrieveError()];
# endif

	objc_autoreleasePoolPop(pool);
}
#endif

- (bool)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination

{
	void *pool;

	if (![source.scheme isEqual: _scheme] ||
	    ![destination.scheme isEqual: _scheme])
		return false;

	if ([self fileExistsAtURL: destination])
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: EEXIST];

	pool = objc_autoreleasePoolPush();

#ifdef OF_AMIGAOS
	OFStringEncoding encoding = [OFLocale encoding];

	if (!Rename([source.fileSystemRepresentation
	    cStringWithEncoding: encoding],
	    [destination.fileSystemRepresentation
	    cStringWithEncoding: encoding]))


		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: retrieveError()];

#else
	int status;

# ifdef OF_WINDOWS
	if ([OFSystemInfo isWindowsNT])
		status = _wrename(source.fileSystemRepresentation.UTF16String,
		    destination.fileSystemRepresentation.UTF16String);
	else {
# endif
		OFStringEncoding encoding = [OFLocale encoding];

		status = rename([source.fileSystemRepresentation
		    cStringWithEncoding: encoding],
		    [destination.fileSystemRepresentation
		    cStringWithEncoding: encoding]);
# ifdef OF_WINDOWS
	}

Modified src/OFGZIPStream.h from [9a326fe541] to [40a91bbae5].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFStream.h"
#import "OFDate.h"

@class OFInflateStream;

OF_ASSUME_NONNULL_BEGIN






















/**
 * @class OFGZIPStream OFGZIPStream.h ObjFW/OFGZIPStream.h
 *
 * @brief A class that handles GZIP compression and decompression transparently
 *	  for an underlying stream.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFGZIPStream: OFStream
{
	OFStream *_stream;
	OFInflateStream *_Nullable _inflateStream;
	enum of_gzip_stream_state {
		OF_GZIP_STREAM_ID1,
		OF_GZIP_STREAM_ID2,
		OF_GZIP_STREAM_COMPRESSION_METHOD,
		OF_GZIP_STREAM_FLAGS,
		OF_GZIP_STREAM_MODIFICATION_TIME,
		OF_GZIP_STREAM_EXTRA_FLAGS,
		OF_GZIP_STREAM_OPERATING_SYSTEM,
		OF_GZIP_STREAM_EXTRA_LENGTH,
		OF_GZIP_STREAM_EXTRA,
		OF_GZIP_STREAM_NAME,
		OF_GZIP_STREAM_COMMENT,
		OF_GZIP_STREAM_HEADER_CRC16,
		OF_GZIP_STREAM_DATA,
		OF_GZIP_STREAM_CRC32,
		OF_GZIP_STREAM_UNCOMPRESSED_SIZE
	} _state;
	enum of_gzip_stream_flags {
		OF_GZIP_STREAM_FLAG_TEXT	 = 0x01,
		OF_GZIP_STREAM_FLAG_HEADER_CRC16 = 0x02,
		OF_GZIP_STREAM_FLAG_EXTRA	 = 0x04,
		OF_GZIP_STREAM_FLAG_NAME	 = 0x08,
		OF_GZIP_STREAM_FLAG_COMMENT	 = 0x10
	} _flags;
	uint8_t _extraFlags;
	enum of_gzip_stream_operating_system {
		OF_GZIP_STREAM_OPERATING_SYSTEM_FAT		=   0,
		OF_GZIP_STREAM_OPERATING_SYSTEM_AMIGA		=   1,
		OF_GZIP_STREAM_OPERATING_SYSTEM_VMS		=   2,
		OF_GZIP_STREAM_OPERATING_SYSTEM_UNIX		=   3,
		OF_GZIP_STREAM_OPERATING_SYSTEM_VM_CMS		=   4,
		OF_GZIP_STREAM_OPERATING_SYSTEM_ATARI_TOS	=   5,
		OF_GZIP_STREAM_OPERATING_SYSTEM_HPFS		=   6,
		OF_GZIP_STREAM_OPERATING_SYSTEM_MACINTOSH	=   7,
		OF_GZIP_STREAM_OPERATING_SYSTEM_Z_SYSTEM	=   8,
		OF_GZIP_STREAM_OPERATING_SYSTEM_CP_M		=   9,
		OF_GZIP_STREAM_OPERATING_SYSTEM_TOPS_20		=  10,
		OF_GZIP_STREAM_OPERATING_SYSTEM_NTFS		=  11,
		OF_GZIP_STREAM_OPERATING_SYSTEM_QDO		=  12,
		OF_GZIP_STREAM_OPERATING_SYSTEM_ACORN_RISC_OS	=  13,
		OF_GZIP_STREAM_OPERATING_SYSTEM_UNKNOWN		= 255
	} _operatingSystemMadeOn;
	size_t _bytesRead;
	uint8_t _buffer[4];
	OFDate *_Nullable _modificationDate;
	uint16_t _extraLength;
	uint32_t _CRC32, _uncompressedSize;
}

/**
 * @brief The operating system on which the data was compressed.
 *
 * This property is only guaranteed to be available once @ref atEndOfStream is
 * true.
 */
@property (readonly, nonatomic)
    enum of_gzip_stream_operating_system operatingSystemMadeOn;

/**
 * @brief The modification date of the original file.
 *
 * This property is only guaranteed to be available once @ref atEndOfStream is
 * true.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFDate *modificationDate;

/**
 * @brief Creates a new OFGZIPStream with the specified underlying stream.
 *
 * @param stream The underlying stream for the OFGZIPStream
 * @param mode The mode for the OFGZIPStream. Valid modes are "r" for reading
 *	       and "w" for writing.
 * @return A new, autoreleased OFGZIPStream
 */
+ (instancetype)streamWithStream: (OFStream *)stream
			    mode: (OFString *)mode;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFGZIPStream with the specified
 *	  underlying stream.
 *

<
<
|




















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











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

|
|
|
|
|
|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|














|

















|
<







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


 * 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 "OFStream.h"
#import "OFDate.h"

@class OFInflateStream;

OF_ASSUME_NONNULL_BEGIN

/**
 * @brief The operating system on which compressed the data.
 */
typedef enum {
	OFGZIPStreamOperatingSystemFAT	       =   0,
	OFGZIPStreamOperatingSystemAmiga       =   1,
	OFGZIPStreamOperatingSystemVMS	       =   2,
	OFGZIPStreamOperatingSystemUNIX	       =   3,
	OFGZIPStreamOperatingSystemVM_CMS      =   4,
	OFGZIPStreamOperatingSystemAtariTOS    =   5,
	OFGZIPStreamOperatingSystemHPFS	       =   6,
	OFGZIPStreamOperatingSystemMacintosh   =   7,
	OFGZIPStreamOperatingSystemZSystem     =   8,
	OFGZIPStreamOperatingSystemCPM	       =   9,
	OFGZIPStreamOperatingSystemTOPS20      =  10,
	OFGZIPStreamOperatingSystemNTFS	       =  11,
	OFGZIPStreamOperatingSystemQDO	       =  12,
	OFGZIPStreamOperatingSystemAcornRISCOS =  13,
	OFGZIPStreamOperatingSystemUnknown     = 255
} OFGZIPStreamOperatingSystem;

/**
 * @class OFGZIPStream OFGZIPStream.h ObjFW/OFGZIPStream.h
 *
 * @brief A class that handles GZIP compression and decompression transparently
 *	  for an underlying stream.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFGZIPStream: OFStream
{
	OFStream *_stream;
	OFInflateStream *_Nullable _inflateStream;
	enum {
		OFGZIPStreamStateID1,
		OFGZIPStreamStateID2,
		OFGZIPStreamStateCompressionMethod,
		OFGZIPStreamStateFlags,
		OFGZIPStreamStateModificationDate,
		OFGZIPStreamStateExtraFlags,
		OFGZIPStreamStateOperatingSystem,
		OFGZIPStreamStateExtraLength,
		OFGZIPStreamStateExtra,
		OFGZIPStreamStateName,
		OFGZIPStreamStateComment,
		OFGZIPStreamStateHeaderCRC16,
		OFGZIPStreamStateData,
		OFGZIPStreamStateCRC32,
		OFGZIPStreamStateUncompressedSize
	} _state;
	enum {
		OFGZIPStreamFlagText	    = 0x01,
		OFGZIPStreamFlagHeaderCRC16 = 0x02,
		OFGZIPStreamFlagExtra	    = 0x04,
		OFGZIPStreamFlagName	    = 0x08,
		OFGZIPStreamFlagComment	    = 0x10
	} _flags;
	uint8_t _extraFlags;
















	OFGZIPStreamOperatingSystem _operatingSystemMadeOn;
	size_t _bytesRead;
	uint8_t _buffer[4];
	OFDate *_Nullable _modificationDate;
	uint16_t _extraLength;
	uint32_t _CRC32, _uncompressedSize;
}

/**
 * @brief The operating system on which the data was compressed.
 *
 * This property is only guaranteed to be available once @ref atEndOfStream is
 * true.
 */
@property (readonly, nonatomic)
    OFGZIPStreamOperatingSystem operatingSystemMadeOn;

/**
 * @brief The modification date of the original file.
 *
 * This property is only guaranteed to be available once @ref atEndOfStream is
 * true.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFDate *modificationDate;

/**
 * @brief Creates a new OFGZIPStream with the specified underlying stream.
 *
 * @param stream The underlying stream for the OFGZIPStream
 * @param mode The mode for the OFGZIPStream. Valid modes are "r" for reading
 *	       and "w" for writing.
 * @return A new, autoreleased OFGZIPStream
 */
+ (instancetype)streamWithStream: (OFStream *)stream mode: (OFString *)mode;


- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFGZIPStream with the specified
 *	  underlying stream.
 *

Modified src/OFGZIPStream.m from [42379805f3] to [947da42ac6].

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
268
269
270
271
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFGZIPStream.h"
#import "OFInflateStream.h"
#import "OFDate.h"

#import "crc32.h"

#import "OFChecksumMismatchException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFTruncatedDataException.h"

@implementation OFGZIPStream
@synthesize operatingSystemMadeOn = _operatingSystemMadeOn;
@synthesize modificationDate = _modificationDate;

+ (instancetype)streamWithStream: (OFStream *)stream
			    mode: (OFString *)mode
{
	return [[[self alloc] initWithStream: stream
					mode: mode] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OFStream *)stream
			  mode: (OFString *)mode
{
	self = [super init];

	@try {
		if (![mode isEqual: @"r"])
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: nil];

		_stream = [stream retain];
		_operatingSystemMadeOn =
		    OF_GZIP_STREAM_OPERATING_SYSTEM_UNKNOWN;
		_CRC32 = ~0;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_stream != nil)
		[self close];

	[_inflateStream release];
	[_modificationDate release];

	[super dealloc];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	for (;;) {
		uint8_t byte;
		uint32_t CRC32, uncompressedSize;

		if (_stream.atEndOfStream) {
			if (_state != OF_GZIP_STREAM_ID1)
				@throw [OFTruncatedDataException exception];

			return 0;
		}

		switch (_state) {
		case OF_GZIP_STREAM_ID1:
		case OF_GZIP_STREAM_ID2:
		case OF_GZIP_STREAM_COMPRESSION_METHOD:
			if ([_stream readIntoBuffer: &byte
					     length: 1] < 1)
				return 0;

			if ((_state == OF_GZIP_STREAM_ID1 && byte != 0x1F) ||
			    (_state == OF_GZIP_STREAM_ID2 && byte != 0x8B) ||
			    (_state == OF_GZIP_STREAM_COMPRESSION_METHOD &&
			    byte != 8))
				@throw [OFInvalidFormatException exception];

			_state++;
			break;
		case OF_GZIP_STREAM_FLAGS:
			if ([_stream readIntoBuffer: &byte
					     length: 1] < 1)
				return 0;

			_flags = byte;
			_state++;
			break;
		case OF_GZIP_STREAM_MODIFICATION_TIME:
			_bytesRead += [_stream
			    readIntoBuffer: _buffer + _bytesRead
				    length: 4 - _bytesRead];

			if (_bytesRead < 4)
				return 0;

			[_modificationDate release];
			_modificationDate = nil;

			_modificationDate = [[OFDate alloc]
			    initWithTimeIntervalSince1970:
			    (_buffer[3] << 24) | (_buffer[2] << 16) |
			    (_buffer[1] << 8) | _buffer[0]];

			_bytesRead = 0;
			_state++;
			break;
		case OF_GZIP_STREAM_EXTRA_FLAGS:
			if ([_stream readIntoBuffer: &byte
					     length: 1] < 1)
				return 0;

			_extraFlags = byte;
			_state++;
			break;
		case OF_GZIP_STREAM_OPERATING_SYSTEM:
			if ([_stream readIntoBuffer: &byte
					     length: 1] < 1)
				return 0;

			_operatingSystemMadeOn = byte;
			_state++;
			break;
		case OF_GZIP_STREAM_EXTRA_LENGTH:
			if (!(_flags & OF_GZIP_STREAM_FLAG_EXTRA)) {
				_state += 2;
				break;
			}

			_bytesRead += [_stream
			    readIntoBuffer: _buffer + _bytesRead
				    length: 2 - _bytesRead];

			if (_bytesRead < 2)
				return 0;

			_extraLength = (_buffer[1] << 8) | _buffer[0];
			_bytesRead = 0;
			_state++;
			break;
		case OF_GZIP_STREAM_EXTRA:
			{
				char tmp[512];
				size_t toRead = _extraLength - _bytesRead;

				if (toRead > 512)
					toRead = 512;

				_bytesRead += [_stream readIntoBuffer: tmp
							       length: toRead];
			}

			if (_bytesRead < _extraLength)
				return 0;

			_bytesRead = 0;
			_state++;
			break;
		case OF_GZIP_STREAM_NAME:
			if (!(_flags & OF_GZIP_STREAM_FLAG_NAME)) {
				_state++;
				break;
			}

			do {
				if ([_stream readIntoBuffer: &byte
						     length: 1] < 1)
					return 0;
			} while (byte != 0);

			_state++;
			break;
		case OF_GZIP_STREAM_COMMENT:
			if (!(_flags & OF_GZIP_STREAM_FLAG_COMMENT)) {
				_state++;
				break;
			}

			do {
				if ([_stream readIntoBuffer: &byte
						     length: 1] < 1)
					return 0;
			} while (byte != 0);

			_state++;
			break;
		case OF_GZIP_STREAM_HEADER_CRC16:
			if (!(_flags & OF_GZIP_STREAM_FLAG_HEADER_CRC16)) {
				_state++;
				break;
			}

			_bytesRead += [_stream
			    readIntoBuffer: _buffer + _bytesRead
				    length: 2 - _bytesRead];

			if (_bytesRead < 2)
				return 0;

			/*
			 * Header CRC16 is not checked, as I could not find a
			 * single file in the wild that actually has a header
			 * CRC16 - and thus no file to test against.
			 */

			_bytesRead = 0;
			_state++;
			break;
		case OF_GZIP_STREAM_DATA:
			if (_inflateStream == nil)
				_inflateStream = [[OFInflateStream alloc]
				    initWithStream: _stream];

			if (!_inflateStream.atEndOfStream) {
				size_t bytesRead = [_inflateStream
				    readIntoBuffer: buffer
					    length: length];

				_CRC32 = of_crc32(_CRC32, buffer, bytesRead);
				_uncompressedSize += bytesRead;

				return bytesRead;
			}

			[_inflateStream release];
			_inflateStream = nil;

			_state++;
			break;
		case OF_GZIP_STREAM_CRC32:
			_bytesRead += [_stream readIntoBuffer: _buffer
						       length: 4 - _bytesRead];

			if (_bytesRead < 4)
				return 0;

			CRC32 = ((uint32_t)_buffer[3] << 24) |

<
<
|
















|

<
|











|
<

|
<







|
<










|
<




















|
<









|






|
|
|
|
<


|
|
|





|
|
<





|


















|
|
<





|
|
<





|
|















|

















|
|












|
|












|
|




















|









|










|







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


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

#import "OFGZIPStream.h"
#import "OFCRC32.h"
#import "OFDate.h"

#import "OFInflateStream.h"

#import "OFChecksumMismatchException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFTruncatedDataException.h"

@implementation OFGZIPStream
@synthesize operatingSystemMadeOn = _operatingSystemMadeOn;
@synthesize modificationDate = _modificationDate;

+ (instancetype)streamWithStream: (OFStream *)stream mode: (OFString *)mode

{
	return [[[self alloc] initWithStream: stream mode: mode] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OFStream *)stream mode: (OFString *)mode

{
	self = [super init];

	@try {
		if (![mode isEqual: @"r"])
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: nil];

		_stream = [stream retain];
		_operatingSystemMadeOn = OFGZIPStreamOperatingSystemUnknown;

		_CRC32 = ~0;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_stream != nil)
		[self close];

	[_inflateStream release];
	[_modificationDate release];

	[super dealloc];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	for (;;) {
		uint8_t byte;
		uint32_t CRC32, uncompressedSize;

		if (_stream.atEndOfStream) {
			if (_state != OFGZIPStreamStateID1)
				@throw [OFTruncatedDataException exception];

			return 0;
		}

		switch (_state) {
		case OFGZIPStreamStateID1:
		case OFGZIPStreamStateID2:
		case OFGZIPStreamStateCompressionMethod:
			if ([_stream readIntoBuffer: &byte length: 1] < 1)

				return 0;

			if ((_state == OFGZIPStreamStateID1 && byte != 0x1F) ||
			    (_state == OFGZIPStreamStateID2 && byte != 0x8B) ||
			    (_state == OFGZIPStreamStateCompressionMethod &&
			    byte != 8))
				@throw [OFInvalidFormatException exception];

			_state++;
			break;
		case OFGZIPStreamStateFlags:
			if ([_stream readIntoBuffer: &byte length: 1] < 1)

				return 0;

			_flags = byte;
			_state++;
			break;
		case OFGZIPStreamStateModificationDate:
			_bytesRead += [_stream
			    readIntoBuffer: _buffer + _bytesRead
				    length: 4 - _bytesRead];

			if (_bytesRead < 4)
				return 0;

			[_modificationDate release];
			_modificationDate = nil;

			_modificationDate = [[OFDate alloc]
			    initWithTimeIntervalSince1970:
			    (_buffer[3] << 24) | (_buffer[2] << 16) |
			    (_buffer[1] << 8) | _buffer[0]];

			_bytesRead = 0;
			_state++;
			break;
		case OFGZIPStreamStateExtraFlags:
			if ([_stream readIntoBuffer: &byte length: 1] < 1)

				return 0;

			_extraFlags = byte;
			_state++;
			break;
		case OFGZIPStreamStateOperatingSystem:
			if ([_stream readIntoBuffer: &byte length: 1] < 1)

				return 0;

			_operatingSystemMadeOn = byte;
			_state++;
			break;
		case OFGZIPStreamStateExtraLength:
			if (!(_flags & OFGZIPStreamFlagExtra)) {
				_state += 2;
				break;
			}

			_bytesRead += [_stream
			    readIntoBuffer: _buffer + _bytesRead
				    length: 2 - _bytesRead];

			if (_bytesRead < 2)
				return 0;

			_extraLength = (_buffer[1] << 8) | _buffer[0];
			_bytesRead = 0;
			_state++;
			break;
		case OFGZIPStreamStateExtra:
			{
				char tmp[512];
				size_t toRead = _extraLength - _bytesRead;

				if (toRead > 512)
					toRead = 512;

				_bytesRead += [_stream readIntoBuffer: tmp
							       length: toRead];
			}

			if (_bytesRead < _extraLength)
				return 0;

			_bytesRead = 0;
			_state++;
			break;
		case OFGZIPStreamStateName:
			if (!(_flags & OFGZIPStreamFlagName)) {
				_state++;
				break;
			}

			do {
				if ([_stream readIntoBuffer: &byte
						     length: 1] < 1)
					return 0;
			} while (byte != 0);

			_state++;
			break;
		case OFGZIPStreamStateComment:
			if (!(_flags & OFGZIPStreamFlagComment)) {
				_state++;
				break;
			}

			do {
				if ([_stream readIntoBuffer: &byte
						     length: 1] < 1)
					return 0;
			} while (byte != 0);

			_state++;
			break;
		case OFGZIPStreamStateHeaderCRC16:
			if (!(_flags & OFGZIPStreamFlagHeaderCRC16)) {
				_state++;
				break;
			}

			_bytesRead += [_stream
			    readIntoBuffer: _buffer + _bytesRead
				    length: 2 - _bytesRead];

			if (_bytesRead < 2)
				return 0;

			/*
			 * Header CRC16 is not checked, as I could not find a
			 * single file in the wild that actually has a header
			 * CRC16 - and thus no file to test against.
			 */

			_bytesRead = 0;
			_state++;
			break;
		case OFGZIPStreamStateData:
			if (_inflateStream == nil)
				_inflateStream = [[OFInflateStream alloc]
				    initWithStream: _stream];

			if (!_inflateStream.atEndOfStream) {
				size_t bytesRead = [_inflateStream
				    readIntoBuffer: buffer
					    length: length];

				_CRC32 = OFCRC32(_CRC32, buffer, bytesRead);
				_uncompressedSize += bytesRead;

				return bytesRead;
			}

			[_inflateStream release];
			_inflateStream = nil;

			_state++;
			break;
		case OFGZIPStreamStateCRC32:
			_bytesRead += [_stream readIntoBuffer: _buffer
						       length: 4 - _bytesRead];

			if (_bytesRead < 4)
				return 0;

			CRC32 = ((uint32_t)_buffer[3] << 24) |
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
					       expectedChecksum: expected];
			}

			_bytesRead = 0;
			_CRC32 = ~0;
			_state++;
			break;
		case OF_GZIP_STREAM_UNCOMPRESSED_SIZE:
			_bytesRead += [_stream readIntoBuffer: _buffer
						       length: 4 - _bytesRead];

			uncompressedSize = ((uint32_t)_buffer[3] << 24) |
			    (_buffer[2] << 16) | (_buffer[1] << 8) | _buffer[0];
			if (_uncompressedSize != uncompressedSize) {
				OFString *actual = [OFString stringWithFormat:
				    @"%" PRIu32, _uncompressedSize];
				OFString *expected = [OFString stringWithFormat:
				    @"%" PRIu32, uncompressedSize];

				@throw [OFChecksumMismatchException
				    exceptionWithActualChecksum: actual
					       expectedChecksum: expected];
			}

			_bytesRead = 0;
			_uncompressedSize = 0;
			_state = OF_GZIP_STREAM_ID1;
			break;
		}
	}
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _stream.atEndOfStream;
}

- (bool)hasDataInReadBuffer
{
	if (_state == OF_GZIP_STREAM_DATA)
		return (super.hasDataInReadBuffer ||
		    _inflateStream.hasDataInReadBuffer);

	return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer);
}

- (void)close







|


















|















|







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
					       expectedChecksum: expected];
			}

			_bytesRead = 0;
			_CRC32 = ~0;
			_state++;
			break;
		case OFGZIPStreamStateUncompressedSize:
			_bytesRead += [_stream readIntoBuffer: _buffer
						       length: 4 - _bytesRead];

			uncompressedSize = ((uint32_t)_buffer[3] << 24) |
			    (_buffer[2] << 16) | (_buffer[1] << 8) | _buffer[0];
			if (_uncompressedSize != uncompressedSize) {
				OFString *actual = [OFString stringWithFormat:
				    @"%" PRIu32, _uncompressedSize];
				OFString *expected = [OFString stringWithFormat:
				    @"%" PRIu32, uncompressedSize];

				@throw [OFChecksumMismatchException
				    exceptionWithActualChecksum: actual
					       expectedChecksum: expected];
			}

			_bytesRead = 0;
			_uncompressedSize = 0;
			_state = OFGZIPStreamStateID1;
			break;
		}
	}
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _stream.atEndOfStream;
}

- (bool)hasDataInReadBuffer
{
	if (_state == OFGZIPStreamStateData)
		return (super.hasDataInReadBuffer ||
		    _inflateStream.hasDataInReadBuffer);

	return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer);
}

- (void)close

Modified src/OFHMAC.h from [e53629b1a1] to [618167fc85].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCryptoHash.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFHMAC OFHMAC.h ObjFW/OFHMAC.h
 *
 * @brief A class which provides methods to calculate an HMAC.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFHMAC: OFObject
{
	Class <OFCryptoHash> _hashClass;
	bool _allowsSwappableMemory;
	id <OFCryptoHash> _Nullable _outerHash, _innerHash;
	id <OFCryptoHash> _Nullable _outerHashCopy, _innerHashCopy;
	bool _calculated;
}

/**
 * @brief The class for the cryptographic hash used by the HMAC.
 */
@property (readonly, nonatomic) Class <OFCryptoHash> hashClass;

/**
 * @brief Whether data may be stored in swappable memory.
 */
@property (readonly, nonatomic) bool allowsSwappableMemory;

/**

<
<
|














|











|

|
|






|







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


 * 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 "OFCryptographicHash.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFHMAC OFHMAC.h ObjFW/OFHMAC.h
 *
 * @brief A class which provides methods to calculate an HMAC.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFHMAC: OFObject
{
	Class <OFCryptographicHash> _hashClass;
	bool _allowsSwappableMemory;
	id <OFCryptographicHash> _Nullable _outerHash, _innerHash;
	id <OFCryptographicHash> _Nullable _outerHashCopy, _innerHashCopy;
	bool _calculated;
}

/**
 * @brief The class for the cryptographic hash used by the HMAC.
 */
@property (readonly, nonatomic) Class <OFCryptographicHash> hashClass;

/**
 * @brief Whether data may be stored in swappable memory.
 */
@property (readonly, nonatomic) bool allowsSwappableMemory;

/**
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
/**
 * @brief Returns a new OFHMAC with the specified hashing algorithm.
 *
 * @param hashClass The class of the hashing algorithm
 * @param allowsSwappableMemory Whether data may be stored in swappable memory
 * @return A new, autoreleased OFHMAC
 */
+ (instancetype)HMACWithHashClass: (Class <OFCryptoHash>)hashClass
	    allowsSwappableMemory: (bool)allowsSwappableMemory;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initialized an already allocated OFHMAC with the specified hashing
 *	  algorithm.
 *
 * @param hashClass The class of the hashing algorithm
 * @param allowsSwappableMemory Whether data may be stored in swappable memory
 * @return An initialized OFHMAC
 */
- (instancetype)initWithHashClass: (Class <OFCryptoHash>)hashClass
	    allowsSwappableMemory: (bool)allowsSwappableMemory
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Sets the key for the HMAC.
 *
 * @note This resets the HMAC!
 *
 * @warning This invalidates any pointer previously returned by @ref digest. If
 *	    you are still interested in the previous digest, you need to memcpy
 *	    it yourself before calling @ref setKey:length:!
 *
 * @param key The key for the HMAC
 * @param length The length of the key for the HMAC
 */
- (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 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
 *	 be calculated efficiently. If you want to reset both, use
 *	 @ref setKey:length:.







|












|















|
<







|
|
>
>
>
>







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
/**
 * @brief Returns a new OFHMAC with the specified hashing algorithm.
 *
 * @param hashClass The class of the hashing algorithm
 * @param allowsSwappableMemory Whether data may be stored in swappable memory
 * @return A new, autoreleased OFHMAC
 */
+ (instancetype)HMACWithHashClass: (Class <OFCryptographicHash>)hashClass
	    allowsSwappableMemory: (bool)allowsSwappableMemory;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initialized an already allocated OFHMAC with the specified hashing
 *	  algorithm.
 *
 * @param hashClass The class of the hashing algorithm
 * @param allowsSwappableMemory Whether data may be stored in swappable memory
 * @return An initialized OFHMAC
 */
- (instancetype)initWithHashClass: (Class <OFCryptographicHash>)hashClass
	    allowsSwappableMemory: (bool)allowsSwappableMemory
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Sets the key for the HMAC.
 *
 * @note This resets the HMAC!
 *
 * @warning This invalidates any pointer previously returned by @ref digest. If
 *	    you are still interested in the previous digest, you need to memcpy
 *	    it yourself before calling @ref setKey:length:!
 *
 * @param key The key for the HMAC
 * @param length The length of the key for the HMAC
 */
- (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
 *	 be calculated efficiently. If you want to reset both, use
 *	 @ref setKey:length:.

Modified src/OFHMAC.m from [e732705f70] to [2ceebc7fc2].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFHMAC.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"

#import "OFInvalidArgumentException.h"

@implementation OFHMAC
@synthesize hashClass = _hashClass;
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

+ (instancetype)HMACWithHashClass: (Class <OFCryptoHash>)class
	    allowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithHashClass: class
			  allowsSwappableMemory: allowsSwappableMemory]
	    autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithHashClass: (Class <OFCryptoHash>)class
	    allowsSwappableMemory: (bool)allowsSwappableMemory
{
	self = [super init];

	_hashClass = class;
	_allowsSwappableMemory = allowsSwappableMemory;

	return self;
}

- (void)dealloc
{
	[_outerHash release];
	[_innerHash release];
	[_outerHashCopy release];
	[_innerHashCopy release];

	[super dealloc];
}

- (void)setKey: (const void *)key
	length: (size_t)length
{
	void *pool = objc_autoreleasePoolPush();
	size_t blockSize = [_hashClass blockSize];
	OFSecureData *outerKeyPad = [OFSecureData
		    dataWithCount: blockSize
	    allowsSwappableMemory: _allowsSwappableMemory];
	OFSecureData *innerKeyPad = [OFSecureData
		    dataWithCount: blockSize
	    allowsSwappableMemory: _allowsSwappableMemory];
	unsigned char *outerKeyPadItems = outerKeyPad.mutableItems;
	unsigned char *innerKeyPadItems = innerKeyPad.mutableItems;

	[_outerHash release];
	[_innerHash release];
	[_outerHashCopy release];
	[_innerHashCopy release];
	_outerHash = _innerHash = _outerHashCopy = _innerHashCopy = nil;

	@try {
		if (length > blockSize) {
			id <OFCryptoHash> hash = [_hashClass
			    cryptoHashWithAllowsSwappableMemory:
			    _allowsSwappableMemory];

			[hash updateWithBuffer: key
					length: length];


			length = hash.digestSize;
			if OF_UNLIKELY (length > blockSize)
				length = blockSize;

			memcpy(outerKeyPadItems, hash.digest, length);
			memcpy(innerKeyPadItems, hash.digest, length);
		} else {
			memcpy(outerKeyPadItems, key, length);
			memcpy(innerKeyPadItems, key, length);
		}

		memset(outerKeyPadItems + length, 0, blockSize - length);
		memset(innerKeyPadItems + length, 0, blockSize - length);

		for (size_t i = 0; i < blockSize; i++) {
			outerKeyPadItems[i] ^= 0x5C;
			innerKeyPadItems[i] ^= 0x36;
		}

		_outerHash = [[_hashClass cryptoHashWithAllowsSwappableMemory:
		    _allowsSwappableMemory] retain];
		_innerHash = [[_hashClass cryptoHashWithAllowsSwappableMemory:
		    _allowsSwappableMemory] retain];

		[_outerHash updateWithBuffer: outerKeyPadItems
				      length: blockSize];
		[_innerHash updateWithBuffer: innerKeyPadItems
				      length: blockSize];
	} @catch (id e) {

<
<
|



















>






|












|




















|
<




















|
|

<
|
<
>




















|

|







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


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

#import "OFHMAC.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"
#import "OFHashNotCalculatedException.h"
#import "OFInvalidArgumentException.h"

@implementation OFHMAC
@synthesize hashClass = _hashClass;
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

+ (instancetype)HMACWithHashClass: (Class <OFCryptographicHash>)class
	    allowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithHashClass: class
			  allowsSwappableMemory: allowsSwappableMemory]
	    autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithHashClass: (Class <OFCryptographicHash>)class
	    allowsSwappableMemory: (bool)allowsSwappableMemory
{
	self = [super init];

	_hashClass = class;
	_allowsSwappableMemory = allowsSwappableMemory;

	return self;
}

- (void)dealloc
{
	[_outerHash release];
	[_innerHash release];
	[_outerHashCopy release];
	[_innerHashCopy release];

	[super dealloc];
}

- (void)setKey: (const void *)key length: (size_t)length

{
	void *pool = objc_autoreleasePoolPush();
	size_t blockSize = [_hashClass blockSize];
	OFSecureData *outerKeyPad = [OFSecureData
		    dataWithCount: blockSize
	    allowsSwappableMemory: _allowsSwappableMemory];
	OFSecureData *innerKeyPad = [OFSecureData
		    dataWithCount: blockSize
	    allowsSwappableMemory: _allowsSwappableMemory];
	unsigned char *outerKeyPadItems = outerKeyPad.mutableItems;
	unsigned char *innerKeyPadItems = innerKeyPad.mutableItems;

	[_outerHash release];
	[_innerHash release];
	[_outerHashCopy release];
	[_innerHashCopy release];
	_outerHash = _innerHash = _outerHashCopy = _innerHashCopy = nil;

	@try {
		if (length > blockSize) {
			id <OFCryptographicHash> hash = [_hashClass
			    hashWithAllowsSwappableMemory:
			    _allowsSwappableMemory];

			[hash updateWithBuffer: key length: length];

			[hash calculate];

			length = hash.digestSize;
			if OF_UNLIKELY (length > blockSize)
				length = blockSize;

			memcpy(outerKeyPadItems, hash.digest, length);
			memcpy(innerKeyPadItems, hash.digest, length);
		} else {
			memcpy(outerKeyPadItems, key, length);
			memcpy(innerKeyPadItems, key, length);
		}

		memset(outerKeyPadItems + length, 0, blockSize - length);
		memset(innerKeyPadItems + length, 0, blockSize - length);

		for (size_t i = 0; i < blockSize; i++) {
			outerKeyPadItems[i] ^= 0x5C;
			innerKeyPadItems[i] ^= 0x36;
		}

		_outerHash = [[_hashClass hashWithAllowsSwappableMemory:
		    _allowsSwappableMemory] retain];
		_innerHash = [[_hashClass hashWithAllowsSwappableMemory:
		    _allowsSwappableMemory] retain];

		[_outerHash updateWithBuffer: outerKeyPadItems
				      length: blockSize];
		[_innerHash updateWithBuffer: innerKeyPadItems
				      length: blockSize];
	} @catch (id e) {
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

	_outerHashCopy = [_outerHash copy];
	_innerHashCopy = [_innerHash copy];

	_calculated = false;
}

- (void)updateWithBuffer: (const void *)buffer
		  length: (size_t)length
{
	if (_innerHash == nil)
		@throw [OFInvalidArgumentException exception];

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

	[_innerHash updateWithBuffer: buffer













			      length: length];


}

- (const unsigned char *)digest
{
	if (_outerHash == nil || _innerHash == nil)
		@throw [OFInvalidArgumentException exception];

	if (_calculated)
		return _outerHash.digest;

	[_outerHash updateWithBuffer: _innerHash.digest
			      length: _innerHash.digestSize];
	_calculated = true;

	return _outerHash.digest;
}

- (size_t)digestSize
{
	return [_hashClass digestSize];







|
<








|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>




<
<
<
|
<
|
<
<
<







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

	_outerHashCopy = [_outerHash copy];
	_innerHashCopy = [_innerHash copy];

	_calculated = false;
}

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

{
	if (_innerHash == nil)
		@throw [OFInvalidArgumentException exception];

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

	[_innerHash updateWithBuffer: buffer length: length];
}

- (void)calculate
{
	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

	if (_outerHash == nil || _innerHash == nil)
		@throw [OFInvalidArgumentException exception];

	[_innerHash calculate];
	[_outerHash updateWithBuffer: _innerHash.digest
			      length: _innerHash.digestSize];
	[_outerHash calculate];
	_calculated = true;
}

- (const unsigned char *)digest
{



	if (!_calculated)

		@throw [OFHashNotCalculatedException exceptionWithObject: self];




	return _outerHash.digest;
}

- (size_t)digestSize
{
	return [_hashClass digestSize];

Modified src/OFHTTPClient.h from [428c441451] to [4532ba7b26].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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 OFURL;

/**
 * @protocol OFHTTPClientDelegate OFHTTPClient.h ObjFW/OFHTTPClient.h
 *
 * @brief A delegate for OFHTTPClient.
 */







>







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

@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.
 */
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
-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (nullable OFHTTPResponse *)response
	  exception: (nullable id)exception;

@optional
/**
 * @brief A callback which is called when an OFHTTPClient creates a socket.
 *
 * This is useful if the connection is using HTTPS and the server requires a
 * client certificate. This callback can then be used to tell the TLS socket
 * about the certificate. Another use case is to tell the socket about a SOCKS5
 * proxy it should use for this connection.
 *
 * @param client The OFHTTPClient that created a socket
 * @param socket The socket created by the OFHTTPClient
 * @param request The request for which the socket was created
 */
-    (void)client: (OFHTTPClient *)client
  didCreateSocket: (OFTCPSocket *)socket
	  request: (OFHTTPRequest *)request;















/**
 * @brief A callback which is called when an OFHTTPClient wants to send the
 *	  body for a request.
 *
 * @param client The OFHTTPClient that wants to send the body
 * @param requestBody A stream into which the body of the request should be







|

<
<
|
|

|
|
|

|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (nullable OFHTTPResponse *)response
	  exception: (nullable id)exception;

@optional
/**
 * @brief A callback which is called when an OFHTTPClient creates a TCP socket.
 *


 * This can be used to tell the socket about a SOCKS5 proxy it should use for
 * this connection.
 *
 * @param client The OFHTTPClient that created a TCP socket
 * @param TCPSocket The socket created by the OFHTTPClient
 * @param request The request for which the TCP socket was created
 */
-	(void)client: (OFHTTPClient *)client
  didCreateTCPSocket: (OFTCPSocket *)TCPSocket
	     request: (OFHTTPRequest *)request;

/**
 * @brief A callback which is called when an OFHTTPClient creates a TLS stream.
 *
 * This can be used to tell the TLS stream about a client certificate it should
 * use before performing the TLS handshake.
 *
 * @param client The OFHTTPClient that created a TLS stream
 * @param TLSStream The TLS stream created by the OFHTTPClient
 * @param request The request for which the TLS stream was created
 */
-	(void)client: (OFHTTPClient *)client
  didCreateTLSStream: (OFTLSStream *)TLSStream
	     request: (OFHTTPRequest *)request;

/**
 * @brief A callback which is called when an OFHTTPClient wants to send the
 *	  body for a request.
 *
 * @param client The OFHTTPClient that wants to send the body
 * @param requestBody A stream into which the body of the request should be
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
@interface OFHTTPClient: OFObject
{
#ifdef OF_HTTPCLIENT_M
@public
#endif
	OFObject <OFHTTPClientDelegate> *_Nullable _delegate;
	bool _allowsInsecureRedirects, _inProgress;
	OFTCPSocket *_Nullable _socket;
	OFURL *_Nullable _lastURL;
	bool _lastWasHEAD;
	OFHTTPResponse *_Nullable _lastResponse;
}

/**
 * @brief The delegate of the HTTP request.







|







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@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.

Modified src/OFHTTPClient.m from [c27ed01d4c] to [74f8b0c30d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFKernelEventObserver.h"
#import "OFNumber.h"
#import "OFRunLoop.h"
#import "OFString.h"
#import "OFTCPSocket.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"
#import "OFWriteFailedException.h"

#import "socket_helpers.h"

#define REDIRECTS_DEFAULT 10

OF_DIRECT_MEMBERS
@interface OFHTTPClientRequestHandler: OFObject <OFTCPSocketDelegate>

{
@public
	OFHTTPClient *_client;
	OFHTTPRequest *_request;
	unsigned int _redirects;
	bool _firstLine;
	OFString *_version;







>

















<
|
<


|
>







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
#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"
#import "OFWriteFailedException.h"


static const unsigned int defaultRedirects = 10;


OF_DIRECT_MEMBERS
@interface OFHTTPClientRequestHandler: OFObject <OFTCPSocketDelegate,
    OFTLSStreamDelegate>
{
@public
	OFHTTPClient *_client;
	OFHTTPRequest *_request;
	unsigned int _redirects;
	bool _firstLine;
	OFString *_version;
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
- (void)closeAndReconnect;
@end

OF_DIRECT_MEMBERS
@interface OFHTTPClientRequestBodyStream: OFStream <OFReadyForWritingObserving>
{
	OFHTTPClientRequestHandler *_handler;
	OFTCPSocket *_socket;
	bool _chunked;
	unsigned long long _toWrite;
	bool _atEndOfStream;
}

- (instancetype)initWithHandler: (OFHTTPClientRequestHandler *)handler
			 socket: (OFTCPSocket *)sock;
@end

OF_DIRECT_MEMBERS
@interface OFHTTPClientResponse: OFHTTPResponse <OFReadyForReadingObserving>
{
	OFTCPSocket *_socket;
	bool _hasContentLength, _chunked, _keepAlive;
	bool _atEndOfStream, _setAtEndOfStream;
	long long _toRead;
}

@property (nonatomic, setter=of_setKeepAlive:) bool of_keepAlive;

- (instancetype)initWithSocket: (OFTCPSocket *)sock;
@end

OF_DIRECT_MEMBERS
@interface OFHTTPClientSyncPerformer: OFObject <OFHTTPClientDelegate>
{
	OFHTTPClient *_client;
	OFObject <OFHTTPClientDelegate> *_delegate;
	OFHTTPResponse *_response;
}

- (instancetype)initWithClient: (OFHTTPClient *)client;
- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
			 redirects: (unsigned int)redirects;
@end

static OFString *
constructRequestString(OFHTTPRequest *request)
{
	void *pool = objc_autoreleasePoolPush();
	of_http_request_method_t 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 %@", of_http_request_method_to_string(method), path];

	if (URL.query != nil) {
		[requestString appendString: @"?"];
		[requestString appendString: URL.URLEncodedQuery];
	}

	[requestString appendString: @" HTTP/"];







|






|





|







|



















|















|







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
- (void)closeAndReconnect;
@end

OF_DIRECT_MEMBERS
@interface OFHTTPClientRequestBodyStream: OFStream <OFReadyForWritingObserving>
{
	OFHTTPClientRequestHandler *_handler;
	OFStream *_stream;
	bool _chunked;
	unsigned long long _toWrite;
	bool _atEndOfStream;
}

- (instancetype)initWithHandler: (OFHTTPClientRequestHandler *)handler
			 stream: (OFStream *)stream;
@end

OF_DIRECT_MEMBERS
@interface OFHTTPClientResponse: OFHTTPResponse <OFReadyForReadingObserving>
{
	OFStream *_stream;
	bool _hasContentLength, _chunked, _keepAlive;
	bool _atEndOfStream, _setAtEndOfStream;
	long long _toRead;
}

@property (nonatomic, setter=of_setKeepAlive:) bool of_keepAlive;

- (instancetype)initWithStream: (OFStream *)stream;
@end

OF_DIRECT_MEMBERS
@interface OFHTTPClientSyncPerformer: OFObject <OFHTTPClientDelegate>
{
	OFHTTPClient *_client;
	OFObject <OFHTTPClientDelegate> *_delegate;
	OFHTTPResponse *_response;
}

- (instancetype)initWithClient: (OFHTTPClient *)client;
- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
			 redirects: (unsigned int)redirects;
@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/"];
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
	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;

		[authorizationData addItems: user.UTF8String
				      count: user.UTF8StringLength];
		[authorizationData addItem: ":"];
		[authorizationData addItems: password.UTF8String
				      count: password.UTF8StringLength];

		authorization = [OFString stringWithFormat:
		    @"Basic %@", authorizationData.stringByBase64Encoding];

		[headers setObject: authorization
			    forKey: @"Authorization"];
	}

	if ([headers objectForKey: @"User-Agent"] == nil)
		[headers setObject: @"Something using ObjFW "
				    @"<https://objfw.nil.im/>"
			    forKey: @"User-Agent"];

	if (request.protocolVersion.major == 1 &&
	    request.protocolVersion.minor == 0 &&
	    [headers objectForKey: @"Connection"] == nil)
		[headers setObject: @"keep-alive"
			    forKey: @"Connection"];

	hasContentLength = ([headers objectForKey: @"Content-Length"] != nil);
	chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	if ((hasContentLength || chunked) &&
	    [headers objectForKey: @"Content-Type"] == nil)







|
<

<
|
















|
<










|
<







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

		[authorizationData addItems: user.UTF8String
				      count: user.UTF8StringLength];
		[authorizationData addItem: ":"];
		[authorizationData addItems: password.UTF8String
				      count: password.UTF8StringLength];

		authorization = [OFString stringWithFormat:
		    @"Basic %@", authorizationData.stringByBase64Encoding];

		[headers setObject: authorization forKey: @"Authorization"];

	}

	if ([headers objectForKey: @"User-Agent"] == nil)
		[headers setObject: @"Something using ObjFW "
				    @"<https://objfw.nil.im/>"
			    forKey: @"User-Agent"];

	if (request.protocolVersion.major == 1 &&
	    request.protocolVersion.minor == 0 &&
	    [headers objectForKey: @"Connection"] == nil)
		[headers setObject: @"keep-alive" forKey: @"Connection"];


	hasContentLength = ([headers objectForKey: @"Content-Length"] != nil);
	chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	if ((hasContentLength || chunked) &&
	    [headers objectForKey: @"Content-Type"] == nil)
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
static OF_INLINE void
normalizeKey(char *str_)
{
	unsigned char *str = (unsigned char *)str_;
	bool firstLetter = true;

	while (*str != '\0') {
		if (!of_ascii_isalpha(*str)) {
			firstLetter = true;
			str++;
			continue;
		}

		*str = (firstLetter
		    ? of_ascii_toupper(*str)
		    : of_ascii_tolower(*str));

		firstLetter = false;
		str++;
	}
}

static bool
defaultShouldFollow(of_http_request_method_t method, short statusCode)
{
	bool follow;

	/*
	 * 301, 302 and 307 should only redirect with user confirmation if the
	 * request method is not GET or HEAD. Asking the delegate and getting
	 * true returned is considered user confirmation.
	 */
	if (method == OF_HTTP_REQUEST_METHOD_GET ||
	    method == OF_HTTP_REQUEST_METHOD_HEAD)
		follow = true;
	/* 303 should always be redirected and converted to a GET request. */
	else if (statusCode == 303)
		follow = true;
	else
		follow = false;








|






<
|







|








|
|







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
static OF_INLINE void
normalizeKey(char *str_)
{
	unsigned char *str = (unsigned char *)str_;
	bool firstLetter = true;

	while (*str != '\0') {
		if (!OFASCIIIsAlpha(*str)) {
			firstLetter = true;
			str++;
			continue;
		}

		*str = (firstLetter

		    ? OFASCIIToUpper(*str) : OFASCIIToLower(*str));

		firstLetter = false;
		str++;
	}
}

static bool
defaultShouldFollow(OFHTTPRequestMethod method, short statusCode)
{
	bool follow;

	/*
	 * 301, 302 and 307 should only redirect with user confirmation if the
	 * request method is not GET or HEAD. Asking the delegate and getting
	 * true returned is considered user confirmation.
	 */
	if (method == OFHTTPRequestMethodGet ||
	    method == OFHTTPRequestMethodHead)
		follow = true;
	/* 303 should always be redirected and converted to a GET request. */
	else if (statusCode == 303)
		follow = true;
	else
		follow = false;

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

	[_client->_delegate client: _client
		 didPerformRequest: _request
			  response: nil
			 exception: exception];
}

- (void)createResponseWithSocketOrThrow: (OFTCPSocket *)sock
{
	OFURL *URL = _request.URL;
	OFHTTPClientResponse *response;
	OFString *connectionHeader;
	bool keepAlive;
	OFString *location;
	id exception;

	response = [[[OFHTTPClientResponse alloc] initWithSocket: sock]
	    autorelease];
	response.protocolVersionString = _version;
	response.statusCode = _status;
	response.headers = _serverHeaders;

	connectionHeader = [_serverHeaders objectForKey: @"Connection"];
	if ([_version isEqual: @"1.1"]) {
		if (connectionHeader != nil)
			keepAlive = [connectionHeader isEqual: @"close"];
		else
			keepAlive = true;
	} else {
		if (connectionHeader != nil)
			keepAlive = ([connectionHeader caseInsensitiveCompare:
			    @"keep-alive"] == OF_ORDERED_SAME);
		else
			keepAlive = false;
	}

	if (keepAlive) {
		response.of_keepAlive = true;

		_client->_socket = [sock retain];
		_client->_lastURL = [URL copy];
		_client->_lastWasHEAD =
		    (_request.method == OF_HTTP_REQUEST_METHOD_HEAD);
		_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"] !=
		    OF_ORDERED_SAME &&
		    [newURLScheme caseInsensitiveCompare: @"https"] !=
		    OF_ORDERED_SAME)
			follow = false;

		if (!_client->_allowsInsecureRedirects &&
		    [URL.scheme caseInsensitiveCompare: @"https"] ==
		    OF_ORDERED_SAME &&
		    [newURLScheme caseInsensitiveCompare: @"http"] ==
		    OF_ORDERED_SAME)
			follow = false;

		if (follow && [_client->_delegate respondsToSelector: @selector(
		    client:shouldFollowRedirect:statusCode:request:response:)])
			follow = [_client->_delegate client: _client
				       shouldFollowRedirect: newURL
						 statusCode: _status







|








|














|







|


|















|

|




|

|







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

	[_client->_delegate client: _client
		 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]
	    autorelease];
	response.protocolVersionString = _version;
	response.statusCode = _status;
	response.headers = _serverHeaders;

	connectionHeader = [_serverHeaders objectForKey: @"Connection"];
	if ([_version isEqual: @"1.1"]) {
		if (connectionHeader != nil)
			keepAlive = [connectionHeader isEqual: @"close"];
		else
			keepAlive = true;
	} else {
		if (connectionHeader != nil)
			keepAlive = ([connectionHeader caseInsensitiveCompare:
			    @"keep-alive"] == OFOrderedSame);
		else
			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
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
				    (object = [objectEnumerator nextObject]) !=
				    nil)
					if ([key hasPrefix: @"Content-"] ||
					    [key hasPrefix: @"Transfer-"])
						[newHeaders
						    removeObjectForKey: key];

				newRequest.method = OF_HTTP_REQUEST_METHOD_GET;
			}

			newRequest.URL = newURL;
			newRequest.headers = newHeaders;

			_client->_inProgress = false;








|







396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
				    (object = [objectEnumerator nextObject]) !=
				    nil)
					if ([key hasPrefix: @"Content-"] ||
					    [key hasPrefix: @"Transfer-"])
						[newHeaders
						    removeObjectForKey: key];

				newRequest.method = OFHTTPRequestMethodGet;
			}

			newRequest.URL = newURL;
			newRequest.headers = newHeaders;

			_client->_inProgress = false;

435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
				 withObject: _client
				 withObject: _request
				 withObject: response
				 withObject: exception
				 afterDelay: 0];
}

- (void)createResponseWithSocket: (OFTCPSocket *)sock
{
	@try {
		[self createResponseWithSocketOrThrow: sock];
	} @catch (id e) {
		[self raiseException: e];
	}
}

- (bool)handleFirstLine: (OFString *)line
{







|


|







428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
				 withObject: _client
				 withObject: _request
				 withObject: response
				 withObject: exception
				 afterDelay: 0];
}

- (void)createResponseWithStream: (OFStream *)stream
{
	@try {
		[self createResponseWithStreamOrThrow: stream];
	} @catch (id e) {
		[self raiseException: e];
	}
}

- (bool)handleFirstLine: (OFString *)line
{
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
		return false;
	}

	if (![line hasPrefix: @"HTTP/"] || line.length < 9 ||
	    [line characterAtIndex: 8] != ' ')
		@throw [OFInvalidServerReplyException exception];

	_version = [[line substringWithRange: of_range(5, 3)] copy];
	if (![_version isEqual: @"1.0"] && ![_version isEqual: @"1.1"])
		@throw [OFUnsupportedVersionException
		    exceptionWithVersion: _version];

	status = [line substringWithRange: of_range(9, 3)].longLongValue;

	if (status < 0 || status > 599)
		@throw [OFInvalidServerReplyException exception];

	_status = (short)status;

	return true;
}

- (bool)handleServerHeader: (OFString *)line
		    socket: (OFTCPSocket *)sock
{
	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
				 didReceiveHeaders: _serverHeaders
					statusCode: _status
					   request: _request];

		sock.delegate = nil;

		[self performSelector: @selector(createResponseWithSocket:)
			   withObject: sock
			   afterDelay: 0];

		return false;
	}

	lineC = line.UTF8String;

	if ((tmp = strchr(lineC, ':')) == NULL)
		@throw [OFInvalidServerReplyException exception];

	keyC = of_malloc(1, tmp - lineC + 1);
	memcpy(keyC, lineC, tmp - lineC);
	keyC[tmp - lineC] = '\0';
	normalizeKey(keyC);

	@try {
		key = [OFString stringWithUTF8StringNoCopy: keyC
					      freeWhenDone: true];
	} @catch (id e) {
		of_free(keyC);
		@throw e;
	}

	do {
		tmp++;
	} while (*tmp == ' ');

	value = [OFString stringWithUTF8String: tmp];

	old = [_serverHeaders objectForKey: key];
	if (old != nil)
		value = [old stringByAppendingFormat: @",%@", value];

	[_serverHeaders setObject: value
			   forKey: key];

	return true;
}

- (bool)stream: (OFStream *)sock
   didReadLine: (OFString *)line
     exception: (id)exception
{
	bool ret;

	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFInvalidEncodingException class]])
			exception = [OFInvalidServerReplyException exception];

		[self raiseException: exception];
		return false;
	}

	@try {
		if (_firstLine) {
			_firstLine = false;
			ret = [self handleFirstLine: line];
		} else
			ret = [self handleServerHeader: line
						socket: (OFTCPSocket *)sock];
	} @catch (id e) {
		[self raiseException: e];
		ret = false;
	}

	return ret;
}

- (OFString *)stream: (OFStream *)stream
      didWriteString: (OFString *)string
	    encoding: (of_string_encoding_t)encoding
	bytesWritten: (size_t)bytesWritten
	   exception: (id)exception
{
	OFDictionary OF_GENERIC(OFString *, OFString *) *headers;
	bool chunked;

	if (exception != nil) {







|




|









|
<


















|

|
|










|








|













|
<




|



















|
<










|







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
		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
				 didReceiveHeaders: _serverHeaders
					statusCode: _status
					   request: _request];

		stream.delegate = nil;

		[self performSelector: @selector(createResponseWithStream:)
			   withObject: stream
			   afterDelay: 0];

		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 {
		key = [OFString stringWithUTF8StringNoCopy: keyC
					      freeWhenDone: true];
	} @catch (id e) {
		OFFreeMemory(keyC);
		@throw e;
	}

	do {
		tmp++;
	} while (*tmp == ' ');

	value = [OFString stringWithUTF8String: tmp];

	old = [_serverHeaders objectForKey: key];
	if (old != nil)
		value = [old stringByAppendingFormat: @",%@", value];

	[_serverHeaders setObject: value forKey: key];


	return true;
}

- (bool)stream: (OFStream *)stream
   didReadLine: (OFString *)line
     exception: (id)exception
{
	bool ret;

	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFInvalidEncodingException class]])
			exception = [OFInvalidServerReplyException exception];

		[self raiseException: exception];
		return false;
	}

	@try {
		if (_firstLine) {
			_firstLine = false;
			ret = [self handleFirstLine: line];
		} else
			ret = [self handleServerHeader: line stream: stream];

	} @catch (id e) {
		[self raiseException: e];
		ret = false;
	}

	return ret;
}

- (OFString *)stream: (OFStream *)stream
      didWriteString: (OFString *)string
	    encoding: (OFStringEncoding)encoding
	bytesWritten: (size_t)bytesWritten
	   exception: (id)exception
{
	OFDictionary OF_GENERIC(OFString *, OFString *) *headers;
	bool chunked;

	if (exception != nil) {
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
	chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	if (chunked || [headers objectForKey: @"Content-Length"] != nil) {
		stream.delegate = nil;

		OFStream *requestBody = [[[OFHTTPClientRequestBodyStream alloc]
		    initWithHandler: self
			     socket: (OFTCPSocket *)stream] autorelease];

		if ([_client->_delegate respondsToSelector:
		    @selector(client:wantsRequestBody:request:)])
			[_client->_delegate client: _client
				  wantsRequestBody: requestBody
					   request: _request];
	} else
		[stream asyncReadLine];

	return nil;
}

- (void)handleSocket: (OFTCPSocket *)sock
{
	/*
	 * As a work around for a bug with split packets in lighttpd when using
	 * HTTPS, we construct the complete request in a buffer string and then
	 * send it all at once.
	 *
	 * We do not use the socket's write buffer in case we need to resend
	 * the entire request (e.g. in case a keep-alive connection timed out).
	 */

	@try {
		[sock asyncWriteString: constructRequestString(_request)];
	} @catch (id e) {
		[self raiseException: e];
		return;
	}
}

-     (void)socket: (OFTCPSocket *)sock
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (id)exception
{




	sock.delegate = self;







	if (exception != nil) {






		[self raiseException: exception];


		return;
	}

	if ([_client->_delegate respondsToSelector:
	    @selector(client:didCreateSocket:request:)])
		[_client->_delegate client: _client
			   didCreateSocket: sock
				   request: _request];





	[self performSelector: @selector(handleSocket:)
		   withObject: sock















		   afterDelay: 0];
}

- (void)start
{
	OFURL *URL = _request.URL;
	OFTCPSocket *sock;

	/* Can we reuse the last socket? */
	if (_client->_socket != nil && !_client->_socket.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 _socket to nil, so that in case of an error it won't be
		 * reused. If everything is successful, we set _socket again
		 * at the end.
		 */
		sock = [_client->_socket autorelease];
		_client->_socket = nil;

		[_client->_lastURL release];
		_client->_lastURL = nil;

		[_client->_lastResponse release];
		_client->_lastResponse = nil;

		sock.delegate = self;

		[self performSelector: @selector(handleSocket:)
			   withObject: sock
			   afterDelay: 0];
	} else
		[self closeAndReconnect];
}

- (void)closeAndReconnect
{
	@try {
		OFURL *URL = _request.URL;
		OFTCPSocket *sock;
		uint16_t port;
		OFNumber *URLPort;

		[_client close];



		if ([URL.scheme caseInsensitiveCompare: @"https"] ==
		    OF_ORDERED_SAME) {
			if (of_tls_socket_class == Nil)
				@throw [OFUnsupportedProtocolException
				    exceptionWithURL: URL];

			sock = [[[of_tls_socket_class alloc] init] autorelease];
			port = 443;
		} else {
			sock = [OFTCPSocket socket];
			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
- (instancetype)initWithHandler: (OFHTTPClientRequestHandler *)handler
			 socket: (OFTCPSocket *)sock
{
	self = [super init];

	@try {
		OFDictionary OF_GENERIC(OFString *, OFString *) *headers;
		OFString *transferEncoding, *contentLengthString;

		_handler = [handler retain];
		_socket = [sock retain];

		headers = _handler->_request.headers;

		transferEncoding = [headers objectForKey: @"Transfer-Encoding"];
		_chunked = [transferEncoding isEqual: @"chunked"];

		contentLengthString = [headers objectForKey: @"Content-Length"];







|
<












|






|




|











>
>
>
>
|
>

>
>
>
>
>
|
>
>
>
>
>
>
|
>
>
|
|

|
|
|
|
|

>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






|


|






|
|


|
|







|

|
|















>
>

<
<
<
<
|
<

|
<

<






|
<








|








|







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
761
762
763
764
765
766
767
768
769
770
771
772
773
774
	chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	if (chunked || [headers objectForKey: @"Content-Length"] != nil) {
		stream.delegate = nil;

		OFStream *requestBody = [[[OFHTTPClientRequestBodyStream alloc]
		    initWithHandler: self stream: stream] autorelease];


		if ([_client->_delegate respondsToSelector:
		    @selector(client:wantsRequestBody:request:)])
			[_client->_delegate client: _client
				  wantsRequestBody: requestBody
					   request: _request];
	} else
		[stream asyncReadLine];

	return nil;
}

- (void)handleStream: (OFStream *)stream
{
	/*
	 * As a work around for a bug with split packets in lighttpd when using
	 * HTTPS, we construct the complete request in a buffer string and then
	 * send it all at once.
	 *
	 * We do not use the streams's write buffer in case we need to resend
	 * the entire request (e.g. in case a keep-alive connection timed out).
	 */

	@try {
		[stream asyncWriteString: constructRequestString(_request)];
	} @catch (id e) {
		[self raiseException: e];
		return;
	}
}

-     (void)socket: (OFTCPSocket *)sock
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (id)exception
{
	if (exception != nil) {
		[self raiseException: exception];
		return;
	}

	sock.canBlock = false;

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

-		       (void)stream: (OFTLSStream *)stream
  didPerformClientHandshakeWithHost: (OFString *)host
			  exception: (id)exception
{
	if (exception != nil) {
		[self raiseException: exception];
		return;
	}

	[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
- (instancetype)initWithHandler: (OFHTTPClientRequestHandler *)handler
			 stream: (OFStream *)stream
{
	self = [super init];

	@try {
		OFDictionary OF_GENERIC(OFString *, OFString *) *headers;
		OFString *transferEncoding, *contentLengthString;

		_handler = [handler retain];
		_stream = [stream retain];

		headers = _handler->_request.headers;

		transferEncoding = [headers objectForKey: @"Transfer-Encoding"];
		_chunked = [transferEncoding isEqual: @"chunked"];

		contentLengthString = [headers objectForKey: @"Content-Length"];
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
	}

	return self;
}

- (void)dealloc
{
	if (_socket != nil)
		[self close];

	[_handler release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	size_t requestedLength = length;
	size_t ret;


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

	/*
	 * We must not send a chunk of size 0, as that would end the body. We
	 * always ignore writing 0 bytes to still allow writing 0 bytes after
	 * the end of stream.
	 */
	if (length == 0)
		return 0;

	if (_atEndOfStream)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: requestedLength
			   bytesWritten: 0
				  errNo: 0];

	if (_chunked)
		[_socket writeFormat: @"%zX\r\n", length];
	else if (length > _toWrite)
		length = (size_t)_toWrite;


	ret = [_socket writeBuffer: buffer
			    length: length];
	if (_chunked)
		[_socket writeString: @"\r\n"];

	if (ret > length)
		@throw [OFOutOfRangeException exception];

	if (!_chunked) {
		_toWrite -= ret;

		if (_toWrite == 0)
			_atEndOfStream = true;
	}

	if (requestedLength > length)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: requestedLength
			   bytesWritten: ret
				  errNo: 0];

	return ret;
}

- (bool)lowlevelIsAtEndOfStream
{
	return _atEndOfStream;
}

- (void)close
{
	if (_socket == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_chunked)
		[_socket writeString: @"0\r\n\r\n"];
	else if (_toWrite > 0)
		@throw [OFTruncatedDataException exception];

	_socket.delegate = _handler;
	[_socket asyncReadLine];

	[_socket release];
	_socket = nil;

	[super close];
}

- (int)fileDescriptorForWriting
{

	return _socket.fileDescriptorForWriting;
}
@end

@implementation OFHTTPClientResponse
@synthesize of_keepAlive = _keepAlive;

- (instancetype)initWithSocket: (OFTCPSocket *)sock
{
	self = [super init];

	_socket = [sock retain];

	return self;
}

- (void)dealloc
{
	if (_socket != nil)
		[self close];

	[super dealloc];
}

- (void)setHeaders: (OFDictionary *)headers
{







|







|
<

<
<
>

|











|
<
|
|
|


|

<
>

<
|

|

<
<
<

|





<
<
<
<
<
<
<
|









|



|



|
|

|
|






>
|






|



|






|







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
	}

	return self;
}

- (void)dealloc
{
	if (_stream != nil)
		[self close];

	[_handler release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{


	/* TODO: Use non-blocking writes */

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	/*
	 * We must not send a chunk of size 0, as that would end the body. We
	 * always ignore writing 0 bytes to still allow writing 0 bytes after
	 * the end of stream.
	 */
	if (length == 0)
		return 0;

	if (_atEndOfStream)
		@throw [OFWriteFailedException exceptionWithObject: self

						   requestedLength: length
						      bytesWritten: 0
							     errNo: ENOTCONN];

	if (_chunked)
		[_stream writeFormat: @"%zX\r\n", length];
	else if (length > _toWrite)

		@throw [OFOutOfRangeException exception];


	[_stream writeBuffer: buffer length: length];
	if (_chunked)
		[_stream writeString: @"\r\n"];




	if (!_chunked) {
		_toWrite -= length;

		if (_toWrite == 0)
			_atEndOfStream = true;
	}








	return length;
}

- (bool)lowlevelIsAtEndOfStream
{
	return _atEndOfStream;
}

- (void)close
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_chunked)
		[_stream writeString: @"0\r\n\r\n"];
	else if (_toWrite > 0)
		@throw [OFTruncatedDataException exception];

	_stream.delegate = _handler;
	[_stream asyncReadLine];

	[_stream release];
	_stream = nil;

	[super close];
}

- (int)fileDescriptorForWriting
{
	return ((OFStream <OFReadyForWritingObserving> *)_stream)
	    .fileDescriptorForWriting;
}
@end

@implementation OFHTTPClientResponse
@synthesize of_keepAlive = _keepAlive;

- (instancetype)initWithStream: (OFStream *)stream
{
	self = [super init];

	_stream = [stream retain];

	return self;
}

- (void)dealloc
{
	if (_stream != nil)
		[self close];

	[super dealloc];
}

- (void)setHeaders: (OFDictionary *)headers
{
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
			_toRead = (long long)toRead;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidServerReplyException exception];
		}
	}
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	if (_socket == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_atEndOfStream)
		return 0;

	if (!_hasContentLength && !_chunked)
		return [_socket readIntoBuffer: buffer
					length: length];

	if (_socket.atEndOfStream)
		@throw [OFTruncatedDataException exception];

	/* Content-Length */
	if (!_chunked) {
		size_t ret;

		if (length > (unsigned long long)_toRead)
			length = (size_t)_toRead;

		ret = [_socket readIntoBuffer: buffer
				       length: length];

		if (ret > length)
			@throw [OFOutOfRangeException exception];

		_toRead -= ret;

		if (_toRead == 0)
			_atEndOfStream = true;

		return ret;
	}

	/* Chunked */
	if (_toRead == -2) {
		char tmp[2];

		switch ([_socket 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 ([_socket readIntoBuffer: &tmp
				     length: 1] == 1) {
			_toRead++;
			if (tmp != '\n')
				@throw [OFInvalidServerReplyException
				    exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;
	} else if (_toRead > 0) {
		if (length > (unsigned long long)_toRead)
			length = (size_t)_toRead;

		length = [_socket readIntoBuffer: buffer
					  length: length];

		_toRead -= length;

		if (_toRead == 0)
			_toRead = -2;

		return length;
	} else {
		void *pool = objc_autoreleasePoolPush();
		OFString *line;
		size_t pos;

		@try {
			line = [_socket tryReadLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidServerReplyException exception];
		}

		if (line == nil)
			return 0;

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

		if (line.length < 1) {
			/*
			 * We have read the empty string because the socket is
			 * at end of stream.
			 */
			if (_socket.atEndOfStream && pos == OF_NOT_FOUND)
				@throw [OFTruncatedDataException exception];
			else
				@throw [OFInvalidServerReplyException
				    exception];
		}

		@try {







|
<

|






<
|

|









<
|
<















<
|



















<
|














<
|


<










|








|




|


|







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
			_toRead = (long long)toRead;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidServerReplyException exception];
		}
	}
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_atEndOfStream)
		return 0;

	if (!_hasContentLength && !_chunked)

		return [_stream readIntoBuffer: buffer length: length];

	if (_stream.atEndOfStream)
		@throw [OFTruncatedDataException exception];

	/* Content-Length */
	if (!_chunked) {
		size_t ret;

		if (length > (unsigned long long)_toRead)
			length = (size_t)_toRead;


		ret = [_stream readIntoBuffer: buffer length: length];

		if (ret > length)
			@throw [OFOutOfRangeException exception];

		_toRead -= ret;

		if (_toRead == 0)
			_atEndOfStream = true;

		return ret;
	}

	/* Chunked */
	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;
	} else if (_toRead > 0) {
		if (length > (unsigned long long)_toRead)
			length = (size_t)_toRead;


		length = [_stream readIntoBuffer: buffer length: length];

		_toRead -= length;

		if (_toRead == 0)
			_toRead = -2;

		return length;
	} else {
		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 {
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
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_atEndOfStream)
		return true;

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

	if (!_hasContentLength && !_chunked)
		return _socket.atEndOfStream;

	return _atEndOfStream;
}

- (int)fileDescriptorForReading
{
	if (_socket == nil)
		return -1;


	return _socket.fileDescriptorForReading;
}

- (bool)hasDataInReadBuffer
{
	return (super.hasDataInReadBuffer || _socket.hasDataInReadBuffer);
}

- (void)close
{
	if (_socket == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	_atEndOfStream = false;

	[_socket release];
	_socket = nil;

	[super close];
}
@end

@implementation OFHTTPClientSyncPerformer
- (instancetype)initWithClient: (OFHTTPClient *)client







|



|






|


>
|




|




|




|
|







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
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_atEndOfStream)
		return true;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (!_hasContentLength && !_chunked)
		return _stream.atEndOfStream;

	return _atEndOfStream;
}

- (int)fileDescriptorForReading
{
	if (_stream == nil)
		return -1;

	return ((OFStream <OFReadyForReadingObserving> *)_stream)
	    .fileDescriptorForReading;
}

- (bool)hasDataInReadBuffer
{
	return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer);
}

- (void)close
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	_atEndOfStream = false;

	[_stream release];
	_stream = nil;

	[super close];
}
@end

@implementation OFHTTPClientSyncPerformer
- (instancetype)initWithClient: (OFHTTPClient *)client
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136

	[super dealloc];
}

- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
			 redirects: (unsigned int)redirects
{
	[_client asyncPerformRequest: request
			   redirects: redirects];

	[[OFRunLoop currentRunLoop] run];

	return _response;
}

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







|
<
<

<







1118
1119
1120
1121
1122
1123
1124
1125


1126

1127
1128
1129
1130
1131
1132
1133

	[super dealloc];
}

- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
			 redirects: (unsigned int)redirects
{
	[_client asyncPerformRequest: request redirects: redirects];


	[[OFRunLoop currentRunLoop] run];

	return _response;
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response
	  exception: (id)exception
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168











1169
1170
1171
1172
1173
1174
1175

	[_delegate     client: client
	    didPerformRequest: request
		     response: response
		    exception: nil];
}

-    (void)client: (OFHTTPClient *)client
  didCreateSocket: (OFTCPSocket *)sock
	  request: (OFHTTPRequest *)request
{
	if ([_delegate respondsToSelector:
	    @selector(client:didCreateSocket:request:)])
		[_delegate   client: client
		    didCreateSocket: sock
			    request: request];











}

-     (void)client: (OFHTTPClient *)client
  wantsRequestBody: (OFStream *)body
	   request: (OFHTTPRequest *)request
{
	if ([_delegate respondsToSelector:







|
|
|


|
|
|
|
>
>
>
>
>
>
>
>
>
>
>







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
1182
1183

	[_delegate     client: client
	    didPerformRequest: request
		     response: response
		    exception: nil];
}

-	(void)client: (OFHTTPClient *)client
  didCreateTCPSocket: (OFTCPSocket *)TCPSocket
	     request: (OFHTTPRequest *)request
{
	if ([_delegate respondsToSelector:
	    @selector(client:didCreateTCPSocket:request:)])
		[_delegate	client: client
		    didCreateTCPSocket: TCPSocket
			       request: request];
}

-	(void)client: (OFHTTPClient *)client
  didCreateTLSStream: (OFTLSStream *)TLSStream
	     request: (OFHTTPRequest *)request
{
	if ([_delegate respondsToSelector:
	    @selector(client:didCreateTLSStream:request:)])
		[_delegate	client: client
		    didCreateTLSStream: TLSStream
			       request: request];
}

-     (void)client: (OFHTTPClient *)client
  wantsRequestBody: (OFStream *)body
	   request: (OFHTTPRequest *)request
{
	if ([_delegate respondsToSelector:
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
	[self close];

	[super dealloc];
}

- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
{
	return [self performRequest: request
			  redirects: REDIRECTS_DEFAULT];
}

- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
			 redirects: (unsigned int)redirects
{
	void *pool = objc_autoreleasePoolPush();
	OFHTTPClientSyncPerformer *syncPerformer =







|
<







1232
1233
1234
1235
1236
1237
1238
1239

1240
1241
1242
1243
1244
1245
1246
	[self close];

	[super dealloc];
}

- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
{
	return [self performRequest: request redirects: defaultRedirects];

}

- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
			 redirects: (unsigned int)redirects
{
	void *pool = objc_autoreleasePoolPush();
	OFHTTPClientSyncPerformer *syncPerformer =
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
	objc_autoreleasePoolPop(pool);

	return [response autorelease];
}

- (void)asyncPerformRequest: (OFHTTPRequest *)request
{
	[self asyncPerformRequest: request
			redirects: REDIRECTS_DEFAULT];
}

- (void)asyncPerformRequest: (OFHTTPRequest *)request
		  redirects: (unsigned int)redirects
{
	void *pool = objc_autoreleasePoolPush();
	OFURL *URL = request.URL;
	OFString *scheme = URL.scheme;

	if ([scheme caseInsensitiveCompare: @"http"] != OF_ORDERED_SAME &&
	    [scheme caseInsensitiveCompare: @"https"] != OF_ORDERED_SAME)
		@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
{
	[_socket release];
	_socket = nil;

	[_lastURL release];
	_lastURL = nil;

	[_lastResponse release];
	_lastResponse = nil;
}
@end







|
<









|
|


















|
|








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
	objc_autoreleasePoolPop(pool);

	return [response autorelease];
}

- (void)asyncPerformRequest: (OFHTTPRequest *)request
{
	[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

Modified src/OFHTTPCookie.h from [49a9c4f266] to [c915720e1d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFHTTPCookie.m from [a6b5d4bf43] to [30659cca3c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
    (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 of_unichar_t *characters = string.characters;
	size_t length = string.length, last = 0;
	enum {
		STATE_PRE_NAME,
		STATE_NAME,
		STATE_EXPECT_VALUE,
		STATE_VALUE,
		STATE_QUOTED_VALUE,
		STATE_POST_QUOTED_VALUE,
		STATE_PRE_ATTR_NAME,
		STATE_ATTR_NAME,
		STATE_ATTR_VALUE
	} state = STATE_PRE_NAME;
	OFString *name = nil, *value = nil;

	for (size_t i = 0; i < length; i++) {
		switch (state) {
		case STATE_PRE_NAME:
			if (characters[i] != ' ') {
				state = STATE_NAME;
				last = i;
				i--;
			}
			break;
		case STATE_NAME:
			if (characters[i] == '=') {
				name = [string substringWithRange:
				    of_range(last, i - last)];
				state = STATE_EXPECT_VALUE;
			}
			break;
		case STATE_EXPECT_VALUE:
			if (characters[i] == '"') {
				state = STATE_QUOTED_VALUE;
				last = i + 1;
			} else {
				state = STATE_VALUE;
				last = i;
			}

			i--;
			break;
		case STATE_VALUE:
			if (characters[i] == ';' || characters[i] == ',') {
				value = [string substringWithRange:
				    of_range(last, i - last)];

				[ret addObject:
				    [OFHTTPCookie cookieWithName: name
							   value: value
							  domain: domain]];

				state = (characters[i] == ';'
				    ? STATE_PRE_ATTR_NAME : STATE_PRE_NAME);
			}
			break;
		case STATE_QUOTED_VALUE:
			if (characters[i] == '"') {
				value = [string substringWithRange:
				    of_range(last, i - last)];
				[ret addObject:
				    [OFHTTPCookie cookieWithName: name
							   value: value
							  domain: domain]];

				state = STATE_POST_QUOTED_VALUE;
			}
			break;
		case STATE_POST_QUOTED_VALUE:
			if (characters[i] == ';')
				state = STATE_PRE_ATTR_NAME;
			else if (characters[i] == ',')
				state = STATE_PRE_NAME;
			else
				@throw [OFInvalidFormatException exception];

			break;
		case STATE_PRE_ATTR_NAME:
			if (characters[i] != ' ') {
				state = STATE_ATTR_NAME;
				last = i;
				i--;
			}
			break;
		case STATE_ATTR_NAME:
			if (characters[i] == '=') {
				name = [string substringWithRange:
				    of_range(last, i - last)];

				state = STATE_ATTR_VALUE;
				last = i + 1;
			} else if (characters[i] == ';' ||
			    characters[i] == ',') {
				name = [string substringWithRange:
				    of_range(last, i - last)];

				handleAttribute(ret.lastObject, name, nil);

				state = (characters[i] == ';'
				    ? STATE_PRE_ATTR_NAME : STATE_PRE_NAME);
			}

			break;
		case STATE_ATTR_VALUE:
			if (characters[i] == ';' || characters[i] == ',') {
				value = [string substringWithRange:
				    of_range(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.
				 */
				if (characters[i] == ',' &&
				    [name caseInsensitiveCompare: @"expires"] ==
				    OF_ORDERED_SAME && value.length == 3 &&
				    ([value isEqual: @"Mon"] ||
				    [value isEqual: @"Tue"] ||
				    [value isEqual: @"Wed"] ||
				    [value isEqual: @"Thu"] ||
				    [value isEqual: @"Fri"] ||
				    [value isEqual: @"Sat"] ||
				    [value isEqual: @"Sun"]))
					break;

				handleAttribute(ret.lastObject, name, value);

				state = (characters[i] == ';'
				    ? STATE_PRE_ATTR_NAME : STATE_PRE_NAME);
			}
			break;
		}
	}

	switch (state) {
	case STATE_PRE_NAME:
	case STATE_POST_QUOTED_VALUE:
	case STATE_PRE_ATTR_NAME:
		break;
	case STATE_NAME:
	case STATE_QUOTED_VALUE:
		@throw [OFInvalidFormatException exception];
		break;
	case STATE_VALUE:
		value = [string substringWithRange:
		    of_range(last, length - last)];
		[ret addObject: [OFHTTPCookie cookieWithName: name
						       value: value
						      domain: domain]];
		break;
	/* We end up here if the cookie is just foo= */
	case STATE_EXPECT_VALUE:
		[ret addObject: [OFHTTPCookie cookieWithName: name
						       value: @""
						      domain: domain]];
		break;
	case STATE_ATTR_NAME:
		if (last != length) {
			name = [string substringWithRange:
			    of_range(last, length - last)];

			handleAttribute(ret.lastObject, name, nil);
		}
		break;
	case STATE_ATTR_VALUE:
		value = [string substringWithRange:
		    of_range(last, length - last)];

		handleAttribute(ret.lastObject, name, value);

		break;
	}

	objc_autoreleasePoolPop(pool);







|


|
|
|
|
|
|
|
|
|
|




|

|




|


|
|


|

|


|





|


|







|


|


|





|


|

|

|




|

|




|


|

|




|




|



|


|










|












|






|
|
|

|
|


|

|





|




|


|




|

|







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
    (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,
		stateQuotedValue,
		statePostQuotedValue,
		statePreAttrName,
		stateAttrName,
		stateAttrValue
	} state = statePreName;
	OFString *name = nil, *value = nil;

	for (size_t i = 0; i < length; i++) {
		switch (state) {
		case statePreName:
			if (characters[i] != ' ') {
				state = stateName;
				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;
			}
			break;
		case statePostQuotedValue:
			if (characters[i] == ';')
				state = statePreAttrName;
			else if (characters[i] == ',')
				state = statePreName;
			else
				@throw [OFInvalidFormatException exception];

			break;
		case statePreAttrName:
			if (characters[i] != ' ') {
				state = stateAttrName;
				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.
				 */
				if (characters[i] == ',' &&
				    [name caseInsensitiveCompare: @"expires"] ==
				    OFOrderedSame && value.length == 3 &&
				    ([value isEqual: @"Mon"] ||
				    [value isEqual: @"Tue"] ||
				    [value isEqual: @"Wed"] ||
				    [value isEqual: @"Thu"] ||
				    [value isEqual: @"Fri"] ||
				    [value isEqual: @"Sat"] ||
				    [value isEqual: @"Sun"]))
					break;

				handleAttribute(ret.lastObject, name, value);

				state = (characters[i] == ';'
				    ? statePreAttrName : statePreName);
			}
			break;
		}
	}

	switch (state) {
	case statePreName:
	case statePostQuotedValue:
	case statePreAttrName:
		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);
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);
	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD_HASH(hash, _value.hash);
	OF_HASH_ADD_HASH(hash, _domain.hash);
	OF_HASH_ADD_HASH(hash, _path.hash);
	OF_HASH_ADD_HASH(hash, _expires.hash);
	OF_HASH_ADD(hash, _secure);
	OF_HASH_ADD(hash, _HTTPOnly);
	OF_HASH_ADD_HASH(hash, _extensions.hash);
	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
	OFHTTPCookie *copy = [[OFHTTPCookie alloc] initWithName: _name







|

|
|
|
|
|
|
|
|
|
|







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
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	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
{
	OFHTTPCookie *copy = [[OFHTTPCookie alloc] initWithName: _name

Modified src/OFHTTPCookieManager.h from [bd806f13f2] to [776c67cc36].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 *
 * @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!
 *







|
<







50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
 *
 * @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!
 *

Modified src/OFHTTPCookieManager.m from [f3b5caf32a] to [ab6e7fb16a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
}

- (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"] != OF_ORDERED_SAME) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	cookieDomain = cookie.domain.lowercaseString;
	cookie.domain = cookieDomain;








|
<









|







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
}

- (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;

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
	}

	i = 0;
	for (OFHTTPCookie *iter in _cookies) {
		if ([iter.name isEqual: cookie.name] &&
		    [iter.domain isEqual: cookie.domain] &&
		    [iter.path isEqual: cookie.path]) {
			[_cookies replaceObjectAtIndex: i
					    withObject: cookie];

			objc_autoreleasePoolPop(pool);
			return;
		}

		i++;
	}

	[_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"] != OF_ORDERED_SAME)
			continue;

		pool = objc_autoreleasePoolPush();

		cookieDomain = cookie.domain.lowercaseString;
		URLHost = URL.host.lowercaseString;
		if ([cookieDomain hasPrefix: @"."]) {







|
<
<
















|
<

















|







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
	}

	i = 0;
	for (OFHTTPCookie *iter in _cookies) {
		if ([iter.name isEqual: cookie.name] &&
		    [iter.domain isEqual: cookie.domain] &&
		    [iter.path isEqual: cookie.path]) {
			[_cookies replaceObjectAtIndex: i withObject: cookie];


			objc_autoreleasePoolPop(pool);
			return;
		}

		i++;
	}

	[_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: @"."]) {

Modified src/OFHTTPRequest.h from [8374acae4d] to [cc3f0d316f].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

@class OFURL;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFData;
@class OFString;

/** @file */

/**
 * @brief The type of an HTTP request.
 */
typedef enum {
	/** OPTIONS */
	OF_HTTP_REQUEST_METHOD_OPTIONS,
	/** GET */
	OF_HTTP_REQUEST_METHOD_GET,
	/** HEAD */
	OF_HTTP_REQUEST_METHOD_HEAD,
	/** POST */
	OF_HTTP_REQUEST_METHOD_POST,
	/** PUT */
	OF_HTTP_REQUEST_METHOD_PUT,
	/** DELETE */
	OF_HTTP_REQUEST_METHOD_DELETE,
	/** TRACE */
	OF_HTTP_REQUEST_METHOD_TRACE,
	/** CONNECT */
	OF_HTTP_REQUEST_METHOD_CONNECT
} of_http_request_method_t;

/**
 * @struct of_http_request_protocol_version_t \
 *	   OFHTTPRequest.h ObjFW/OFHTTPRequest.h
 *
 * @brief The HTTP version of the HTTP request.
 */
struct OF_BOXABLE of_http_request_protocol_version_t {
	/** The major of the HTTP version */
	unsigned char major;
	/** The minor of the HTTP version */
	unsigned char minor;
};
typedef struct of_http_request_protocol_version_t
    of_http_request_protocol_version_t;

/**
 * @class OFHTTPRequest OFHTTPRequest.h ObjFW/OFHTTPRequest.h
 *
 * @brief A class for storing HTTP requests.
 */
@interface OFHTTPRequest: OFObject <OFCopying>
{
	OFURL *_URL;
	of_http_request_method_t _method;
	of_http_request_protocol_version_t _protocolVersion;
	OFDictionary OF_GENERIC(OFString *, OFString *) *_Nullable _headers;
	of_socket_address_t _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) of_http_request_protocol_version_t 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) of_http_request_method_t method;

/**
 * @brief The headers for the HTTP request.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic)
    OFDictionary OF_GENERIC(OFString *, OFString *) *headers;

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

/**
 * @brief Creates a new OFHTTPRequest.
 *
 * @return A new, autoreleased OFHTTPRequest
 */
+ (instancetype)request;

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


@end

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Returns a C string describing the specified request method.
 *
 * @param method The request method which should be described as a C string
 * @return A C string describing the specified request method
 */
extern const char *_Nullable of_http_request_method_to_string(
    of_http_request_method_t 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 of_http_request_method_t of_http_request_method_from_string(
    OFString *string);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

<
<
|














|
<
|















|

|

|

|

|

|

|

|
|


<
|



|




<
<
|









|
|

|












|









|












|
<
<
<
<
<
<
<
<
















>
>











|
|







<
|





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


 * 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 "OFSocket.h"

#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

@class OFURL;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFData;
@class OFString;

/** @file */

/**
 * @brief The type of an HTTP request.
 */
typedef enum {
	/** OPTIONS */
	OFHTTPRequestMethodOptions,
	/** GET */
	OFHTTPRequestMethodGet,
	/** HEAD */
	OFHTTPRequestMethodHead,
	/** POST */
	OFHTTPRequestMethodPost,
	/** PUT */
	OFHTTPRequestMethodPut,
	/** DELETE */
	OFHTTPRequestMethodDelete,
	/** TRACE */
	OFHTTPRequestMethodTrace,
	/** CONNECT */
	OFHTTPRequestMethodConnect
} OFHTTPRequestMethod;

/**

 * @struct OFHTTPRequestProtocolVersion OFHTTPRequest.h ObjFW/OFHTTPRequest.h
 *
 * @brief The HTTP version of the HTTP request.
 */
typedef struct OF_BOXABLE {
	/** The major of the HTTP version */
	unsigned char major;
	/** The minor of the HTTP version */
	unsigned char minor;


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

/**
 * @brief The headers for the HTTP request.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic)
    OFDictionary OF_GENERIC(OFString *, OFString *) *headers;

/**
 * @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
/**
 * @brief Returns a C string describing the specified request method.
 *
 * @param method The request method which should be described as a C string
 * @return A C string describing the specified request method
 */
extern const char *_Nullable OFHTTPRequestMethodName(
    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

Modified src/OFHTTPRequest.m from [517654630c] to [723c374a89].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
#import "OFUnsupportedVersionException.h"

const char *
of_http_request_method_to_string(of_http_request_method_t method)
{
	switch (method) {
	case OF_HTTP_REQUEST_METHOD_OPTIONS:
		return "OPTIONS";
	case OF_HTTP_REQUEST_METHOD_GET:
		return "GET";
	case OF_HTTP_REQUEST_METHOD_HEAD:
		return "HEAD";
	case OF_HTTP_REQUEST_METHOD_POST:
		return "POST";
	case OF_HTTP_REQUEST_METHOD_PUT:
		return "PUT";
	case OF_HTTP_REQUEST_METHOD_DELETE:
		return "DELETE";
	case OF_HTTP_REQUEST_METHOD_TRACE:
		return "TRACE";
	case OF_HTTP_REQUEST_METHOD_CONNECT:
		return "CONNECT";
	}

	return NULL;
}

of_http_request_method_t
of_http_request_method_from_string(OFString *string)
{
	if ([string isEqual: @"OPTIONS"])
		return OF_HTTP_REQUEST_METHOD_OPTIONS;
	if ([string isEqual: @"GET"])
		return OF_HTTP_REQUEST_METHOD_GET;
	if ([string isEqual: @"HEAD"])
		return OF_HTTP_REQUEST_METHOD_HEAD;
	if ([string isEqual: @"POST"])
		return OF_HTTP_REQUEST_METHOD_POST;
	if ([string isEqual: @"PUT"])
		return OF_HTTP_REQUEST_METHOD_PUT;
	if ([string isEqual: @"DELETE"])
		return OF_HTTP_REQUEST_METHOD_DELETE;
	if ([string isEqual: @"TRACE"])
		return OF_HTTP_REQUEST_METHOD_TRACE;
	if ([string isEqual: @"CONNECT"])
		return OF_HTTP_REQUEST_METHOD_CONNECT;

	@throw [OFInvalidArgumentException exception];
}

@implementation OFHTTPRequest
@synthesize URL = _URL, method = _method, headers = _headers;

+ (instancetype)request
{
	return [[[self alloc] init] autorelease];
}

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

- (instancetype)init
{
	self = [super init];



	_method = OF_HTTP_REQUEST_METHOD_GET;
	_protocolVersion.major = 1;
	_protocolVersion.minor = 1;

	return self;
}

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

	@try {
		_URL = [URL copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_URL release];
	[_headers release];

	[super dealloc];
}

- (void)setRemoteAddress: (const of_socket_address_t *)remoteAddress
{
	_hasRemoteAddress = (remoteAddress != NULL);

	if (_hasRemoteAddress)
		_remoteAddress = *remoteAddress;
}

- (const of_socket_address_t *)remoteAddress
{
	if (_hasRemoteAddress)
		return &_remoteAddress;

	return NULL;
}

- (id)copy
{
	OFHTTPRequest *copy = [[OFHTTPRequest alloc] init];

	@try {
		copy->_method = _method;
		copy->_protocolVersion = _protocolVersion;
		copy.URL = _URL;
		copy.headers = _headers;
		copy.remoteAddress = self.remoteAddress;
	} @catch (id e) {
		[copy release];
		@throw e;
	}








|


|

|

|

|

|

|

|

|






|
|


|

|

|

|

|

|

|

|







<
<
<
<
<





|



>
>
|
|
|
<
<
<
<
<
<
<
<
<
<







>
>
>
>
>









|







|









|




<







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

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
#import "OFUnsupportedVersionException.h"

const char *
OFHTTPRequestMethodName(OFHTTPRequestMethod method)
{
	switch (method) {
	case OFHTTPRequestMethodOptions:
		return "OPTIONS";
	case OFHTTPRequestMethodGet:
		return "GET";
	case OFHTTPRequestMethodHead:
		return "HEAD";
	case OFHTTPRequestMethodPost:
		return "POST";
	case OFHTTPRequestMethodPut:
		return "PUT";
	case OFHTTPRequestMethodDelete:
		return "DELETE";
	case OFHTTPRequestMethodTrace:
		return "TRACE";
	case OFHTTPRequestMethodConnect:
		return "CONNECT";
	}

	return NULL;
}

OFHTTPRequestMethod
OFHTTPRequestMethodParseName(OFString *string)
{
	if ([string isEqual: @"OPTIONS"])
		return OFHTTPRequestMethodOptions;
	if ([string isEqual: @"GET"])
		return OFHTTPRequestMethodGet;
	if ([string isEqual: @"HEAD"])
		return OFHTTPRequestMethodHead;
	if ([string isEqual: @"POST"])
		return OFHTTPRequestMethodPost;
	if ([string isEqual: @"PUT"])
		return OFHTTPRequestMethodPut;
	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
{
	_hasRemoteAddress = (remoteAddress != NULL);

	if (_hasRemoteAddress)
		_remoteAddress = *remoteAddress;
}

- (const OFSocketAddress *)remoteAddress
{
	if (_hasRemoteAddress)
		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) {
		[copy release];
		@throw e;
	}

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
	    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 &&
	    !of_socket_address_equal(request.remoteAddress, self.remoteAddress))
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD(hash, _method);
	OF_HASH_ADD(hash, _protocolVersion.major);
	OF_HASH_ADD(hash, _protocolVersion.minor);
	OF_HASH_ADD_HASH(hash, _URL.hash);
	OF_HASH_ADD_HASH(hash, _headers.hash);
	if (_hasRemoteAddress)
		OF_HASH_ADD_HASH(hash, of_socket_address_hash(&_remoteAddress));

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (void)setProtocolVersion: (of_http_request_protocol_version_t)protocolVersion
{
	if (protocolVersion.major != 1 || protocolVersion.minor > 1)
		@throw [OFUnsupportedVersionException exceptionWithVersion:
		    [OFString stringWithFormat: @"%hhu.%hhu",
						protocolVersion.major,
						protocolVersion.minor]];

	_protocolVersion = protocolVersion;
}

- (of_http_request_protocol_version_t)protocolVersion
{
	return _protocolVersion;
}

- (void)setProtocolVersionString: (OFString *)string
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *components = [string componentsSeparatedByString: @"."];
	unsigned long long major, minor;
	of_http_request_protocol_version_t protocolVersion;

	if (components.count != 2)
		@throw [OFInvalidFormatException exception];

	major = [components.firstObject unsignedLongLongValue];
	minor = [components.lastObject unsignedLongLongValue];








|







|

|

|
|
|
|
|

|

|




|










|









|







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

- (void)setProtocolVersion: (OFHTTPRequestProtocolVersion)protocolVersion
{
	if (protocolVersion.major != 1 || protocolVersion.minor > 1)
		@throw [OFUnsupportedVersionException exceptionWithVersion:
		    [OFString stringWithFormat: @"%hhu.%hhu",
						protocolVersion.major,
						protocolVersion.minor]];

	_protocolVersion = protocolVersion;
}

- (OFHTTPRequestProtocolVersion)protocolVersion
{
	return _protocolVersion;
}

- (void)setProtocolVersionString: (OFString *)string
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *components = [string componentsSeparatedByString: @"."];
	unsigned long long major, minor;
	OFHTTPRequestProtocolVersion protocolVersion;

	if (components.count != 2)
		@throw [OFInvalidFormatException exception];

	major = [components.firstObject unsignedLongLongValue];
	minor = [components.lastObject unsignedLongLongValue];

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
					   _protocolVersion.major,
					   _protocolVersion.minor];
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	const char *method = of_http_request_method_to_string(_method);
	OFString *indentedHeaders, *remoteAddress, *ret;

	indentedHeaders = [_headers.description
	    stringByReplacingOccurrencesOfString: @"\n"
				      withString: @"\n\t"];

	if (_hasRemoteAddress)
		remoteAddress =
		    of_socket_address_ip_string(&_remoteAddress, NULL);
	else
		remoteAddress = nil;

	ret = [[OFString alloc] initWithFormat:
	    @"<%@:\n\tURL = %@\n"
	    @"\tMethod = %s\n"
	    @"\tHeaders = %@\n"







|







|
<







239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
					   _protocolVersion.major,
					   _protocolVersion.minor];
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	const char *method = OFHTTPRequestMethodName(_method);
	OFString *indentedHeaders, *remoteAddress, *ret;

	indentedHeaders = [_headers.description
	    stringByReplacingOccurrencesOfString: @"\n"
				      withString: @"\n\t"];

	if (_hasRemoteAddress)
		remoteAddress = OFSocketAddressString(&_remoteAddress);

	else
		remoteAddress = nil;

	ret = [[OFString alloc] initWithFormat:
	    @"<%@:\n\tURL = %@\n"
	    @"\tMethod = %s\n"
	    @"\tHeaders = %@\n"

Modified src/OFHTTPResponse.h from [91600eee28] to [b3d8087036].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFArray OF_GENERIC(ObjectType);

/**
 * @class OFHTTPResponse OFHTTPResponse.h ObjFW/OFHTTPResponse.h
 *
 * @brief A class for representing an HTTP request reply as a stream.
 */
@interface OFHTTPResponse: OFStream
{
	of_http_request_protocol_version_t _protocolVersion;
	short _statusCode;
	OFDictionary OF_GENERIC(OFString *, OFString *) *_headers;
	OF_RESERVE_IVARS(OFHTTPResponse, 4)
}

/**
 * @brief The protocol version of the HTTP request reply.
 */
@property (nonatomic) of_http_request_protocol_version_t protocolVersion;

/**
 * @brief The protocol version of the HTTP request reply as a string.
 */
@property (copy, nonatomic) OFString *protocolVersionString;

/**
 * @brief The status code of the reply to the HTTP request.
 */
@property (nonatomic) short statusCode;

/**
 * @brief The headers of the reply to the HTTP request.
 */
@property (copy, nonatomic) OFDictionary OF_GENERIC(OFString *, OFString *)
    *headers;

/**
 * @brief The reply as a string, trying to detect the encoding.



 */
@property (readonly, nonatomic) OFString *string;

/**
 * @brief Returns the reply as a string, trying to detect the encoding and
 *	  falling back to the specified encoding if not detectable.
 *
 * @return The reply as a string
 */
- (OFString *)stringWithEncoding: (of_string_encoding_t)encoding;
@end

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Returns a description string for the specified HTTP status code.
 *
 * @param code The HTTP status code to return a description string for
 * @return A description string for the specified HTTP status code
 */
extern OFString *_Nonnull of_http_status_code_to_string(short code);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|



|






|

|


|




|




|





|
>
>
>

|


|


|

|











|





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

@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@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;

/**
 * @brief The headers of the response to the HTTP request.
 */
@property (copy, nonatomic) OFDictionary OF_GENERIC(OFString *, OFString *)
    *headers;

/**
 * @brief Read the response as a string, trying to detect the encoding and
 *	  falling back to the specified encoding if not detectable.
 *
 * @return The response as a string
 */
- (OFString *)readString;

/**
 * @brief Rread the response as a string, trying to detect the encoding and
 *	  falling back to the specified encoding if not detectable.
 *
 * @return The response as a string
 */
- (OFString *)readStringWithEncoding: (OFStringEncoding)encoding;
@end

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Returns a description string for the specified HTTP status code.
 *
 * @param code The HTTP status code to return a description string for
 * @return A description string for the specified HTTP status code
 */
extern OFString *_Nonnull OFHTTPStatusCodeString(short code);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFHTTPResponse.m from [4e1a4b346b] to [17a72a5d36].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedVersionException.h"

OFString *
of_http_status_code_to_string(short code)
{
	switch (code) {
	case 100:
		return @"Continue";
	case 101:
		return @"Switching Protocols";
	case 200:







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedVersionException.h"

OFString *
OFHTTPStatusCodeString(short code)
{
	switch (code) {
	case 100:
		return @"Continue";
	case 101:
		return @"Switching Protocols";
	case 200:
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
	case 505:
		return @"HTTP Version Not Supported";
	default:
		return @"(unknown)";
	}
}

static of_string_encoding_t
encodingForContentType(OFString *contentType)
{
	const char *UTF8String = contentType.UTF8String;
	size_t last, length = contentType.UTF8StringLength;
	enum {
		STATE_TYPE,
		STATE_BEFORE_PARAM_NAME,
		STATE_PARAM_NAME,
		STATE_PARAM_VALUE_OR_QUOTE,
		STATE_PARAM_VALUE,
		STATE_PARAM_QUOTED_VALUE,
		STATE_AFTER_PARAM_VALUE
	} state = STATE_TYPE;
	OFString *name = nil, *value = nil, *charset = nil;
	of_string_encoding_t ret;

	last = 0;
	for (size_t i = 0; i < length; i++) {
		switch (state) {
		case STATE_TYPE:
			if (UTF8String[i] == ';') {
				state = STATE_BEFORE_PARAM_NAME;
				last = i + 1;
			}
			break;
		case STATE_BEFORE_PARAM_NAME:
			if (UTF8String[i] == ' ')
				last = i + 1;
			else {
				state = STATE_PARAM_NAME;
				i--;
			}
			break;
		case STATE_PARAM_NAME:
			if (UTF8String[i] == '=') {
				name = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				state = STATE_PARAM_VALUE_OR_QUOTE;
				last = i + 1;
			}
			break;
		case STATE_PARAM_VALUE_OR_QUOTE:
			if (UTF8String[i] == '"') {
				state = STATE_PARAM_QUOTED_VALUE;
				last = i + 1;
			} else {
				state = STATE_PARAM_VALUE;
				i--;
			}
			break;
		case STATE_PARAM_VALUE:
			if (UTF8String[i] == ';') {
				value = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];
				value =
				    value.stringByDeletingTrailingWhitespaces;

				if ([name isEqual: @"charset"])
					charset = value;

				state = STATE_BEFORE_PARAM_NAME;
				last = i + 1;
			}
			break;
		case STATE_PARAM_QUOTED_VALUE:
			if (UTF8String[i] == '"') {
				value = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				if ([name isEqual: @"charset"])
					charset = value;

				state = STATE_AFTER_PARAM_VALUE;
			}
			break;
		case STATE_AFTER_PARAM_VALUE:
			if (UTF8String[i] == ';') {
				state = STATE_BEFORE_PARAM_NAME;
				last = i + 1;
			} else if (UTF8String[i] != ' ')
				return OF_STRING_ENCODING_AUTODETECT;
			break;
		}
	}
	if (state == STATE_PARAM_VALUE) {
		value = [OFString stringWithUTF8String: UTF8String + last
						length: length - last];
		value = value.stringByDeletingTrailingWhitespaces;

		if ([name isEqual: @"charset"])
			charset = value;
	}

	@try {
		ret = of_string_parse_encoding(charset);
	} @catch (OFInvalidArgumentException *e) {
		ret = OF_STRING_ENCODING_AUTODETECT;
	}

	return ret;
}

@implementation OFHTTPResponse
@synthesize statusCode = _statusCode, headers = _headers;







|





|
|
|
|
|
|
|
|

|




|

|



|



|



|





|



|

|


|



|










|



|








|


|

|


|



|









|

|







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
	case 505:
		return @"HTTP Version Not Supported";
	default:
		return @"(unknown)";
	}
}

static OFStringEncoding
encodingForContentType(OFString *contentType)
{
	const char *UTF8String = contentType.UTF8String;
	size_t last, length = contentType.UTF8StringLength;
	enum {
		stateType,
		stateBeforeParamName,
		stateParamName,
		stateParamValueOrQuote,
		stateParamValue,
		stateParamQuotedValue,
		stateAfterParamValue
	} state = stateType;
	OFString *name = nil, *value = nil, *charset = nil;
	OFStringEncoding ret;

	last = 0;
	for (size_t i = 0; i < length; i++) {
		switch (state) {
		case stateType:
			if (UTF8String[i] == ';') {
				state = stateBeforeParamName;
				last = i + 1;
			}
			break;
		case stateBeforeParamName:
			if (UTF8String[i] == ' ')
				last = i + 1;
			else {
				state = stateParamName;
				i--;
			}
			break;
		case stateParamName:
			if (UTF8String[i] == '=') {
				name = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				state = stateParamValueOrQuote;
				last = i + 1;
			}
			break;
		case stateParamValueOrQuote:
			if (UTF8String[i] == '"') {
				state = stateParamQuotedValue;
				last = i + 1;
			} else {
				state = stateParamValue;
				i--;
			}
			break;
		case stateParamValue:
			if (UTF8String[i] == ';') {
				value = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];
				value =
				    value.stringByDeletingTrailingWhitespaces;

				if ([name isEqual: @"charset"])
					charset = value;

				state = stateBeforeParamName;
				last = i + 1;
			}
			break;
		case stateParamQuotedValue:
			if (UTF8String[i] == '"') {
				value = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				if ([name isEqual: @"charset"])
					charset = value;

				state = stateAfterParamValue;
			}
			break;
		case stateAfterParamValue:
			if (UTF8String[i] == ';') {
				state = stateBeforeParamName;
				last = i + 1;
			} else if (UTF8String[i] != ' ')
				return OFStringEncodingAutodetect;
			break;
		}
	}
	if (state == stateParamValue) {
		value = [OFString stringWithUTF8String: UTF8String + last
						length: length - last];
		value = value.stringByDeletingTrailingWhitespaces;

		if ([name isEqual: @"charset"])
			charset = value;
	}

	@try {
		ret = OFStringEncodingParseName(charset);
	} @catch (OFInvalidArgumentException *e) {
		ret = OFStringEncodingAutodetect;
	}

	return ret;
}

@implementation OFHTTPResponse
@synthesize statusCode = _statusCode, headers = _headers;
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
- (void)dealloc
{
	[_headers release];

	[super dealloc];
}

- (void)setProtocolVersion: (of_http_request_protocol_version_t)protocolVersion
{
	if (protocolVersion.major != 1 || protocolVersion.minor > 1)
		@throw [OFUnsupportedVersionException exceptionWithVersion:
		    [OFString stringWithFormat: @"%hhu.%hhu",
						protocolVersion.major,
						protocolVersion.minor]];

	_protocolVersion = protocolVersion;
}

- (of_http_request_protocol_version_t)protocolVersion
{
	return _protocolVersion;
}

- (void)setProtocolVersionString: (OFString *)string
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *components = [string componentsSeparatedByString: @"."];
	unsigned long long major, minor;
	of_http_request_protocol_version_t protocolVersion;

	if (components.count != 2)
		@throw [OFInvalidFormatException exception];

	major = [components.firstObject unsignedLongLongValue];
	minor = [components.lastObject unsignedLongLongValue];








|










|









|







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
- (void)dealloc
{
	[_headers release];

	[super dealloc];
}

- (void)setProtocolVersion: (OFHTTPRequestProtocolVersion)protocolVersion
{
	if (protocolVersion.major != 1 || protocolVersion.minor > 1)
		@throw [OFUnsupportedVersionException exceptionWithVersion:
		    [OFString stringWithFormat: @"%hhu.%hhu",
						protocolVersion.major,
						protocolVersion.minor]];

	_protocolVersion = protocolVersion;
}

- (OFHTTPRequestProtocolVersion)protocolVersion
{
	return _protocolVersion;
}

- (void)setProtocolVersionString: (OFString *)string
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *components = [string componentsSeparatedByString: @"."];
	unsigned long long major, minor;
	OFHTTPRequestProtocolVersion protocolVersion;

	if (components.count != 2)
		@throw [OFInvalidFormatException exception];

	major = [components.firstObject unsignedLongLongValue];
	minor = [components.lastObject unsignedLongLongValue];

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
- (OFString *)protocolVersionString
{
	return [OFString stringWithFormat: @"%hhu.%hhu",
					   _protocolVersion.major,
					   _protocolVersion.minor];
}

- (OFString *)string
{
	return [self stringWithEncoding: OF_STRING_ENCODING_AUTODETECT];
}

- (OFString *)stringWithEncoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFString *contentType, *contentLengthString, *ret;
	OFData *data;

	if (encoding == OF_STRING_ENCODING_AUTODETECT &&
	    (contentType = [_headers objectForKey: @"Content-Type"]) != nil)
		encoding = encodingForContentType(contentType);

	if (encoding == OF_STRING_ENCODING_AUTODETECT)
		encoding = OF_STRING_ENCODING_UTF_8;

	data = [self readDataUntilEndOfStream];

	contentLengthString = [_headers objectForKey: @"Content-Length"];
	if (contentLengthString != nil) {
		unsigned long long contentLength =
		    contentLengthString.unsignedLongLongValue;







|

|


|





|



|
|







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
- (OFString *)protocolVersionString
{
	return [OFString stringWithFormat: @"%hhu.%hhu",
					   _protocolVersion.major,
					   _protocolVersion.minor];
}

- (OFString *)readString
{
	return [self readStringWithEncoding: OFStringEncodingAutodetect];
}

- (OFString *)readStringWithEncoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFString *contentType, *contentLengthString, *ret;
	OFData *data;

	if (encoding == OFStringEncodingAutodetect &&
	    (contentType = [_headers objectForKey: @"Content-Type"]) != nil)
		encoding = encodingForContentType(contentType);

	if (encoding == OFStringEncodingAutodetect)
		encoding = OFStringEncodingUTF8;

	data = [self readDataUntilEndOfStream];

	contentLengthString = [_headers objectForKey: @"Content-Length"];
	if (contentLengthString != nil) {
		unsigned long long contentLength =
		    contentLengthString.unsignedLongLongValue;

Modified src/OFHTTPServer.h from [19c7df3c10] to [a2fd4b8141].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
 *	   connections will still be handled and you can start accepting new
 *	   connections again by calling @ref OFHTTPServer::start again.
 */
-			  (bool)server: (OFHTTPServer *)server
  didReceiveExceptionOnListeningSocket: (id)exception;

/**
 * @brief This method is called when a client socket encountered an exception.

 *
 * This can happen when the OFHTTPServer tries to properly close the
 * connection. If no headers have been sent yet, it will send headers, and if
 * chunked transfer encoding was used, it will send a chunk of size 0. However,
 * if the other end already closed the connection before that, this will raise
 * an exception.
 *







|
>







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 *	   connections will still be handled and you can start accepting new
 *	   connections again by calling @ref OFHTTPServer::start again.
 */
-			  (bool)server: (OFHTTPServer *)server
  didReceiveExceptionOnListeningSocket: (id)exception;

/**
 * @brief This method is called when a socket for a client encountered an
 * exception.
 *
 * This can happen when the OFHTTPServer tries to properly close the
 * connection. If no headers have been sent yet, it will send headers, and if
 * chunked transfer encoding was used, it will send a chunk of size 0. However,
 * if the other end already closed the connection before that, this will raise
 * an exception.
 *
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
 * @brief A class for creating a simple HTTP server inside of applications.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFHTTPServer: OFObject
{
	OFString *_Nullable _host;
	uint16_t _port;
	bool _usesTLS;
	OFString *_Nullable _certificateFile, *_Nullable _privateKeyFile;
	const char *_Nullable _privateKeyPassphrase;
	id <OFHTTPServerDelegate> _Nullable _delegate;
	OFString *_Nullable _name;
	OFTCPSocket *_Nullable _listeningSocket;
#ifdef OF_HAVE_THREADS
	size_t _numberOfThreads, _nextThreadIndex;
	OFArray *_threadPool;
#endif







<
<
<







90
91
92
93
94
95
96



97
98
99
100
101
102
103
 * @brief A class for creating a simple HTTP server inside of applications.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFHTTPServer: OFObject
{
	OFString *_Nullable _host;
	uint16_t _port;



	id <OFHTTPServerDelegate> _Nullable _delegate;
	OFString *_Nullable _name;
	OFTCPSocket *_Nullable _listeningSocket;
#ifdef OF_HAVE_THREADS
	size_t _numberOfThreads, _nextThreadIndex;
	OFArray *_threadPool;
#endif
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
 * @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 Whether the HTTP server uses TLS.
 *
 * Setting this after @ref start has been called raises an
 * @ref OFAlreadyConnectedException.
 */
@property (nonatomic) bool usesTLS;

/**
 * @brief The path to the X.509 certificate file to use for TLS.
 *
 * Setting this after @ref start has been called raises an
 * @ref OFAlreadyConnectedException.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *certificateFile;

/**
 * @brief The path to the PKCS#8 private key file to use for TLS.
 *
 * Setting this after @ref start has been called raises an
 * @ref OFAlreadyConnectedException.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *privateKeyFile;

/**
 * @brief The passphrase to decrypt the PKCS#8 private key file for TLS.
 *
 * @warning You have to ensure that this is in secure memory protected from
 *	    swapping! This is also the reason why this is not an OFString.
 *
 * Setting this after @ref start has been called raises an
 * @ref OFAlreadyConnectedException.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    const char *privateKeyPassphrase;

/**
 * @brief The delegate for the HTTP server.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFHTTPServerDelegate> delegate;

#ifdef OF_HAVE_THREADS







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







115
116
117
118
119
120
121




































122
123
124
125
126
127
128
 * @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

Modified src/OFHTTPServer.m from [59a786282c] to [56dd1ea76e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <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 "OFTCPSocket.h"
#import "OFTLSSocket.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"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFWriteFailedException.h"

#import "socket_helpers.h"

#define BUFFER_SIZE 1024

/*
 * FIXME: Key normalization replaces headers like "DNT" with "Dnt".
 * FIXME: Errors are not reported to the user.
 */

@interface OFHTTPServer () <OFTCPSocketDelegate>
@end

<
<
|















>











|
|















<
<
<
<







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


 * 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>
#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"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFWriteFailedException.h"





/*
 * FIXME: Key normalization replaces headers like "DNT" with "Dnt".
 * FIXME: Errors are not reported to the user.
 */

@interface OFHTTPServer () <OFTCPSocketDelegate>
@end
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@interface OFHTTPServerConnection: OFObject <OFTCPSocketDelegate>
{
@public
	OFStreamSocket *_socket;
	OFHTTPServer *_server;
	OFTimer *_timer;
	enum {
		AWAITING_PROLOG,
		PARSING_HEADERS,
		SEND_RESPONSE
	} _state;
	uint8_t _HTTPMinorVersion;
	of_http_request_method_t _method;
	OFString *_host, *_path;
	uint16_t _port;
	OFMutableDictionary *_headers;
	size_t _contentLength;
	OFStream *_requestBody;
}








|
|
|


|







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@interface OFHTTPServerConnection: OFObject <OFTCPSocketDelegate>
{
@public
	OFStreamSocket *_socket;
	OFHTTPServer *_server;
	OFTimer *_timer;
	enum {
		stateAwaitingProlog,
		stateParsingHeaders,
		stateSendResponse
	} _state;
	uint8_t _HTTPMinorVersion;
	OFHTTPRequestMethod _method;
	OFString *_host, *_path;
	uint16_t _port;
	OFMutableDictionary *_headers;
	size_t _contentLength;
	OFStream *_requestBody;
}

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
#ifdef OF_HAVE_THREADS
OF_DIRECT_MEMBERS
@interface OFHTTPServerThread: OFThread
- (void)stop;
@end
#endif

static OF_INLINE OFString *
normalizedKey(OFString *key)
{
	char *cString = of_strdup(key.UTF8String);
	unsigned char *tmp = (unsigned char *)cString;
	bool firstLetter = true;

	if (cString == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: strlen(key.UTF8String)];

	while (*tmp != '\0') {
		if (!of_ascii_isalpha(*tmp)) {
			firstLetter = true;
			tmp++;
			continue;
		}

		*tmp = (firstLetter
		    ? of_ascii_toupper(*tmp)
		    : of_ascii_tolower(*tmp));

		firstLetter = false;
		tmp++;
	}


	return [OFString stringWithUTF8StringNoCopy: cString
				       freeWhenDone: true];






}

@implementation OFHTTPServerResponse
- (instancetype)initWithSocket: (OFStreamSocket *)sock
			server: (OFHTTPServer *)server
		       request: (OFHTTPRequest *)request
{







|


|


<
|
<
<


|






<
|





>
|
|
>
>
>
>
>
>







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
#ifdef OF_HAVE_THREADS
OF_DIRECT_MEMBERS
@interface OFHTTPServerThread: OFThread
- (void)stop;
@end
#endif

static OFString *
normalizedKey(OFString *key)
{
	char *cString = OFStrDup(key.UTF8String);
	unsigned char *tmp = (unsigned char *)cString;
	bool firstLetter = true;

	OFString *ret;



	while (*tmp != '\0') {
		if (!OFASCIIIsAlpha(*tmp)) {
			firstLetter = true;
			tmp++;
			continue;
		}

		*tmp = (firstLetter

		    ? OFASCIIToUpper(*tmp) : OFASCIIToLower(*tmp));

		firstLetter = false;
		tmp++;
	}

	@try {
		ret = [OFString stringWithUTF8StringNoCopy: cString
					      freeWhenDone: true];
	} @catch (id e) {
		OFFreeMemory(cString);
		@throw e;
	}

	return ret;
}

@implementation OFHTTPServerResponse
- (instancetype)initWithSocket: (OFStreamSocket *)sock
			server: (OFHTTPServer *)server
		       request: (OFHTTPRequest *)request
{
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
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers;
	OFEnumerator *keyEnumerator, *valueEnumerator;
	OFString *key, *value;

	[_socket writeFormat: @"HTTP/%@ %hd %@\r\n",
			      self.protocolVersionString, _statusCode,
			      of_http_status_code_to_string(_statusCode)];

	headers = [[_headers mutableCopy] autorelease];

	if ([headers objectForKey: @"Date"] == nil) {
		OFString *date = [[OFDate date]
		    dateStringWithFormat: @"%a, %d %b %Y %H:%M:%S GMT"];

		[headers setObject: date
			    forKey: @"Date"];
	}

	if ([headers objectForKey: @"Server"] == nil) {
		OFString *name = _server.name;

		if (name != nil)
			[headers setObject: name
				    forKey: @"Server"];
	}

	keyEnumerator = [headers keyEnumerator];
	valueEnumerator = [headers objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	    (value = [valueEnumerator nextObject]) != nil)
		[_socket writeFormat: @"%@: %@\r\n", key, value];

	[_socket writeString: @"\r\n"];

	_headersSent = true;
	_chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	objc_autoreleasePoolPop(pool);
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	/* TODO: Use non-blocking writes */

	void *pool;

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

	if (!_headersSent)
		[self of_sendHeaders];

	if (!_chunked)

		return [_socket writeBuffer: buffer







				     length: length];


	pool = objc_autoreleasePoolPush();
	[_socket writeString: [OFString stringWithFormat: @"%zX\r\n", length]];
	objc_autoreleasePoolPop(pool);

	[_socket writeBuffer: buffer
		      length: length];
	[_socket writeString: @"\r\n"];

	return length;
}

- (void)close
{







|






<
|
<






|
<

















|
<











|
>
|
>
>
>
>
>
>
>
|
>





|
<







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
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers;
	OFEnumerator *keyEnumerator, *valueEnumerator;
	OFString *key, *value;

	[_socket writeFormat: @"HTTP/%@ %hd %@\r\n",
			      self.protocolVersionString, _statusCode,
			      OFHTTPStatusCodeString(_statusCode)];

	headers = [[_headers mutableCopy] autorelease];

	if ([headers objectForKey: @"Date"] == nil) {
		OFString *date = [[OFDate date]
		    dateStringWithFormat: @"%a, %d %b %Y %H:%M:%S GMT"];

		[headers setObject: date forKey: @"Date"];

	}

	if ([headers objectForKey: @"Server"] == nil) {
		OFString *name = _server.name;

		if (name != nil)
			[headers setObject: name forKey: @"Server"];

	}

	keyEnumerator = [headers keyEnumerator];
	valueEnumerator = [headers objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	    (value = [valueEnumerator nextObject]) != nil)
		[_socket writeFormat: @"%@: %@\r\n", key, value];

	[_socket writeString: @"\r\n"];

	_headersSent = true;
	_chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	objc_autoreleasePoolPop(pool);
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{
	/* TODO: Use non-blocking writes */

	void *pool;

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

	if (!_headersSent)
		[self of_sendHeaders];

	if (!_chunked) {
		@try {
			[_socket writeBuffer: buffer length: length];
		} @catch (OFWriteFailedException *e) {
			if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN)
				return e.bytesWritten;

			@throw e;
		}

		return length;
	}

	pool = objc_autoreleasePoolPush();
	[_socket writeString: [OFString stringWithFormat: @"%zX\r\n", length]];
	objc_autoreleasePoolPop(pool);

	[_socket writeBuffer: buffer length: length];

	[_socket writeString: @"\r\n"];

	return length;
}

- (void)close
{
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
		_server = [server retain];
		_timer = [[OFTimer
		    scheduledTimerWithTimeInterval: 10
					    target: _socket
					  selector: @selector(
							cancelAsyncRequests)
					   repeats: false] retain];
		_state = AWAITING_PROLOG;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







|







299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
		_server = [server retain];
		_timer = [[OFTimer
		    scheduledTimerWithTimeInterval: 10
					    target: _socket
					  selector: @selector(
							cancelAsyncRequests)
					   repeats: false] retain];
		_state = stateAwaitingProlog;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
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
     exception: (id)exception
{
	if (line == nil || exception != nil)
		return false;

	@try {
		switch (_state) {
		case AWAITING_PROLOG:
			return [self parseProlog: line];
		case PARSING_HEADERS:
			return [self parseHeaders: line];
		default:
			return false;
		}
	} @catch (OFWriteFailedException *e) {
		return false;
	}

	OF_ENSURE(0);
}

- (bool)parseProlog: (OFString *)line
{
	OFString *method;
	OFMutableString *path;
	size_t pos;

	@try {
		OFString *version = [line
		    substringWithRange: of_range(line.length - 9, 9)];
		of_unichar_t tmp;

		if (![version hasPrefix: @" HTTP/1."])
			return [self sendErrorAndClose: 505];

		tmp = [version characterAtIndex: 8];
		if (tmp < '0' || tmp > '9')
			return [self sendErrorAndClose: 400];

		_HTTPMinorVersion = (uint8_t)(tmp - '0');
	} @catch (OFOutOfRangeException *e) {
		return [self sendErrorAndClose: 400];
	}

	pos = [line rangeOfString: @" "].location;
	if (pos == OF_NOT_FOUND)
		return [self sendErrorAndClose: 400];

	method = [line substringToIndex: pos];
	@try {
		_method = of_http_request_method_from_string(method);
	} @catch (OFInvalidArgumentException *e) {
		return [self sendErrorAndClose: 405];
	}

	@try {
		of_range_t range = of_range(pos + 1, line.length - pos - 10);

		path = [[[line substringWithRange:
		    range] mutableCopy] autorelease];
	} @catch (OFOutOfRangeException *e) {
		return [self sendErrorAndClose: 400];
	}

	[path deleteEnclosingWhitespaces];
	[path makeImmutable];

	if (![path hasPrefix: @"/"])
		return [self sendErrorAndClose: 400];

	_headers = [[OFMutableDictionary alloc] init];
	_path = [path copy];
	_state = PARSING_HEADERS;

	return true;
}

- (bool)parseHeaders: (OFString *)line
{
	OFString *key, *value, *old;







|

|








|










|
|














|




|





|















|







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
     exception: (id)exception
{
	if (line == nil || exception != nil)
		return false;

	@try {
		switch (_state) {
		case stateAwaitingProlog:
			return [self parseProlog: line];
		case stateParsingHeaders:
			return [self parseHeaders: line];
		default:
			return false;
		}
	} @catch (OFWriteFailedException *e) {
		return false;
	}

	OFEnsure(0);
}

- (bool)parseProlog: (OFString *)line
{
	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')
			return [self sendErrorAndClose: 400];

		_HTTPMinorVersion = (uint8_t)(tmp - '0');
	} @catch (OFOutOfRangeException *e) {
		return [self sendErrorAndClose: 400];
	}

	pos = [line rangeOfString: @" "].location;
	if (pos == OFNotFound)
		return [self sendErrorAndClose: 400];

	method = [line substringToIndex: pos];
	@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];
	}

	[path deleteEnclosingWhitespaces];
	[path makeImmutable];

	if (![path hasPrefix: @"/"])
		return [self sendErrorAndClose: 400];

	_headers = [[OFMutableDictionary alloc] init];
	_path = [path copy];
	_state = stateParsingHeaders;

	return true;
}

- (bool)parseHeaders: (OFString *)line
{
	OFString *key, *value, *old;
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
			     contentLength: contentLength];

			[_timer invalidate];
			[_timer release];
			_timer = nil;
		}

		_state = SEND_RESPONSE;
		[self createResponse];

		return false;
	}

	pos = [line rangeOfString: @":"].location;
	if (pos == OF_NOT_FOUND)
		return [self sendErrorAndClose: 400];

	key = [line substringToIndex: pos];
	value = [line substringFromIndex: pos + 1];

	key = normalizedKey(key.stringByDeletingTrailingWhitespaces);
	value = value.stringByDeletingLeadingWhitespaces;

	old = [_headers objectForKey: key];
	if (old != nil)
		value = [old stringByAppendingFormat: @",%@", value];

	[_headers setObject: value
		     forKey: key];

	if ([key isEqual: @"Host"]) {
		pos = [value
		    rangeOfString: @":"
			  options: OF_STRING_SEARCH_BACKWARDS].location;

		if (pos != OF_NOT_FOUND) {
			[_host release];
			_host = [[value substringToIndex: pos] retain];

			@try {
				unsigned long long portTmp =
				    [value substringFromIndex: pos + 1]
				    .unsignedLongLongValue;







|






|












|
<


|
<
|

|







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
			     contentLength: contentLength];

			[_timer invalidate];
			[_timer release];
			_timer = nil;
		}

		_state = stateSendResponse;
		[self createResponse];

		return false;
	}

	pos = [line rangeOfString: @":"].location;
	if (pos == OFNotFound)
		return [self sendErrorAndClose: 400];

	key = [line substringToIndex: pos];
	value = [line substringFromIndex: pos + 1];

	key = normalizedKey(key.stringByDeletingTrailingWhitespaces);
	value = value.stringByDeletingLeadingWhitespaces;

	old = [_headers objectForKey: key];
	if (old != nil)
		value = [old stringByAppendingFormat: @",%@", value];

	[_headers setObject: value forKey: key];


	if ([key isEqual: @"Host"]) {
		pos = [value rangeOfString: @":"

				   options: OFStringSearchBackwards].location;

		if (pos != OFNotFound) {
			[_host release];
			_host = [[value substringToIndex: pos] retain];

			@try {
				unsigned long long portTmp =
				    [value substringFromIndex: pos + 1]
				    .unsignedLongLongValue;
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
	return true;
}

- (bool)sendErrorAndClose: (short)statusCode
{
	OFString *date = [[OFDate date]
	    dateStringWithFormat: @"%a, %d %b %Y %H:%M:%S GMT"];

	[_socket writeFormat: @"HTTP/1.1 %hd %@\r\n"
			      @"Date: %@\r\n"
			      @"Server: %@\r\n"
			      @"\r\n",
			      statusCode,
			      of_http_status_code_to_string(statusCode),
			      date, _server.name];

	return false;
}

- (void)createResponse
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableURL *URL;







<




|
<

<







496
497
498
499
500
501
502

503
504
505
506
507

508

509
510
511
512
513
514
515
	return true;
}

- (bool)sendErrorAndClose: (short)statusCode
{
	OFString *date = [[OFDate date]
	    dateStringWithFormat: @"%a, %d %b %Y %H:%M:%S GMT"];

	[_socket writeFormat: @"HTTP/1.1 %hd %@\r\n"
			      @"Date: %@\r\n"
			      @"Server: %@\r\n"
			      @"\r\n",
			      statusCode, OFHTTPStatusCodeString(statusCode),

			      date, _server.name];

	return false;
}

- (void)createResponse
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableURL *URL;
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

	URL = [OFMutableURL URL];
	URL.scheme = @"http";
	URL.host = _host;
	if (_port != 80)
		URL.port = [OFNumber numberWithUnsignedShort: _port];


	if ((pos = [_path rangeOfString: @"?"].location) != OF_NOT_FOUND) {

		OFString *path, *query;

		path = [_path substringToIndex: pos];
		query = [_path substringFromIndex: pos + 1];

		URL.URLEncodedPath = path;
		URL.URLEncodedQuery = query;
	} else
		URL.URLEncodedPath = _path;






	[URL makeImmutable];

	request = [OFHTTPRequest requestWithURL: URL];
	request.method = _method;
	request.protocolVersion =
	    (of_http_request_protocol_version_t){ 1, _HTTPMinorVersion };
	request.headers = _headers;
	request.remoteAddress = _socket.remoteAddress;

	response = [[[OFHTTPServerResponse alloc]
	    initWithSocket: _socket
		    server: _server
		   request: request] autorelease];







>
|
>
|

|
|

|
|
|
|
>
>
>
>
>






|







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

	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]
	    initWithSocket: _socket
		    server: _server
		   request: request] autorelease];
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
}

- (bool)lowlevelIsAtEndOfStream
{
	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	if (_socket == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_atEndOfStream)
		return 0;

	if (_socket.atEndOfStream)
		@throw [OFTruncatedDataException exception];

	/* Content-Length */
	if (!_chunked) {
		size_t ret;

		if (length > (unsigned long long)_toRead)
			length = (size_t)_toRead;

		ret = [_socket readIntoBuffer: buffer
				       length: length];

		_toRead -= ret;

		if (_toRead == 0)
			_atEndOfStream = true;

		return ret;
	}

	/* Chunked */
	if (_toRead == -2) {
		char tmp[2];

		switch ([_socket readIntoBuffer: tmp
					 length: 2]) {
		case 2:
			_toRead++;
			if (tmp[1] != '\n')
				@throw [OFInvalidFormatException exception];
		case 1:
			_toRead++;
			if (tmp[0] != '\r')
				@throw [OFInvalidFormatException exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;
	} else if (_toRead == -1) {
		char tmp;

		if ([_socket readIntoBuffer: &tmp
				     length: 1] == 1) {
			_toRead++;
			if (tmp != '\n')
				@throw [OFInvalidFormatException exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;
	} else if (_toRead > 0) {
		if (length > (unsigned long long)_toRead)
			length = (size_t)_toRead;

		length = [_socket readIntoBuffer: buffer
					  length: length];

		_toRead -= length;

		if (_toRead == 0)
			_toRead = -2;

		return length;
	} else {
		void *pool = objc_autoreleasePoolPush();
		OFString *line;
		size_t pos;
		unsigned long long toRead;

		@try {
			line = [_socket tryReadLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidFormatException exception];
		}

		if (line == nil)
			return 0;

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

		if (line.length < 1) {
			/*
			 * We have read the empty string because the socket is
			 * at end of stream.
			 */
			if (_socket.atEndOfStream && pos == OF_NOT_FOUND)
				@throw [OFTruncatedDataException exception];
			else
				@throw [OFInvalidFormatException exception];
		}

		toRead = [line unsignedLongLongValueWithBase: 16];
		if (toRead > LLONG_MAX)







|
<

















|
<













|
<

















|
<













|
<


<




















|







|







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
}

- (bool)lowlevelIsAtEndOfStream
{
	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

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

	if (_atEndOfStream)
		return 0;

	if (_socket.atEndOfStream)
		@throw [OFTruncatedDataException exception];

	/* Content-Length */
	if (!_chunked) {
		size_t ret;

		if (length > (unsigned long long)_toRead)
			length = (size_t)_toRead;

		ret = [_socket readIntoBuffer: buffer length: length];


		_toRead -= ret;

		if (_toRead == 0)
			_atEndOfStream = true;

		return ret;
	}

	/* Chunked */
	if (_toRead == -2) {
		char tmp[2];

		switch ([_socket readIntoBuffer: tmp length: 2]) {

		case 2:
			_toRead++;
			if (tmp[1] != '\n')
				@throw [OFInvalidFormatException exception];
		case 1:
			_toRead++;
			if (tmp[0] != '\r')
				@throw [OFInvalidFormatException exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;
	} else if (_toRead == -1) {
		char tmp;

		if ([_socket readIntoBuffer: &tmp length: 1] == 1) {

			_toRead++;
			if (tmp != '\n')
				@throw [OFInvalidFormatException exception];
		}

		if (_setAtEndOfStream && _toRead == 0)
			_atEndOfStream = true;

		return 0;
	} else if (_toRead > 0) {
		if (length > (unsigned long long)_toRead)
			length = (size_t)_toRead;

		length = [_socket readIntoBuffer: buffer length: length];


		_toRead -= length;

		if (_toRead == 0)
			_toRead = -2;

		return length;
	} else {
		void *pool = objc_autoreleasePoolPush();
		OFString *line;
		size_t pos;
		unsigned long long toRead;

		@try {
			line = [_socket tryReadLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidFormatException 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 socket is
			 * at end of stream.
			 */
			if (_socket.atEndOfStream && pos == OFNotFound)
				@throw [OFTruncatedDataException exception];
			else
				@throw [OFInvalidFormatException exception];
		}

		toRead = [line unsignedLongLongValueWithBase: 16];
		if (toRead > LLONG_MAX)
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
}

- (uint16_t)port
{
	return _port;
}

- (void)setUsesTLS: (bool)usesTLS
{
	if (_listeningSocket != nil)
		@throw [OFAlreadyConnectedException exception];

	_usesTLS = usesTLS;
}

- (bool)usesTLS
{
	return _usesTLS;
}

- (void)setCertificateFile: (OFString *)certificateFile
{
	OFString *old;

	if (_listeningSocket != nil)
		@throw [OFAlreadyConnectedException exception];

	old = _certificateFile;
	_certificateFile = [certificateFile copy];
	[old release];
}

- (OFString *)certificateFile
{
	return _certificateFile;
}

- (void)setPrivateKeyFile: (OFString *)privateKeyFile
{
	OFString *old;

	if (_listeningSocket != nil)
		@throw [OFAlreadyConnectedException exception];

	old = _privateKeyFile;
	_privateKeyFile = [privateKeyFile copy];
	[old release];
}

- (OFString *)privateKeyFile
{
	return _privateKeyFile;
}

- (void)setPrivateKeyPassphrase: (const char *)privateKeyPassphrase
{
	if (_listeningSocket != nil)
		@throw [OFAlreadyConnectedException exception];

	_privateKeyPassphrase = privateKeyPassphrase;
}

- (const char *)privateKeyPassphrase
{
	return _privateKeyPassphrase;
}

#ifdef OF_HAVE_THREADS
- (void)setNumberOfThreads: (size_t)numberOfThreads
{
	if (numberOfThreads == 0)
		@throw [OFInvalidArgumentException exception];

	if (_listeningSocket != nil)







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







824
825
826
827
828
829
830




























































831
832
833
834
835
836
837
}

- (uint16_t)port
{
	return _port;
}





























































#ifdef OF_HAVE_THREADS
- (void)setNumberOfThreads: (size_t)numberOfThreads
{
	if (numberOfThreads == 0)
		@throw [OFInvalidArgumentException exception];

	if (_listeningSocket != nil)
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

	if (_host == nil)
		@throw [OFInvalidArgumentException exception];

	if (_listeningSocket != nil)
		@throw [OFAlreadyConnectedException exception];

	if (_usesTLS) {
		OFTCPSocket <OFTLSSocket> *TLSSocket;

		if (of_tls_socket_class == Nil)
			@throw [OFUnsupportedProtocolException exception];

		TLSSocket = [[of_tls_socket_class alloc] init];
		_listeningSocket = TLSSocket;

		TLSSocket.certificateFile = _certificateFile;
		TLSSocket.privateKeyFile = _privateKeyFile;
		TLSSocket.privateKeyPassphrase = _privateKeyPassphrase;
	} else
		_listeningSocket = [[OFTCPSocket alloc] init];

	_port = [_listeningSocket bindToHost: _host
					port: _port];
	[_listeningSocket listen];

#ifdef OF_HAVE_THREADS
	if (_numberOfThreads > 1) {
		OFMutableArray *threads =
		    [OFMutableArray arrayWithCapacity: _numberOfThreads - 1];








<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|
<







852
853
854
855
856
857
858













859

860

861
862
863
864
865
866
867

	if (_host == nil)
		@throw [OFInvalidArgumentException exception];

	if (_listeningSocket != nil)
		@throw [OFAlreadyConnectedException exception];














	_listeningSocket = [[OFTCPSocket alloc] init];

	_port = [_listeningSocket bindToHost: _host port: _port];

	[_listeningSocket listen];

#ifdef OF_HAVE_THREADS
	if (_numberOfThreads > 1) {
		OFMutableArray *threads =
		    [OFMutableArray arrayWithCapacity: _numberOfThreads - 1];

992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
	exception: (id)exception
{
	if (exception != nil) {
		if (![_delegate respondsToSelector:
		    @selector(server:didReceiveExceptionOnListeningSocket:)])
			return false;

		return [_delegate		  server: self
		    didReceiveExceptionOnListeningSocket: exception];
	}

#ifdef OF_HAVE_THREADS
	if (_numberOfThreads > 1) {
		OFHTTPServerThread *thread =
		    [_threadPool objectAtIndex: _nextThreadIndex];







|







915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
	exception: (id)exception
{
	if (exception != nil) {
		if (![_delegate respondsToSelector:
		    @selector(server:didReceiveExceptionOnListeningSocket:)])
			return false;

		return [_delegate server: self
		    didReceiveExceptionOnListeningSocket: exception];
	}

#ifdef OF_HAVE_THREADS
	if (_numberOfThreads > 1) {
		OFHTTPServerThread *thread =
		    [_threadPool objectAtIndex: _nextThreadIndex];

Modified src/OFHTTPURLHandler.h from [93af9fbb1c] to [6b4755acdc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFHTTPURLHandler.m from [b01378202f] to [c4a43aad17].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFHostAddressResolver.h from [51f39b12b0] to [5c6783fd03].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFDNSResolver.h"
#import "OFRunLoop.h"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

@class OFDNSResolverSettings;
@class OFDNSResourceRecord;
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableData;
@class OFString;

@interface OFHostAddressResolver: OFObject <OFDNSResolverQueryDelegate>
{
	OFString *_host;
	of_socket_address_family_t _addressFamily;
	OFDNSResolver *_resolver;
	OFDNSResolverSettings *_settings;
	of_run_loop_mode_t _Nullable _runLoopMode;
	id <OFDNSResolverHostDelegate> _Nullable _delegate;
	bool _isFQDN;
	size_t _searchDomainIndex;
	unsigned int _numExpectedResponses;
	OFMutableData *_addresses;
}

- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (of_socket_address_family_t)addressFamily
		    resolver: (OFDNSResolver *)resolver
		    settings: (OFDNSResolverSettings *)settings
		 runLoopMode: (nullable of_run_loop_mode_t)runLoopMode
		    delegate: (nullable id <OFDNSResolverHostDelegate>)delegate;
- (void)asyncResolve;
- (OFData *)resolve;
@end

OF_ASSUME_NONNULL_END

<
<
|
















<
|












|


|








|


|






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


 * 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 "OFDNSResolver.h"
#import "OFRunLoop.h"

#import "OFSocket.h"

OF_ASSUME_NONNULL_BEGIN

@class OFDNSResolverSettings;
@class OFDNSResourceRecord;
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableData;
@class OFString;

@interface OFHostAddressResolver: OFObject <OFDNSResolverQueryDelegate>
{
	OFString *_host;
	OFSocketAddressFamily _addressFamily;
	OFDNSResolver *_resolver;
	OFDNSResolverSettings *_settings;
	OFRunLoopMode _Nullable _runLoopMode;
	id <OFDNSResolverHostDelegate> _Nullable _delegate;
	bool _isFQDN;
	size_t _searchDomainIndex;
	unsigned int _numExpectedResponses;
	OFMutableData *_addresses;
}

- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (OFSocketAddressFamily)addressFamily
		    resolver: (OFDNSResolver *)resolver
		    settings: (OFDNSResolverSettings *)settings
		 runLoopMode: (nullable OFRunLoopMode)runLoopMode
		    delegate: (nullable id <OFDNSResolverHostDelegate>)delegate;
- (void)asyncResolve;
- (OFData *)resolve;
@end

OF_ASSUME_NONNULL_END

Modified src/OFHostAddressResolver.m from [1663ef2651] to [3275d58c93].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@public
	bool _done;
	OFData *_addresses;
	id _exception;
}
@end

static const of_run_loop_mode_t resolveRunLoopMode =
    @"of_host_address_resolver_resolve_mode";

static bool
isFQDN(OFString *host, unsigned int minNumberOfDotsInAbsoluteName)
{
	const char *UTF8String;
	size_t length;
	unsigned int dots;







|
|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@public
	bool _done;
	OFData *_addresses;
	id _exception;
}
@end

static const OFRunLoopMode resolveRunLoopMode =
    @"OFHostAddressResolverResolveRunLoopMode";

static bool
isFQDN(OFString *host, unsigned int minNumberOfDotsInAbsoluteName)
{
	const char *UTF8String;
	size_t length;
	unsigned int dots;
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
			dots++;

	return (dots >= minNumberOfDotsInAbsoluteName);
}

static bool
addressForRecord(OF_KINDOF(OFDNSResourceRecord *) record,
    const of_socket_address_t **address,
    of_socket_address_family_t addressFamily)
{
	switch ([record recordType]) {
#ifdef OF_HAVE_IPV6
	case OF_DNS_RECORD_TYPE_AAAA:
		if (addressFamily != OF_SOCKET_ADDRESS_FAMILY_IPV6 &&
		    addressFamily != OF_SOCKET_ADDRESS_FAMILY_ANY)
			return false;
		break;
#endif
	case OF_DNS_RECORD_TYPE_A:
		if (addressFamily != OF_SOCKET_ADDRESS_FAMILY_IPV4 &&
		    addressFamily != OF_SOCKET_ADDRESS_FAMILY_ANY)
			return false;
		break;
	default:
		return false;
	}

	*address = [record address];
	return true;
}

static void
callDelegateInMode(of_run_loop_mode_t runLoopMode,
    id <OFDNSResolverHostDelegate> delegate, OFDNSResolver *resolver,
    OFString *host, OFData *addresses, id exception)
{
	SEL selector = @selector(resolver:didResolveHost:addresses:exception:);

	if ([delegate respondsToSelector: selector]) {
		OFTimer *timer = [OFTimer







|
<



|
|
|



|
|
|











|







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
			dots++;

	return (dots >= minNumberOfDotsInAbsoluteName);
}

static bool
addressForRecord(OF_KINDOF(OFDNSResourceRecord *) record,
    const OFSocketAddress **address, OFSocketAddressFamily addressFamily)

{
	switch ([record recordType]) {
#ifdef OF_HAVE_IPV6
	case OFDNSRecordTypeAAAA:
		if (addressFamily != OFSocketAddressFamilyIPv6 &&
		    addressFamily != OFSocketAddressFamilyAny)
			return false;
		break;
#endif
	case OFDNSRecordTypeA:
		if (addressFamily != OFSocketAddressFamilyIPv4 &&
		    addressFamily != OFSocketAddressFamilyAny)
			return false;
		break;
	default:
		return false;
	}

	*address = [record address];
	return true;
}

static void
callDelegateInMode(OFRunLoopMode runLoopMode,
    id <OFDNSResolverHostDelegate> delegate, OFDNSResolver *resolver,
    OFString *host, OFData *addresses, id exception)
{
	SEL selector = @selector(resolver:didResolveHost:addresses:exception:);

	if ([delegate respondsToSelector: selector]) {
		OFTimer *timer = [OFTimer
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
		[[OFRunLoop currentRunLoop] addTimer: timer
					     forMode: runLoopMode];
	}
}

@implementation OFHostAddressResolver: OFObject
- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (of_socket_address_family_t)addressFamily
		    resolver: (OFDNSResolver *)resolver
		    settings: (OFDNSResolverSettings *)settings
		 runLoopMode: (of_run_loop_mode_t)runLoopMode
		    delegate: (id <OFDNSResolverHostDelegate>)delegate
{
	self = [super init];

	@try {
		_host = [host copy];
		_addressFamily = addressFamily;







|


|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
		[[OFRunLoop currentRunLoop] addTimer: timer
					     forMode: runLoopMode];
	}
}

@implementation OFHostAddressResolver: OFObject
- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (OFSocketAddressFamily)addressFamily
		    resolver: (OFDNSResolver *)resolver
		    settings: (OFDNSResolverSettings *)settings
		 runLoopMode: (OFRunLoopMode)runLoopMode
		    delegate: (id <OFDNSResolverHostDelegate>)delegate
{
	self = [super init];

	@try {
		_host = [host copy];
		_addressFamily = addressFamily;
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

		domainName = [OFString stringWithFormat: @"%@.%@",
							 _host, searchDomain];
	} else
		domainName = _host;

#ifdef OF_HAVE_IPV6
	if (_addressFamily == OF_SOCKET_ADDRESS_FAMILY_IPV6 ||
	    _addressFamily == OF_SOCKET_ADDRESS_FAMILY_ANY) {
		OFDNSQuery *query = [OFDNSQuery
		    queryWithDomainName: domainName
			       DNSClass: OF_DNS_CLASS_IN
			     recordType: OF_DNS_RECORD_TYPE_AAAA];
		_numExpectedResponses++;
		[_resolver asyncPerformQuery: query
				 runLoopMode: _runLoopMode
				    delegate: self];
	}
#endif

	if (_addressFamily == OF_SOCKET_ADDRESS_FAMILY_IPV4 ||
	    _addressFamily == OF_SOCKET_ADDRESS_FAMILY_ANY) {
		OFDNSQuery *query = [OFDNSQuery
		    queryWithDomainName: domainName
			       DNSClass: OF_DNS_CLASS_IN
			     recordType: OF_DNS_RECORD_TYPE_A];
		_numExpectedResponses++;
		[_resolver asyncPerformQuery: query
				 runLoopMode: _runLoopMode
				    delegate: self];
	}
}

-  (void)resolver: (OFDNSResolver *)resolver
  didPerformQuery: (OFDNSQuery *)query
	 response: (OFDNSResponse *)response
	exception: (id)exception
{
	_numExpectedResponses--;

	if ([exception isKindOfClass: [OFDNSQueryFailedException class]] &&
	    [exception error] == OF_DNS_RESOLVER_ERROR_SERVER_NAME_ERROR &&
	    !_isFQDN && _numExpectedResponses == 0 && _addresses.count == 0 &&
	    _searchDomainIndex + 1 < _settings->_searchDomains.count) {
		_searchDomainIndex++;
		[self sendQueries];
		return;
	}

	for (OF_KINDOF(OFDNSResourceRecord *) record in
	    [response.answerRecords objectForKey: query.domainName]) {
		const of_socket_address_t *address = NULL;
		OFDNSQuery *CNAMEQuery;

		if ([record DNSClass] != OF_DNS_CLASS_IN)
			continue;

		if (addressForRecord(record, &address, _addressFamily)) {
			[_addresses addItem: address];
			continue;
		}

		if ([record recordType] != OF_DNS_RECORD_TYPE_CNAME)
			continue;

		/* FIXME: Check if it's already in answers */
		CNAMEQuery = [OFDNSQuery queryWithDomainName: [record alias]
						    DNSClass: OF_DNS_CLASS_IN
						  recordType: query.recordType];
		_numExpectedResponses++;
		[_resolver asyncPerformQuery: CNAMEQuery
				 runLoopMode: _runLoopMode
				    delegate: self];
	}








|
|


|
|







|
|


|
|















|









|


|







|




|







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

		domainName = [OFString stringWithFormat: @"%@.%@",
							 _host, searchDomain];
	} else
		domainName = _host;

#ifdef OF_HAVE_IPV6
	if (_addressFamily == OFSocketAddressFamilyIPv6 ||
	    _addressFamily == OFSocketAddressFamilyAny) {
		OFDNSQuery *query = [OFDNSQuery
		    queryWithDomainName: domainName
			       DNSClass: OFDNSClassIN
			     recordType: OFDNSRecordTypeAAAA];
		_numExpectedResponses++;
		[_resolver asyncPerformQuery: query
				 runLoopMode: _runLoopMode
				    delegate: self];
	}
#endif

	if (_addressFamily == OFSocketAddressFamilyIPv4 ||
	    _addressFamily == OFSocketAddressFamilyAny) {
		OFDNSQuery *query = [OFDNSQuery
		    queryWithDomainName: domainName
			       DNSClass: OFDNSClassIN
			     recordType: OFDNSRecordTypeA];
		_numExpectedResponses++;
		[_resolver asyncPerformQuery: query
				 runLoopMode: _runLoopMode
				    delegate: self];
	}
}

-  (void)resolver: (OFDNSResolver *)resolver
  didPerformQuery: (OFDNSQuery *)query
	 response: (OFDNSResponse *)response
	exception: (id)exception
{
	_numExpectedResponses--;

	if ([exception isKindOfClass: [OFDNSQueryFailedException class]] &&
	    [exception errorCode] == OFDNSResolverErrorCodeServerNameError &&
	    !_isFQDN && _numExpectedResponses == 0 && _addresses.count == 0 &&
	    _searchDomainIndex + 1 < _settings->_searchDomains.count) {
		_searchDomainIndex++;
		[self sendQueries];
		return;
	}

	for (OF_KINDOF(OFDNSResourceRecord *) record in
	    [response.answerRecords objectForKey: query.domainName]) {
		const OFSocketAddress *address = NULL;
		OFDNSQuery *CNAMEQuery;

		if ([record DNSClass] != OFDNSClassIN)
			continue;

		if (addressForRecord(record, &address, _addressFamily)) {
			[_addresses addItem: address];
			continue;
		}

		if ([record recordType] != OFDNSRecordTypeCNAME)
			continue;

		/* FIXME: Check if it's already in answers */
		CNAMEQuery = [OFDNSQuery queryWithDomainName: [record alias]
						    DNSClass: OFDNSClassIN
						  recordType: query.recordType];
		_numExpectedResponses++;
		[_resolver asyncPerformQuery: CNAMEQuery
				 runLoopMode: _runLoopMode
				    delegate: self];
	}

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
		_addresses = nil;

		if ([exception isKindOfClass:
		    [OFDNSQueryFailedException class]])
			exception = [OFResolveHostFailedException
			    exceptionWithHost: _host
				addressFamily: _addressFamily
					error: [exception error]];

		if (exception == nil)
			exception = [OFResolveHostFailedException
			    exceptionWithHost: _host
				addressFamily: _addressFamily
					error: OF_DNS_RESOLVER_ERROR_NO_RESULT];
	} else
		exception = nil;

	if ([_delegate respondsToSelector:
	    @selector(resolver:didResolveHost:addresses:exception:)])
		[_delegate resolver: _resolver
		     didResolveHost: _host
			  addresses: _addresses
			  exception: exception];
}

- (void)asyncResolve
{
	void *pool = objc_autoreleasePoolPush();
	OFArray OF_GENERIC(OFString *) *aliases;

	@try {
		of_socket_address_t address =
		    of_socket_address_parse_ip(_host, 0);
		OFData *addresses = nil;
		id exception = nil;

		if (_addressFamily == address.family ||
		    _addressFamily == OF_SOCKET_ADDRESS_FAMILY_ANY)
			addresses = [OFData dataWithItems: &address

						 itemSize: sizeof(address)
						    count: 1];
		else
			exception = [OFInvalidArgumentException exception];

		callDelegateInMode(_runLoopMode, _delegate, _resolver, _host,
		    addresses, exception);

		objc_autoreleasePoolPop(pool);
		return;
	} @catch (OFInvalidFormatException *e) {
	}

	if ((aliases = [_settings->_staticHosts objectForKey: _host]) != nil) {
		OFMutableData *addresses = [OFMutableData
		    dataWithItemSize: sizeof(of_socket_address_t)];
		id exception = nil;

		for (OFString *alias in aliases) {
			of_socket_address_t address;

			@try {
				address = of_socket_address_parse_ip(alias, 0);
			} @catch (OFInvalidFormatException *e) {
				continue;
			}

			if (_addressFamily != address.family &&
			    _addressFamily != OF_SOCKET_ADDRESS_FAMILY_ANY)
				continue;

			[addresses addItem: &address];
		}

		[addresses makeImmutable];

		if (addresses.count == 0) {
			addresses = nil;
			exception = [OFResolveHostFailedException
			    exceptionWithHost: _host
				addressFamily: _addressFamily
					error: OF_DNS_RESOLVER_ERROR_NO_RESULT];
		}

		callDelegateInMode(_runLoopMode, _delegate, _resolver, _host,
		    addresses, exception);

		objc_autoreleasePoolPop(pool);
		return;
	}

	_isFQDN = isFQDN(_host, _settings->_minNumberOfDotsInAbsoluteName);
	_addresses = [[OFMutableData alloc]
	    initWithItemSize: sizeof(of_socket_address_t)];

	[self sendQueries];

	objc_autoreleasePoolPop(pool);
}

- (OFData *)resolve
{
	void *pool = objc_autoreleasePoolPush();
	OFRunLoop *runLoop = [OFRunLoop currentRunLoop];
	OFHostAddressResolverDelegate *delegate;
	OFData *ret;

	delegate = [[[OFHostAddressResolverDelegate alloc] init] autorelease];
	_runLoopMode = [resolveRunLoopMode copy];
	_delegate = [delegate retain];

	[self asyncResolve];

	while (!delegate->_done)
		[runLoop runMode: resolveRunLoopMode
		      beforeDate: nil];

	/* Cleanup */
	[runLoop runMode: resolveRunLoopMode
	      beforeDate: [OFDate date]];

	if (delegate->_exception != nil)
		@throw delegate->_exception;

	ret = [delegate->_addresses copy];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

@implementation OFHostAddressResolverDelegate
- (void)dealloc
{







|





|

















|
<




|

>
|
<













|



|


|





|












|











|




















|
<


|
<





<

<







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
		_addresses = nil;

		if ([exception isKindOfClass:
		    [OFDNSQueryFailedException class]])
			exception = [OFResolveHostFailedException
			    exceptionWithHost: _host
				addressFamily: _addressFamily
				    errorCode: [exception errorCode]];

		if (exception == nil)
			exception = [OFResolveHostFailedException
			    exceptionWithHost: _host
				addressFamily: _addressFamily
				    errorCode: OFDNSResolverErrorCodeNoResult];
	} else
		exception = nil;

	if ([_delegate respondsToSelector:
	    @selector(resolver:didResolveHost:addresses:exception:)])
		[_delegate resolver: _resolver
		     didResolveHost: _host
			  addresses: _addresses
			  exception: exception];
}

- (void)asyncResolve
{
	void *pool = objc_autoreleasePoolPush();
	OFArray OF_GENERIC(OFString *) *aliases;

	@try {
		OFSocketAddress address = OFSocketAddressParseIP(_host, 0);

		OFData *addresses = nil;
		id exception = nil;

		if (_addressFamily == address.family ||
		    _addressFamily == OFSocketAddressFamilyAny)
			addresses = [OFData dataWithItems: &address
						    count: 1
						 itemSize: sizeof(address)];

		else
			exception = [OFInvalidArgumentException exception];

		callDelegateInMode(_runLoopMode, _delegate, _resolver, _host,
		    addresses, exception);

		objc_autoreleasePoolPop(pool);
		return;
	} @catch (OFInvalidFormatException *e) {
	}

	if ((aliases = [_settings->_staticHosts objectForKey: _host]) != nil) {
		OFMutableData *addresses = [OFMutableData
		    dataWithItemSize: sizeof(OFSocketAddress)];
		id exception = nil;

		for (OFString *alias in aliases) {
			OFSocketAddress address;

			@try {
				address = OFSocketAddressParseIP(alias, 0);
			} @catch (OFInvalidFormatException *e) {
				continue;
			}

			if (_addressFamily != address.family &&
			    _addressFamily != OFSocketAddressFamilyAny)
				continue;

			[addresses addItem: &address];
		}

		[addresses makeImmutable];

		if (addresses.count == 0) {
			addresses = nil;
			exception = [OFResolveHostFailedException
			    exceptionWithHost: _host
				addressFamily: _addressFamily
				    errorCode: OFDNSResolverErrorCodeNoResult];
		}

		callDelegateInMode(_runLoopMode, _delegate, _resolver, _host,
		    addresses, exception);

		objc_autoreleasePoolPop(pool);
		return;
	}

	_isFQDN = isFQDN(_host, _settings->_minNumberOfDotsInAbsoluteName);
	_addresses = [[OFMutableData alloc]
	    initWithItemSize: sizeof(OFSocketAddress)];

	[self sendQueries];

	objc_autoreleasePoolPop(pool);
}

- (OFData *)resolve
{
	void *pool = objc_autoreleasePoolPush();
	OFRunLoop *runLoop = [OFRunLoop currentRunLoop];
	OFHostAddressResolverDelegate *delegate;
	OFData *ret;

	delegate = [[[OFHostAddressResolverDelegate alloc] init] autorelease];
	_runLoopMode = [resolveRunLoopMode copy];
	_delegate = [delegate retain];

	[self asyncResolve];

	while (!delegate->_done)
		[runLoop runMode: resolveRunLoopMode beforeDate: nil];


	/* Cleanup */
	[runLoop runMode: resolveRunLoopMode beforeDate: [OFDate date]];


	if (delegate->_exception != nil)
		@throw delegate->_exception;

	ret = [delegate->_addresses copy];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

@implementation OFHostAddressResolverDelegate
- (void)dealloc
{

Renamed and modified src/huffman_tree.h [bf284ca06e] to src/OFHuffmanTree.h [0152a399af].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#import "macros.h"

#import "OFInvalidFormatException.h"

OF_ASSUME_NONNULL_BEGIN

struct of_huffman_tree {
	struct of_huffman_tree *_Nullable leaves[2];
	uint16_t value;
};


static OF_INLINE bool
of_huffman_tree_walk(id _Nullable stream,
    bool (*bitReader)(id _Nullable, uint16_t *_Nonnull, uint8_t),
    struct of_huffman_tree *_Nonnull *_Nonnull tree, uint16_t *_Nonnull value)
{
	struct of_huffman_tree *iter = *tree;
	uint16_t bits;

	while (iter->value == 0xFFFF) {
		if OF_UNLIKELY (!bitReader(stream, &bits, 1)) {
			*tree = iter;
			return false;
		}







|
|

|

>

|

|

|







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

#import "macros.h"

#import "OFInvalidFormatException.h"

OF_ASSUME_NONNULL_BEGIN

typedef struct _OFHuffmanTree {
	struct _OFHuffmanTree *_Nullable leaves[2];
	uint16_t value;
} *OFHuffmanTree;

/* Inlined for performance. */
static OF_INLINE bool
OFHuffmanTreeWalk(id _Nullable stream,
    bool (*bitReader)(id _Nullable, uint16_t *_Nonnull, uint8_t),
    OFHuffmanTree _Nonnull *_Nonnull tree, uint16_t *_Nonnull value)
{
	OFHuffmanTree iter = *tree;
	uint16_t bits;

	while (iter->value == 0xFFFF) {
		if OF_UNLIKELY (!bitReader(stream, &bits, 1)) {
			*tree = iter;
			return false;
		}
52
53
54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
	*value = iter->value;
	return true;
}

#ifdef __cplusplus
extern "C" {
#endif
extern struct of_huffman_tree *_Nonnull of_huffman_tree_construct(
    uint8_t lengths[_Nonnull], uint16_t count);
extern struct of_huffman_tree *_Nonnull of_huffman_tree_construct_single(
    uint16_t value);

extern void of_huffman_tree_release(struct of_huffman_tree *_Nonnull tree);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







<
|
<
|
>
|





51
52
53
54
55
56
57

58

59
60
61
62
63
64
65
66
	*value = iter->value;
	return true;
}

#ifdef __cplusplus
extern "C" {
#endif

extern OFHuffmanTree _Nonnull OFHuffmanTreeNew(uint8_t lengths[_Nonnull],

    uint16_t count);
extern OFHuffmanTree _Nonnull OFHuffmanTreeNewSingle(uint16_t value);
extern void OFHuffmanTreeFree(OFHuffmanTree _Nonnull tree);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Renamed and modified src/huffman_tree.m [c3ebe65312] to src/OFHuffmanTree.m [13f0a946b0].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdint.h>
#include <stdlib.h>

#import "huffman_tree.h"

#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"

static struct of_huffman_tree *
newTree(void)
{
	struct of_huffman_tree *tree;

	tree = of_malloc(1, sizeof(*tree));
	tree->leaves[0] = tree->leaves[1] = NULL;
	tree->value = 0xFFFF;

	return tree;
}

static void
insertTree(struct of_huffman_tree *tree, uint16_t code, uint8_t length,
    uint16_t value)
{
	while (length > 0) {
		uint8_t bit;

		length--;
		bit = (code & (1u << length)) >> length;

		if (tree->leaves[bit] == NULL)
			tree->leaves[bit] = newTree();

		tree = tree->leaves[bit];
	}

	tree->value = value;
}

struct of_huffman_tree *
of_huffman_tree_construct(uint8_t lengths[], uint16_t count)
{
	struct of_huffman_tree *tree;
	uint16_t *lengthCount = NULL;
	uint16_t code, maxCode = 0, *nextCode = NULL;
	uint_fast8_t maxBit = 0;

	@try {
		for (uint16_t i = 0; i < count; i++) {
			uint_fast8_t length = lengths[i];

			if OF_UNLIKELY (length > maxBit) {
				lengthCount = of_realloc(lengthCount,
				    length + 1, sizeof(uint16_t));
				nextCode = of_realloc(nextCode,
				    length + 1, sizeof(uint16_t));

				for (uint_fast8_t j = maxBit + 1; j <= length;
				    j++) {
					lengthCount[j] = 0;
					nextCode[j] = 0;
				}

<
<
|


















|




|


|

|







|
<
















|
|

|









|

|







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


 * 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 <stdint.h>
#include <stdlib.h>

#import "OFHuffmanTree.h"

#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"

static OFHuffmanTree
newTree(void)
{
	OFHuffmanTree tree;

	tree = OFAllocMemory(1, sizeof(*tree));
	tree->leaves[0] = tree->leaves[1] = NULL;
	tree->value = 0xFFFF;

	return tree;
}

static void
treeInsert(OFHuffmanTree tree, uint16_t code, uint8_t length, uint16_t value)

{
	while (length > 0) {
		uint8_t bit;

		length--;
		bit = (code & (1u << length)) >> length;

		if (tree->leaves[bit] == NULL)
			tree->leaves[bit] = newTree();

		tree = tree->leaves[bit];
	}

	tree->value = value;
}

OFHuffmanTree
OFHuffmanTreeNew(uint8_t lengths[], uint16_t count)
{
	OFHuffmanTree tree;
	uint16_t *lengthCount = NULL;
	uint16_t code, maxCode = 0, *nextCode = NULL;
	uint_fast8_t maxBit = 0;

	@try {
		for (uint16_t i = 0; i < count; i++) {
			uint_fast8_t length = lengths[i];

			if OF_UNLIKELY (length > maxBit) {
				lengthCount = OFResizeMemory(lengthCount,
				    length + 1, sizeof(uint16_t));
				nextCode = OFResizeMemory(nextCode,
				    length + 1, sizeof(uint16_t));

				for (uint_fast8_t j = maxBit + 1; j <= length;
				    j++) {
					lengthCount[j] = 0;
					nextCode[j] = 0;
				}
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

		tree = newTree();

		for (uint16_t i = 0; i <= maxCode; i++) {
			uint8_t length = lengths[i];

			if (length > 0)
				insertTree(tree, nextCode[length]++, length, i);
		}
	} @finally {
		of_free(lengthCount);
		of_free(nextCode);
	}

	return tree;
}

struct of_huffman_tree *
of_huffman_tree_construct_single(uint16_t value)
{
	struct of_huffman_tree *tree = newTree();

	tree->value = value;

	return tree;
}

void
of_huffman_tree_release(struct of_huffman_tree *tree)
{
	for (uint_fast8_t i = 0; i < 2; i++)
		if OF_LIKELY (tree->leaves[i] != NULL)
			of_huffman_tree_release(tree->leaves[i]);

	of_free(tree);
}







|


|
|





|
|

|







|



|

|

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

		tree = newTree();

		for (uint16_t i = 0; i <= maxCode; i++) {
			uint8_t length = lengths[i];

			if (length > 0)
				treeInsert(tree, nextCode[length]++, length, i);
		}
	} @finally {
		OFFreeMemory(lengthCount);
		OFFreeMemory(nextCode);
	}

	return tree;
}

OFHuffmanTree
OFHuffmanTreeNewSingle(uint16_t value)
{
	OFHuffmanTree tree = newTree();

	tree->value = value;

	return tree;
}

void
OFHuffmanTreeFree(OFHuffmanTree tree)
{
	for (uint_fast8_t i = 0; i < 2; i++)
		if OF_LIKELY (tree->leaves[i] != NULL)
			OFHuffmanTreeFree(tree->leaves[i]);

	OFFreeMemory(tree);
}

Modified src/OFINICategory+Private.h from [daec4f9da8] to [6b757dc025].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
23
24
25
26
27
28
29
30
31
32
33
34
@class OFStream;

OF_DIRECT_MEMBERS
@interface OFINICategory ()
- (instancetype)of_initWithName: (OFString *)name OF_METHOD_FAMILY(init);
- (void)of_parseLine: (OFString *)line;
- (bool)of_writeToStream: (OFStream *)stream
		encoding: (of_string_encoding_t)encoding
		   first: (bool)first;
@end

OF_ASSUME_NONNULL_END







|




21
22
23
24
25
26
27
28
29
30
31
32
@class OFStream;

OF_DIRECT_MEMBERS
@interface OFINICategory ()
- (instancetype)of_initWithName: (OFString *)name OF_METHOD_FAMILY(init);
- (void)of_parseLine: (OFString *)line;
- (bool)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding
		   first: (bool)first;
@end

OF_ASSUME_NONNULL_END

Modified src/OFINICategory.h from [0c961c5344] to [e194c2c9e1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 * @brief The name of the INI category
 */
@property (copy, nonatomic) OFString *name;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Returns the string value for the specified key, or `nil` if it does
 *	  not exist.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is returned.
 *
 * @param key The key for which the string value should be returned
 * @return The string value for the specified key, or `nil` if it does not exist
 */
- (nullable OFString *)stringForKey: (OFString *)key;

/**
 * @brief Returns the string value for the specified key or the specified
 *	  default value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is returned.
 *
 * @param key The key for which the string value should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The string value for the specified key or the specified default
 *	   value if it does not exist
 */
- (nullable OFString *)stringForKey: (OFString *)key
		       defaultValue: (nullable OFString *)defaultValue;

/**
 * @brief Returns the integer value for the specified key or the specified
 *	  default value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is returned.
 *
 * @param key The key for which the integer value should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The integer value for the specified key or the specified default
 *	   value if it does not exist
 */
- (long long)integerForKey: (OFString *)key
	      defaultValue: (long long)defaultValue;

/**
 * @brief Returns the bool value for the specified key or the specified default
 *	  value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is returned.
 *
 * @param key The key for which the bool value should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The bool value 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 value for the specified key or the specified
 *	  default value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is returned.
 *
 * @param key The key for which the float value should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The float value 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 value for the specified key or the specified
 *	  default value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is returned.
 *
 * @param key The key for which the double value should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The double value 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 string values for the specified multi-key, or an
 *	  empty array if the key does not exist.
 *
 * A multi-key is a key which exists several times in the same category. Each
 * occurrence of the key/value pair adds the respective value to the array.
 *
 * @param key The multi-key for which the array should be returned
 * @return The array for the specified key, or an empty array if it does not
 *	   exist
 */
- (OFArray OF_GENERIC(OFString *) *)arrayForKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified string.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is changed.
 *
 * @param string The string to which the value of the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setString: (OFString *)string
	   forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified integer.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is changed.
 *
 * @param integer The integer to which the value of the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setInteger: (long long)integer
	    forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified bool.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is changed.
 *
 * @param bool_ The bool to which the value of the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setBool: (bool)bool_
	 forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified float.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is changed.
 *
 * @param float_ The float to which the value of the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setFloat: (float)float_
	  forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified double.
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), the value of
 * the first key/value pair found is changed.
 *
 * @param double_ The double to which the value of the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setDouble: (double)double_
	   forKey: (OFString *)key;

/**
 * @brief Sets the specified multi-key to the specified array of strings.
 *
 * It replaces the first occurrence of the multi-key with several key/value
 * pairs and removes all following occurrences. If the multi-key does not exist
 * yet, it is appended to the section.
 *
 * See also @ref arrayForKey: for more information about multi-keys.
 *
 * @param array The array of strings to which the multi-key should be set
 * @param key The multi-key for which the new values should be set
 */
- (void)setArray: (OFArray OF_GENERIC(OFString *) *)array
	  forKey: (OFString *)key;

/**
 * @brief Removes the value for the specified key
 *
 * If the specified key is a multi-key (see @ref arrayForKey:), all key/value
 * pairs matching the specified key are removed.
 *
 * @param key The key of the value to remove
 */
- (void)removeValueForKey: (OFString *)key;
@end

OF_ASSUME_NONNULL_END







|
|

|
|

|
|




|
|

|
|

|

|
|





|
|

|
|

|

|
|

|
|


|
|

|
|

|

|
|

|
<


|
|

|
|

|

|
|

|
<


|
|

|
|

|

|
|

|
<


|
|








|




|
|

|


|
<


|

|
|

|


<
|




|
|

|


|
<




|
|

|


|
<




|
|

|


|
<








|




|
|




|
|







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
 * @brief The name of the INI category
 */
@property (copy, nonatomic) OFString *name;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Returns the string for the specified key, or `nil` 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 string should be returned
 * @return The string for the specified key, or `nil` if it does not exist
 */
- (nullable OFString *)stringForKey: (OFString *)key;

/**
 * @brief Returns the string 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 string should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The string for the specified key or the specified default value if
 *	   it does not exist
 */
- (nullable OFString *)stringForKey: (OFString *)key
		       defaultValue: (nullable OFString *)defaultValue;

/**
 * @brief Returns the long long 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 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.
 *
 * A multi-key is a key which exists several times in the same category. Each
 * occurrence of the key/value pair adds the respective value to the array.
 *
 * @param key The multi-key for which the array should be returned
 * @return The array for the specified key, or an empty array if it does not
 *	   exist
 */
- (OFArray OF_GENERIC(OFString *) *)stringArrayForKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified string.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param string The string to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setString: (OFString *)string forKey: (OFString *)key;


/**
 * @brief Sets the value of the specified key to the specified long long.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param longLong The long long to which the key should be set
 * @param key The key for which the new value should be set
 */

- (void)setLongLong: (long long)longLong forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified bool.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param bool_ The bool to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setBool: (bool)bool_ forKey: (OFString *)key;


/**
 * @brief Sets the value of the specified key to the specified float.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param float_ The float to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setFloat: (float)float_ forKey: (OFString *)key;


/**
 * @brief Sets the value of the specified key to the specified double.
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param double_ The double to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setDouble: (double)double_ forKey: (OFString *)key;


/**
 * @brief Sets the specified multi-key to the specified array of strings.
 *
 * It replaces the first occurrence of the multi-key with several key/value
 * pairs and removes all following occurrences. If the multi-key does not exist
 * yet, it is appended to the section.
 *
 * See also @ref stringArrayForKey: for more information about multi-keys.
 *
 * @param array The array of strings to which the multi-key should be set
 * @param key The multi-key for which the new values should be set
 */
- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array
		forKey: (OFString *)key;

/**
 * @brief Removes the value for the specified key
 *
 * If the specified key is a multi-key (see @ref stringArrayForKey:), all
 * key/value pairs matching the specified key are removed.
 *
 * @param key The key of the value to remove
 */
- (void)removeValueForKey: (OFString *)key;
@end

OF_ASSUME_NONNULL_END

Modified src/OFINICategory.m from [ee9139e5e6] to [0938799665].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	    ![string hasPrefix: @"\f"] && ![string hasSuffix: @" "] &&
	    ![string hasSuffix: @"\t"] && ![string hasSuffix: @"\f"] &&
	    ![string containsString: @"\""])
		return string;

	mutableString = [[string mutableCopy] autorelease];

	[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: of_range(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: @"\\"];

	[mutableString makeImmutable];

	return mutableString;
}

@implementation OFINICategoryPair
- (void)dealloc
{
	[_key release];
	[_value release];

	[super dealloc];
}





@end

@implementation OFINICategoryComment
- (void)dealloc
{
	[_comment release];

	[super dealloc];
}





@end

@implementation OFINICategory
@synthesize name = _name;

- (instancetype)of_initWithName: (OFString *)name OF_DIRECT
{







|
<
|
<
|
<
|
<
|
<

















|


|
<
|
<
|
<
|
<
|
<














>
>
>
>
>









>
>
>
>
>







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
	    ![string hasPrefix: @"\f"] && ![string hasSuffix: @" "] &&
	    ![string hasSuffix: @"\t"] && ![string hasSuffix: @"\f"] &&
	    ![string containsString: @"\""])
		return string;

	mutableString = [[string mutableCopy] autorelease];

	[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: @"\\"];


	[mutableString makeImmutable];

	return mutableString;
}

@implementation OFINICategoryPair
- (void)dealloc
{
	[_key release];
	[_value release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"%@ = %@", _key, _value];
}
@end

@implementation OFINICategoryComment
- (void)dealloc
{
	[_comment release];

	[super dealloc];
}

- (OFString *)description
{
	return [[_comment copy] autorelease];
}
@end

@implementation OFINICategory
@synthesize name = _name;

- (instancetype)of_initWithName: (OFString *)name OF_DIRECT
{
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
{
	if (![line hasPrefix: @";"]) {
		OFINICategoryPair *pair =
		    [[[OFINICategoryPair alloc] init] autorelease];
		OFString *key, *value;
		size_t pos;

		if ((pos = [line rangeOfString: @"="].location) == OF_NOT_FOUND)
			@throw [OFInvalidFormatException exception];

		key = unescapeString([line substringToIndex: pos]
		    .stringByDeletingEnclosingWhitespaces);
		value = unescapeString([line substringFromIndex: pos + 1]
		    .stringByDeletingEnclosingWhitespaces);








|







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
{
	if (![line hasPrefix: @";"]) {
		OFINICategoryPair *pair =
		    [[[OFINICategoryPair alloc] init] autorelease];
		OFString *key, *value;
		size_t pos;

		if ((pos = [line rangeOfString: @"="].location) == OFNotFound)
			@throw [OFInvalidFormatException exception];

		key = unescapeString([line substringToIndex: pos]
		    .stringByDeletingEnclosingWhitespaces);
		value = unescapeString([line substringFromIndex: pos + 1]
		    .stringByDeletingEnclosingWhitespaces);

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

		[_lines addObject: comment];
	}
}

- (OFString *)stringForKey: (OFString *)key
{
	return [self stringForKey: key
		     defaultValue: nil];
}

- (OFString *)stringForKey: (OFString *)key
	      defaultValue: (OFString *)defaultValue
{
	for (id line in _lines) {
		OFINICategoryPair *pair;

		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key])
			return [[pair->_value copy] autorelease];
	}

	return defaultValue;
}

- (long long)integerForKey: (OFString *)key
	      defaultValue: (long long)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringForKey: key
				defaultValue: nil];
	long long ret;

	if (value != nil)
		ret = [value longLongValueWithBase: 0];
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)boolForKey: (OFString *)key
      defaultValue: (bool)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringForKey: key
				defaultValue: nil];
	bool ret;

	if (value != nil) {
		if ([value isEqual: @"true"])
			ret = true;
		else if ([value isEqual: @"false"])
			ret = false;
		else
			@throw [OFInvalidFormatException exception];
	} else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (float)floatForKey: (OFString *)key
	defaultValue: (float)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringForKey: key
				defaultValue: nil];
	float ret;

	if (value != nil)
		ret = value.floatValue;
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (double)doubleForKey: (OFString *)key
	  defaultValue: (double)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringForKey: key
				defaultValue: nil];
	double ret;

	if (value != nil)
		ret = value.doubleValue;
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFArray *)arrayForKey: (OFString *)key
{
	OFMutableArray *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();

	for (id line in _lines) {
		OFINICategoryPair *pair;








|
<




















|
|


|
<












|
<


|
<

















|
<


|
<












|
<


|
<












|







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

		[_lines addObject: comment];
	}
}

- (OFString *)stringForKey: (OFString *)key
{
	return [self stringForKey: key defaultValue: nil];

}

- (OFString *)stringForKey: (OFString *)key
	      defaultValue: (OFString *)defaultValue
{
	for (id line in _lines) {
		OFINICategoryPair *pair;

		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key])
			return [[pair->_value copy] autorelease];
	}

	return defaultValue;
}

- (long long)longLongForKey: (OFString *)key
	       defaultValue: (long long)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringForKey: key defaultValue: nil];

	long long ret;

	if (value != nil)
		ret = [value longLongValueWithBase: 0];
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)boolForKey: (OFString *)key defaultValue: (bool)defaultValue

{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringForKey: key defaultValue: nil];

	bool ret;

	if (value != nil) {
		if ([value isEqual: @"true"])
			ret = true;
		else if ([value isEqual: @"false"])
			ret = false;
		else
			@throw [OFInvalidFormatException exception];
	} else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (float)floatForKey: (OFString *)key defaultValue: (float)defaultValue

{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringForKey: key defaultValue: nil];

	float ret;

	if (value != nil)
		ret = value.floatValue;
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (double)doubleForKey: (OFString *)key defaultValue: (double)defaultValue

{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringForKey: key defaultValue: nil];

	double ret;

	if (value != nil)
		ret = value.doubleValue;
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFArray OF_GENERIC(OFString *) *)stringArrayForKey: (OFString *)key
{
	OFMutableArray *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();

	for (id line in _lines) {
		OFINICategoryPair *pair;

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}

- (void)setString: (OFString *)string
	   forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();
	OFINICategoryPair *pair;

	for (id line in _lines) {
		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;







|
<







291
292
293
294
295
296
297
298

299
300
301
302
303
304
305
	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}

- (void)setString: (OFString *)string forKey: (OFString *)key

{
	void *pool = objc_autoreleasePoolPush();
	OFINICategoryPair *pair;

	for (id line in _lines) {
		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;
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

		@throw e;
	}

	objc_autoreleasePoolPop(pool);
}

- (void)setInteger: (long long)integer
	    forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();

	[self setString: [OFString stringWithFormat: @"%lld", integer]
		 forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setBool: (bool)bool_
	 forKey: (OFString *)key
{
	[self setString: (bool_ ? @"true" : @"false")
		 forKey: key];
}

- (void)setFloat: (float)float_
	  forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();

	[self setString: [OFString stringWithFormat: @"%g", float_]
		 forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setDouble: (double)double_
	   forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();

	[self setString: [OFString stringWithFormat: @"%g", double_]
		 forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setArray: (OFArray *)array
	  forKey: (OFString *)key
{
	void *pool;
	OFMutableArray *pairs;
	id const *lines;
	size_t count;
	bool replaced;

	if (array.count == 0) {
		[self removeValueForKey: key];
		return;
	}

	pool = objc_autoreleasePoolPush();

	pairs = [OFMutableArray arrayWithCapacity: array.count];

	for (id object in array) {
		OFINICategoryPair *pair;

		if (![object isKindOfClass: [OFString class]])
			@throw [OFInvalidArgumentException exception];

		pair = [[[OFINICategoryPair alloc] init] autorelease];
		pair->_key = [key copy];
		pair->_value = [object copy];

		[pairs addObject: pair];
	}

	lines = _lines.objects;
	count = _lines.count;
	replaced = false;







<
|



|





|
<

|
<


|
<









|
<









|
|
















|


|




|







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

		@throw e;
	}

	objc_autoreleasePoolPop(pool);
}


- (void)setLongLong: (long long)longLong forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();

	[self setString: [OFString stringWithFormat: @"%lld", longLong]
		 forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setBool: (bool)bool_ forKey: (OFString *)key

{
	[self setString: (bool_ ? @"true" : @"false") forKey: key];

}

- (void)setFloat: (float)float_ forKey: (OFString *)key

{
	void *pool = objc_autoreleasePoolPush();

	[self setString: [OFString stringWithFormat: @"%g", float_]
		 forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setDouble: (double)double_ forKey: (OFString *)key

{
	void *pool = objc_autoreleasePoolPush();

	[self setString: [OFString stringWithFormat: @"%g", double_]
		 forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array
		forKey: (OFString *)key
{
	void *pool;
	OFMutableArray *pairs;
	id const *lines;
	size_t count;
	bool replaced;

	if (array.count == 0) {
		[self removeValueForKey: key];
		return;
	}

	pool = objc_autoreleasePoolPush();

	pairs = [OFMutableArray arrayWithCapacity: array.count];

	for (OFString *string in array) {
		OFINICategoryPair *pair;

		if (![string isKindOfClass: [OFString class]])
			@throw [OFInvalidArgumentException exception];

		pair = [[[OFINICategoryPair alloc] init] autorelease];
		pair->_key = [key copy];
		pair->_value = [string copy];

		[pairs addObject: pair];
	}

	lines = _lines.objects;
	count = _lines.count;
	replaced = false;
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
		}
	}

	objc_autoreleasePoolPop(pool);
}

- (bool)of_writeToStream: (OFStream *)stream
		encoding: (of_string_encoding_t)encoding
		   first: (bool)first
{
	if (_lines.count == 0)
		return false;

	if (first)
		[stream writeFormat: @"[%@]\r\n", _name];
	else
		[stream writeFormat: @"\r\n[%@]\r\n", _name];

	for (id line in _lines) {
		if ([line isKindOfClass: [OFINICategoryComment class]]) {
			OFINICategoryComment *comment = line;
			[stream writeFormat: @"%@\r\n", comment->_comment];
		} else if ([line isKindOfClass: [OFINICategoryPair class]]) {
			OFINICategoryPair *pair = line;
			OFString *key = escapeString(pair->_key);
			OFString *value = escapeString(pair->_value);
			OFString *tmp = [OFString
			    stringWithFormat: @"%@=%@\r\n", key, value];

			[stream writeString: tmp
				   encoding: encoding];
		} else
			@throw [OFInvalidArgumentException exception];
	}

	return true;
}






@end







|




















<
|
<






>
>
>
>
>
>

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

	objc_autoreleasePoolPop(pool);
}

- (bool)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding
		   first: (bool)first
{
	if (_lines.count == 0)
		return false;

	if (first)
		[stream writeFormat: @"[%@]\r\n", _name];
	else
		[stream writeFormat: @"\r\n[%@]\r\n", _name];

	for (id line in _lines) {
		if ([line isKindOfClass: [OFINICategoryComment class]]) {
			OFINICategoryComment *comment = line;
			[stream writeFormat: @"%@\r\n", comment->_comment];
		} else if ([line isKindOfClass: [OFINICategoryPair class]]) {
			OFINICategoryPair *pair = line;
			OFString *key = escapeString(pair->_key);
			OFString *value = escapeString(pair->_value);
			OFString *tmp = [OFString
			    stringWithFormat: @"%@=%@\r\n", key, value];

			[stream writeString: tmp encoding: encoding];

		} else
			@throw [OFInvalidArgumentException exception];
	}

	return true;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@ \"%@\": %@>",
					   self.class, _name, _lines];
}
@end

Modified src/OFINIFile.h from [c3c19039f7] to [85f822e023].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 */
OF_SUBCLASSING_RESTRICTED
@interface OFINIFile: OFObject
{
	OFMutableArray OF_GENERIC(OFINICategory *) *_categories;
}






/**
 * @brief Creates a new OFINIFile with the contents of the specified file.
 *
 * @param path The path to the file whose contents the OFINIFile should contain
 *
 * @return A new, autoreleased OFINIFile with the contents of the specified file
 */
+ (instancetype)fileWithPath: (OFString *)path;

/**
 * @brief Creates a new OFINIFile with the contents of the specified file in
 *	  the specified encoding.
 *
 * @param path The path 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)fileWithPath: (OFString *)path
		    encoding: (of_string_encoding_t)encoding;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFINIFile with the contents of the
 *	  specified file.
 *







>
>
>
>
>



















|







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
 */
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 path The path to the file whose contents the OFINIFile should contain
 *
 * @return A new, autoreleased OFINIFile with the contents of the specified file
 */
+ (instancetype)fileWithPath: (OFString *)path;

/**
 * @brief Creates a new OFINIFile with the contents of the specified file in
 *	  the specified encoding.
 *
 * @param path The path 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)fileWithPath: (OFString *)path
		    encoding: (OFStringEncoding)encoding;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFINIFile with the contents of the
 *	  specified file.
 *
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
 *
 * @param path The path 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)initWithPath: (OFString *)path
		    encoding: (of_string_encoding_t)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







|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 *
 * @param path The path 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)initWithPath: (OFString *)path
		    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
101
102
103
104
105
106
107
108
109
110
111
112
/**
 * @brief Writes the contents of the OFINIFile to a file in the specified
 *	  encoding.
 *
 * @param path The path of the file to write to
 * @param encoding The encoding to use
 */
- (void)writeToFile: (OFString *)path
	   encoding: (of_string_encoding_t)encoding;
@end

OF_ASSUME_NONNULL_END







|
<



104
105
106
107
108
109
110
111

112
113
114
/**
 * @brief Writes the contents of the OFINIFile to a file in the specified
 *	  encoding.
 *
 * @param path The path of the file to write to
 * @param encoding The encoding to use
 */
- (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding;

@end

OF_ASSUME_NONNULL_END

Modified src/OFINIFile.m from [e386265069] to [f3dcfaef7e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFINICategory+Private.h"

#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"

OF_DIRECT_MEMBERS
@interface OFINIFile ()
- (void)of_parseFile: (OFString *)path
	    encoding: (of_string_encoding_t)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 (!of_ascii_isspace(cString[i]))
			return false;

	return true;
}

@implementation OFINIFile


+ (instancetype)fileWithPath: (OFString *)path
{
	return [[[self alloc] initWithPath: path] autorelease];
}

+ (instancetype)fileWithPath: (OFString *)path
		    encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithPath: path
				  encoding: encoding] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPath: (OFString *)path
{
	return [self initWithPath: path
			 encoding: OF_STRING_ENCODING_UTF_8];
}

- (instancetype)initWithPath: (OFString *)path
		    encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		_categories = [[OFMutableArray alloc] init];

		[self of_parseFile: path
			  encoding: encoding];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







|
<









|






>
>






|












|
<



|






|
<







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
#import "OFINICategory+Private.h"

#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"

OF_DIRECT_MEMBERS
@interface OFINIFile ()
- (void)of_parseFile: (OFString *)path 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)fileWithPath: (OFString *)path
{
	return [[[self alloc] initWithPath: path] autorelease];
}

+ (instancetype)fileWithPath: (OFString *)path
		    encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithPath: path
				  encoding: encoding] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPath: (OFString *)path
{
	return [self initWithPath: path encoding: OFStringEncodingUTF8];

}

- (instancetype)initWithPath: (OFString *)path
		    encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_categories = [[OFMutableArray alloc] init];

		[self of_parseFile: path encoding: encoding];

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

	return self;
}
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
	[_categories addObject: category];

	objc_autoreleasePoolPop(pool);

	return category;
}

- (void)of_parseFile: (OFString *)path
	    encoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file;
	OFINICategory *category = nil;
	OFString *line;

	@try {
		file = [OFFile fileWithPath: path
				       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:
			    of_range(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)writeToFile: (OFString *)path
{
	[self writeToFile: path
		 encoding: OF_STRING_ENCODING_UTF_8];
}

- (void)writeToFile: (OFString *)path
	   encoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file = [OFFile fileWithPath: path
				       mode: @"w"];
	bool first = true;

	for (OFINICategory *category in _categories)
		if ([category of_writeToStream: file
				      encoding: encoding
					 first: first])
			first = false;

	objc_autoreleasePoolPop(pool);
}






@end







|
<







|
<



















|
<
















|
<


|
<


|
<










>
>
>
>
>
>

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
	[_categories addObject: category];

	objc_autoreleasePoolPop(pool);

	return category;
}

- (void)of_parseFile: (OFString *)path encoding: (OFStringEncoding)encoding

{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file;
	OFINICategory *category = nil;
	OFString *line;

	@try {
		file = [OFFile fileWithPath: path 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)writeToFile: (OFString *)path
{
	[self writeToFile: path encoding: OFStringEncodingUTF8];

}

- (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding

{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file = [OFFile fileWithPath: path mode: @"w"];

	bool first = true;

	for (OFINICategory *category in _categories)
		if ([category of_writeToStream: file
				      encoding: encoding
					 first: first])
			first = false;

	objc_autoreleasePoolPop(pool);
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@: %@>",
					   self.class, _categories];
}
@end

Modified src/OFINIFileSettings.h from [23812347d2] to [1f5bf8dc61].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFINIFileSettings.m from [062d94ddbb] to [768ab5ebfe].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
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
}

- (void)of_getCategory: (OFString **)category
		andKey: (OFString **)key
	       forPath: (OFString *)path OF_DIRECT
{
	size_t pos = [path rangeOfString: @"."
				 options: OF_STRING_SEARCH_BACKWARDS].location;

	if (pos == OF_NOT_FOUND) {
		*category = @"";
		*key = path;
		return;
	}

	*category = [path substringToIndex: pos];
	*key = [path substringFromIndex: pos + 1];
}

- (void)setString: (OFString *)string
	  forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category

		      andKey: &key


		     forPath: path];






	[[_INIFile categoryForName: category] setString: string
						 forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setInteger: (long long)integer
	   forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	[[_INIFile categoryForName: category] setInteger: integer
						  forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setBool: (bool)bool_
	forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category

		      andKey: &key


		     forPath: path];






	[[_INIFile categoryForName: category] setBool: bool_
					       forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setFloat: (float)float_
	 forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	[[_INIFile categoryForName: category] setFloat: float_
						forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setDouble: (double)double_
	  forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	[[_INIFile categoryForName: category] setDouble: double_
						 forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setArray: (OFArray *)array
	 forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	[[_INIFile categoryForName: category] setArray: array
						forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (OFString *)stringForPath: (OFString *)path
	       defaultValue: (OFString *)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key, *ret;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	ret = [[_INIFile categoryForName: category] stringForKey: key
						    defaultValue: defaultValue];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (long long)integerForPath: (OFString *)path
	       defaultValue: (long long)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	long long ret;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	ret = [[_INIFile categoryForName: category]
	    integerForKey: key
	     defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)boolForPath: (OFString *)path
       defaultValue: (bool)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	bool ret;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	ret = [[_INIFile categoryForName: category] boolForKey: key
						  defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (float)floatForPath: (OFString *)path
	 defaultValue: (float)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	float ret;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	ret = [[_INIFile categoryForName: category] floatForKey: key
						   defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (double)doubleForPath: (OFString *)path
	   defaultValue: (double)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	double ret;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	ret = [[_INIFile categoryForName: category] doubleForKey: key
						    defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFArray *)arrayForPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	OFArray *ret;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	ret = [[_INIFile categoryForName: category] arrayForKey: key];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (void)removeValueForPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category
		      andKey: &key
		     forPath: path];

	[[_INIFile categoryForName: category] removeValueForKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)save
{
	[_INIFile writeToFile: _filePath];
}
@end







|

|









|
<




|
>
|
>
>
|
>
|
>
>
>
>
|
|




<
|




|
<
<
<
|
<




<
|




|
>
|
>
>
|
>
|
>
>
>
>
|
<




|
|




|
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










|
<
<
<








|
|





|
<
<
<

|
|






|
<





|
<
<
<








|
<





|
<
<
<








|
<





|
<
<
<








|





|
<
<
<
|











|
<
<
<










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
}

- (void)of_getCategory: (OFString **)category
		andKey: (OFString **)key
	       forPath: (OFString *)path OF_DIRECT
{
	size_t pos = [path rangeOfString: @"."
				 options: OFStringSearchBackwards].location;

	if (pos == OFNotFound) {
		*category = @"";
		*key = path;
		return;
	}

	*category = [path substringToIndex: pos];
	*key = [path substringFromIndex: pos + 1];
}

- (void)setString: (OFString *)string forPath: (OFString *)path

{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setString: string forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setLongLong: (long long)longLong forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setLongLong: longLong
						   forKey: key];

	objc_autoreleasePoolPop(pool);
}


- (void)setBool: (bool)bool_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];



	[[_INIFile categoryForName: category] setBool: bool_ forKey: key];


	objc_autoreleasePoolPop(pool);
}


- (void)setFloat: (float)float_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setFloat: float_ forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setDouble: (double)double_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setDouble: double_ forKey: key];


	objc_autoreleasePoolPop(pool);
}

- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array
	       forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];



	[[_INIFile categoryForName: category] setStringArray: array
						      forKey: key];

































	objc_autoreleasePoolPop(pool);
}

- (OFString *)stringForPath: (OFString *)path
	       defaultValue: (OFString *)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key, *ret;

	[self of_getCategory: &category andKey: &key forPath: path];



	ret = [[_INIFile categoryForName: category] stringForKey: key
						    defaultValue: defaultValue];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (long long)longLongForPath: (OFString *)path
		defaultValue: (long long)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	long long ret;

	[self of_getCategory: &category andKey: &key forPath: path];



	ret = [[_INIFile categoryForName: category]
	    longLongForKey: key
	      defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)boolForPath: (OFString *)path defaultValue: (bool)defaultValue

{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	bool ret;

	[self of_getCategory: &category andKey: &key forPath: path];



	ret = [[_INIFile categoryForName: category] boolForKey: key
						  defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (float)floatForPath: (OFString *)path defaultValue: (float)defaultValue

{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	float ret;

	[self of_getCategory: &category andKey: &key forPath: path];



	ret = [[_INIFile categoryForName: category] floatForKey: key
						   defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (double)doubleForPath: (OFString *)path defaultValue: (double)defaultValue

{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	double ret;

	[self of_getCategory: &category andKey: &key forPath: path];



	ret = [[_INIFile categoryForName: category] doubleForKey: key
						    defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFArray OF_GENERIC(OFString *) *)stringArrayForPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	OFArray *ret;

	[self of_getCategory: &category andKey: &key forPath: path];



	ret = [[_INIFile categoryForName: category] stringArrayForKey: key];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (void)removeValueForPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];



	[[_INIFile categoryForName: category] removeValueForKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)save
{
	[_INIFile writeToFile: _filePath];
}
@end

Modified src/OFIPSocketAsyncConnector.h from [36d029e68c] to [248ffd2ce4].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFDNSResolver.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"

OF_ASSUME_NONNULL_BEGIN

@protocol OFIPSocketAsyncConnecting
- (bool)of_createSocketForAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo;
- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo;
- (void)of_closeSocket;
@end

@interface OFIPSocketAsyncConnector: OFObject <OFRunLoopConnectDelegate,
    OFDNSResolverHostDelegate>
{

<
<
|




















|

|







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 "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>
{
44
45
46
47
48
49
50
51
52
53
54
55

- (instancetype)initWithSocket: (id)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (nullable id)delegate
			 block: (nullable id)block;
- (void)didConnect;
- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
@end

OF_ASSUME_NONNULL_END







|
|



42
43
44
45
46
47
48
49
50
51
52
53

- (instancetype)initWithSocket: (id)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (nullable id)delegate
			 block: (nullable id)block;
- (void)didConnect;
- (void)tryNextAddressWithRunLoopMode: (OFRunLoopMode)runLoopMode;
- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode;
@end

OF_ASSUME_NONNULL_END

Modified src/OFIPSocketAsyncConnector.m from [6a448fb9d2] to [e235fd33e4].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFIPSocketAsyncConnector.h"
#import "OFData.h"
#ifdef OF_HAVE_SCTP
# import "OFSCTPSocket.h"
#endif
#import "OFTCPSocket.h"
#import "OFThread.h"
#import "OFTimer.h"

#import "OFConnectionFailedException.h"
#import "OFInvalidFormatException.h"


<
<
|



















<
<
<







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


 * 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 "OFIPSocketAsyncConnector.h"
#import "OFData.h"



#import "OFTCPSocket.h"
#import "OFThread.h"
#import "OFTimer.h"

#import "OFConnectionFailedException.h"
#import "OFInvalidFormatException.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
{
	if (_exception == nil)
		[_socket setCanBlock: true];

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		if ([_socket isKindOfClass: [OFTCPSocket class]])
			((of_tcp_socket_async_connect_block_t)_block)(
			    _exception);
# ifdef OF_HAVE_SCTP
		else if ([_socket isKindOfClass: [OFSCTPSocket class]])
			((of_sctp_socket_async_connect_block_t)_block)(
			    _exception);
# endif
		else
			OF_ENSURE(0);
	} else {
#endif
		if ([_delegate respondsToSelector:
		    @selector(socket:didConnectToHost:port:exception:)])
			[_delegate    socket: _socket
			    didConnectToHost: _host
					port: _port
				   exception: _exception];
#ifdef OF_HAVE_BLOCKS
	}
#endif
}

- (void)of_socketDidConnect: (id)sock
		  exception: (id)exception
{
	if (exception != nil) {
		/*
		 * self might be retained only by the pending async requests,
		 * which we're about to cancel.
		 */
		[[self retain] autorelease];







<
|
<
<
<
<
<

|













|
<







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
{
	if (_exception == nil)
		[_socket setCanBlock: true];

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		if ([_socket isKindOfClass: [OFTCPSocket class]])

			((OFTCPSocketAsyncConnectBlock)_block)(_exception);





		else
			OFEnsure(0);
	} else {
#endif
		if ([_delegate respondsToSelector:
		    @selector(socket:didConnectToHost:port:exception:)])
			[_delegate    socket: _socket
			    didConnectToHost: _host
					port: _port
				   exception: _exception];
#ifdef OF_HAVE_BLOCKS
	}
#endif
}

- (void)of_socketDidConnect: (id)sock exception: (id)exception

{
	if (exception != nil) {
		/*
		 * self might be retained only by the pending async requests,
		 * which we're about to cancel.
		 */
		[[self retain] autorelease];
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
			    @selector(tryNextAddressWithRunLoopMode:);
			OFTimer *timer = [OFTimer
			    timerWithTimeInterval: 0
					   target: self
					 selector: selector
					   object: runLoop.currentMode
					  repeats: false];
			[runLoop addTimer: timer
				  forMode: runLoop.currentMode];
		}

		return;
	}

	[self didConnect];
}

- (id)of_connectionFailedExceptionForErrNo: (int)errNo
{
	return [OFConnectionFailedException exceptionWithHost: _host
							 port: _port
						       socket: _socket
							errNo: errNo];
}

- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
{
	of_socket_address_t address = *(const of_socket_address_t *)
	    [_socketAddresses itemAtIndex: _socketAddressesIndex++];
	int errNo;

	of_socket_address_set_port(&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];







|
<
















|

|



|

|
<







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
			    @selector(tryNextAddressWithRunLoopMode:);
			OFTimer *timer = [OFTimer
			    timerWithTimeInterval: 0
					   target: self
					 selector: selector
					   object: runLoop.currentMode
					  repeats: false];
			[runLoop addTimer: timer forMode: runLoop.currentMode];

		}

		return;
	}

	[self didConnect];
}

- (id)of_connectionFailedExceptionForErrNo: (int)errNo
{
	return [OFConnectionFailedException exceptionWithHost: _host
							 port: _port
						       socket: _socket
							errNo: errNo];
}

- (void)tryNextAddressWithRunLoopMode: (OFRunLoopMode)runLoopMode
{
	OFSocketAddress address = *(const OFSocketAddress *)
	    [_socketAddresses itemAtIndex: _socketAddressesIndex++];
	int errNo;

	OFSocketAddressSetPort(&address, _port);

	if (![_socket of_createSocketForAddress: &address errNo: &errNo]) {

		if (_socketAddressesIndex >= _socketAddresses.count) {
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: _socket
				   errNo: errNo];
			[self didConnect];
181
182
183
184
185
186
187
188
189
190



191

192
193
194
195
196
197
198
	 * FIXME: Use a different thread as a work around.
	 */
	[_socket setCanBlock: true];
#else
	[_socket setCanBlock: false];
#endif

	if (![_socket of_connectSocketToAddress: &address
					  errNo: &errNo]) {
#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)



		if (errNo == EINPROGRESS) {

			[OFRunLoop of_addAsyncConnectForSocket: _socket
							  mode: runLoopMode
						      delegate: self];
			return;
		} else {
#endif
			[_socket of_closeSocket];







|
<

>
>
>

>







167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
	 * FIXME: Use a different thread as a work around.
	 */
	[_socket setCanBlock: true];
#else
	[_socket setCanBlock: false];
#endif

	if (![_socket of_connectSocketToAddress: &address errNo: &errNo]) {

#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
# ifdef OF_WINDOWS
		if (errNo == EINPROGRESS || errNo == EWOULDBLOCK) {
# else
		if (errNo == EINPROGRESS) {
# endif
			[OFRunLoop of_addAsyncConnectForSocket: _socket
							  mode: runLoopMode
						      delegate: self];
			return;
		} else {
#endif
			[_socket of_closeSocket];
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

	_socketAddresses = [addresses copy];

	[self tryNextAddressWithRunLoopMode:
	    [OFRunLoop currentRunLoop].currentMode];
}

- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
{
	@try {
		of_socket_address_t address =
		    of_socket_address_parse_ip(_host, _port);

		_socketAddresses = [[OFData alloc]
		    initWithItems: &address

			 itemSize: sizeof(address)
			    count: 1];

		[self tryNextAddressWithRunLoopMode: runLoopMode];
		return;
	} @catch (OFInvalidFormatException *e) {
	}

	[[OFThread DNSResolver]
	    asyncResolveAddressesForHost: _host
			   addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY
			     runLoopMode: runLoopMode
				delegate: self];
}
@end







|


<
|



>
|
<








|




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

	_socketAddresses = [addresses copy];

	[self tryNextAddressWithRunLoopMode:
	    [OFRunLoop currentRunLoop].currentMode];
}

- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode
{
	@try {

		OFSocketAddress address = OFSocketAddressParseIP(_host, _port);

		_socketAddresses = [[OFData alloc]
		    initWithItems: &address
			    count: 1
			 itemSize: sizeof(address)];


		[self tryNextAddressWithRunLoopMode: runLoopMode];
		return;
	} @catch (OFInvalidFormatException *e) {
	}

	[[OFThread DNSResolver]
	    asyncResolveAddressesForHost: _host
			   addressFamily: OFSocketAddressFamilyAny
			     runLoopMode: runLoopMode
				delegate: self];
}
@end

Modified src/OFIPXSocket.h from [fb0c56ae44] to [a2ca3d4d82].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@end

/**
 * @class OFIPXSocket OFIPXSocket.h ObjFW/OFIPXSocket.h
 *
 * @brief A class which provides methods to create and use IPX sockets.
 *
 * Addresses are of type @ref of_socket_address_t. You can use
 * @ref of_socket_address_ipx to create an address or
 * @ref of_socket_address_get_ipx_network to get the IPX network,
 * @ref of_socket_address_get_ipx_node to get the IPX node and
 * @ref of_socket_address_get_port 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







|
|
|
|
|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@end

/**
 * @class OFIPXSocket OFIPXSocket.h ObjFW/OFIPXSocket.h
 *
 * @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
70
71
72
73
74
75
76
77
78
79
80
81
 *	  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
 */
- (of_socket_address_t)bindToPort: (uint16_t)port
		       packetType: (uint8_t)packetType;
@end

OF_ASSUME_NONNULL_END







<
|



68
69
70
71
72
73
74

75
76
77
78
 *	  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

Modified src/OFIPXSocket.m from [06e0564b9c] to [832ec6a7bd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#include <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFIPXSocket.h"



#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"

#import "socket.h"
#import "socket_helpers.h"

@implementation OFIPXSocket
@dynamic delegate;

- (of_socket_address_t)bindToPort: (uint16_t)port
		       packetType: (uint8_t)packetType
{
	const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
	of_socket_address_t address;
	int protocol = 0;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = of_socket_address_ipx(zeroNode, 0, port);

#ifdef OF_WINDOWS
	protocol = NSPROTO_IPX + packetType;
#else
	_packetType = address.sockaddr.ipx.sipx_type = packetType;
#endif

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_DGRAM | SOCK_CLOEXEC, protocol)) == INVALID_SOCKET)
		@throw [OFBindFailedException
		    exceptionWithPort: port
			   packetType: packetType
			       socket: self
				errNo: of_socket_errno()];

	_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, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: packetType
							 socket: self
							  errNo: errNo];
	}

	memset(&address, 0, sizeof(address));
	address.family = OF_SOCKET_ADDRESS_FAMILY_IPX;
	address.length = (socklen_t)sizeof(address.sockaddr);

	if (of_getsockname(_socket, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: packetType
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_family != AF_IPX) {
		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: packetType
							 socket: self
							  errNo: EAFNOSUPPORT];
	}

	return address;
}

#ifndef OF_WINDOWS
- (void)sendBuffer: (const void *)buffer
	    length: (size_t)length
	  receiver: (const of_socket_address_t *)receiver
{
	of_socket_address_t fixedReceiver;

	memcpy(&fixedReceiver, receiver, sizeof(fixedReceiver));

	/* If it's not IPX, no fix-up needed - it will fail anyway. */
	if (fixedReceiver.family == OF_SOCKET_ADDRESS_FAMILY_IPX)
		fixedReceiver.sockaddr.ipx.sipx_type = _packetType;

	[super sendBuffer: buffer
		   length: length
		 receiver: &fixedReceiver];
}
#endif
@end







>
>




<
<
<



<
|


|





|


|








|




|









|


|








|


|

|


|









|













|

|




|


|
<
<



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
#include <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#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(zeroNode, 0, port);

#ifdef OF_WINDOWS
	protocol = NSPROTO_IPX + packetType;
#else
	_packetType = address.sockaddr.ipx.sipx_type = packetType;
#endif

	if ((_socket = socket(address.sockaddr.sockaddr.sa_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, &address.sockaddr.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, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: packetType
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_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
	    length: (size_t)length
	  receiver: (const OFSocketAddress *)receiver
{
	OFSocketAddress fixedReceiver;

	memcpy(&fixedReceiver, receiver, sizeof(fixedReceiver));

	/* If it's not IPX, no fix-up needed - it will fail anyway. */
	if (fixedReceiver.family == OFSocketAddressFamilyIPX)
		fixedReceiver.sockaddr.ipx.sipx_type = _packetType;

	[super sendBuffer: buffer length: length receiver: &fixedReceiver];


}
#endif
@end

Modified src/OFInflate64Stream.h from [701031a024] to [2f3dc057f8].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFStream.h"
#import "OFKernelEventObserver.h"

OF_ASSUME_NONNULL_BEGIN

#define OF_INFLATE64_STREAM_BUFFER_SIZE 4096

/**
 * @class OFInflate64Stream OFInflate64Stream.h ObjFW/OFInflate64Stream.h
 *
 * @note This class only conforms to OFReadyForReadingObserving if the
 *	 underlying stream does so, too.
 *
 * @brief A class that handles Deflate decompression transparently for an
 *	  underlying stream.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFInflate64Stream: OFStream <OFReadyForReadingObserving>
{
	OFStream *_stream;
	unsigned char _buffer[OF_INFLATE64_STREAM_BUFFER_SIZE];
	uint16_t _bufferIndex, _bufferLength;
	uint8_t _byte;
	uint8_t _bitIndex, _savedBitsLength;
	uint16_t _savedBits;
	unsigned char *_Nullable _slidingWindow;
	uint16_t _slidingWindowIndex, _slidingWindowMask;
	int _state;
	union {
		struct {
			uint8_t position;
			uint8_t length[4];
		} uncompressedHeader;
		struct {
			uint16_t position, length;
		} uncompressed;
		struct {
			struct of_huffman_tree *_Nullable litLenTree;
			struct of_huffman_tree *_Nullable distTree;
			struct of_huffman_tree *_Nullable codeLenTree;
			struct of_huffman_tree *_Nullable treeIter;
			uint8_t *_Nullable lengths;
			uint16_t receivedCount;
			uint8_t value, litLenCodesCount, distCodesCount;
			uint8_t codeLenCodesCount;
		} huffmanTree;
		struct {
			struct of_huffman_tree *_Nullable litLenTree;
			struct of_huffman_tree *_Nullable distTree;
			struct of_huffman_tree *_Nullable treeIter;
			int state;
			uint16_t value, length, distance, extraBits;
		} huffman;
	} _context;
	bool _inLastBlock, _atEndOfStream;
}


<
<
|


















|














|
















|
|
|
|






|
|
|







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


 * 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 "OFStream.h"
#import "OFKernelEventObserver.h"

OF_ASSUME_NONNULL_BEGIN

#define OFInflate64StreamBufferSize 4096

/**
 * @class OFInflate64Stream OFInflate64Stream.h ObjFW/OFInflate64Stream.h
 *
 * @note This class only conforms to OFReadyForReadingObserving if the
 *	 underlying stream does so, too.
 *
 * @brief A class that handles Deflate decompression transparently for an
 *	  underlying stream.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFInflate64Stream: OFStream <OFReadyForReadingObserving>
{
	OFStream *_stream;
	unsigned char _buffer[OFInflate64StreamBufferSize];
	uint16_t _bufferIndex, _bufferLength;
	uint8_t _byte;
	uint8_t _bitIndex, _savedBitsLength;
	uint16_t _savedBits;
	unsigned char *_Nullable _slidingWindow;
	uint16_t _slidingWindowIndex, _slidingWindowMask;
	int _state;
	union {
		struct {
			uint8_t position;
			uint8_t length[4];
		} uncompressedHeader;
		struct {
			uint16_t position, length;
		} uncompressed;
		struct {
			struct _OFHuffmanTree *_Nullable litLenTree;
			struct _OFHuffmanTree *_Nullable distTree;
			struct _OFHuffmanTree *_Nullable codeLenTree;
			struct _OFHuffmanTree *_Nullable treeIter;
			uint8_t *_Nullable lengths;
			uint16_t receivedCount;
			uint8_t value, litLenCodesCount, distCodesCount;
			uint8_t codeLenCodesCount;
		} huffmanTree;
		struct {
			struct _OFHuffmanTree *_Nullable litLenTree;
			struct _OFHuffmanTree *_Nullable distTree;
			struct _OFHuffmanTree *_Nullable treeIter;
			int state;
			uint16_t value, length, distance, extraBits;
		} huffman;
	} _context;
	bool _inLastBlock, _atEndOfStream;
}

Modified src/OFInflate64Stream.m from [01fc1ef62b] to [96e65e9055].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFInflateStream.h from [c18a2de259] to [fe62e030fd].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFStream.h"
#import "OFKernelEventObserver.h"

OF_ASSUME_NONNULL_BEGIN

#define OF_INFLATE_STREAM_BUFFER_SIZE 4096

/**
 * @class OFInflateStream OFInflateStream.h ObjFW/OFInflateStream.h
 *
 * @note This class only conforms to OFReadyForReadingObserving if the
 *	 underlying stream does so, too.
 *
 * @brief A class that handles Deflate decompression transparently for an
 *	  underlying stream.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFInflateStream: OFStream <OFReadyForReadingObserving>
{
	OFStream *_stream;
	unsigned char _buffer[OF_INFLATE_STREAM_BUFFER_SIZE];
	uint16_t _bufferIndex, _bufferLength;
	uint8_t _byte;
	uint8_t _bitIndex, _savedBitsLength;
	uint16_t _savedBits;
	unsigned char *_Nullable _slidingWindow;
	uint16_t _slidingWindowIndex, _slidingWindowMask;
	int _state;
	union {
		struct {
			uint8_t position;
			uint8_t length[4];
		} uncompressedHeader;
		struct {
			uint16_t position, length;
		} uncompressed;
		struct {
			struct of_huffman_tree *_Nullable litLenTree;
			struct of_huffman_tree *_Nullable distTree;
			struct of_huffman_tree *_Nullable codeLenTree;
			struct of_huffman_tree *_Nullable treeIter;
			uint8_t *_Nullable lengths;
			uint16_t receivedCount;
			uint8_t value, litLenCodesCount, distCodesCount;
			uint8_t codeLenCodesCount;
		} huffmanTree;
		struct {
			struct of_huffman_tree *_Nullable litLenTree;
			struct of_huffman_tree *_Nullable distTree;
			struct of_huffman_tree *_Nullable treeIter;
			int state;
			uint16_t value, length, distance, extraBits;
		} huffman;
	} _context;
	bool _inLastBlock, _atEndOfStream;
}


<
<
|


















|














|
















|
|
|
|






|
|
|







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


 * 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 "OFStream.h"
#import "OFKernelEventObserver.h"

OF_ASSUME_NONNULL_BEGIN

#define OFInflateStreamBufferSize 4096

/**
 * @class OFInflateStream OFInflateStream.h ObjFW/OFInflateStream.h
 *
 * @note This class only conforms to OFReadyForReadingObserving if the
 *	 underlying stream does so, too.
 *
 * @brief A class that handles Deflate decompression transparently for an
 *	  underlying stream.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFInflateStream: OFStream <OFReadyForReadingObserving>
{
	OFStream *_stream;
	unsigned char _buffer[OFInflateStreamBufferSize];
	uint16_t _bufferIndex, _bufferLength;
	uint8_t _byte;
	uint8_t _bitIndex, _savedBitsLength;
	uint16_t _savedBits;
	unsigned char *_Nullable _slidingWindow;
	uint16_t _slidingWindowIndex, _slidingWindowMask;
	int _state;
	union {
		struct {
			uint8_t position;
			uint8_t length[4];
		} uncompressedHeader;
		struct {
			uint16_t position, length;
		} uncompressed;
		struct {
			struct _OFHuffmanTree *_Nullable litLenTree;
			struct _OFHuffmanTree *_Nullable distTree;
			struct _OFHuffmanTree *_Nullable codeLenTree;
			struct _OFHuffmanTree *_Nullable treeIter;
			uint8_t *_Nullable lengths;
			uint16_t receivedCount;
			uint8_t value, litLenCodesCount, distCodesCount;
			uint8_t codeLenCodesCount;
		} huffmanTree;
		struct {
			struct _OFHuffmanTree *_Nullable litLenTree;
			struct _OFHuffmanTree *_Nullable distTree;
			struct _OFHuffmanTree *_Nullable treeIter;
			int state;
			uint16_t value, length, distance, extraBits;
		} huffman;
	} _context;
	bool _inLastBlock, _atEndOfStream;
}

Modified src/OFInflateStream.m from [c1d8d6e047] to [28d4702177].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#ifndef OF_INFLATE64_STREAM_M
# import "OFInflateStream.h"
#else
# import "OFInflate64Stream.h"
# define OFInflateStream OFInflate64Stream
#endif

#import "huffman_tree.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"

#ifndef OF_INFLATE64_STREAM_M
# define BUFFER_SIZE OF_INFLATE_STREAM_BUFFER_SIZE
#else
# define BUFFER_SIZE OF_INFLATE64_STREAM_BUFFER_SIZE
#endif

enum state {
	BLOCK_HEADER,
	UNCOMPRESSED_BLOCK_HEADER,
	UNCOMPRESSED_BLOCK,
	HUFFMAN_TREE,
	HUFFMAN_BLOCK
};


enum huffman_state {
	WRITE_VALUE,
	AWAIT_CODE,
	AWAIT_LENGTH_EXTRA_BITS,
	AWAIT_DISTANCE,
	AWAIT_DISTANCE_EXTRA_BITS,
	PROCESS_PAIR
};

#ifndef OF_INFLATE64_STREAM_M
static const uint8_t numDistanceCodes = 30;
static const uint8_t lengthCodes[29] = {
	/* indices are -257, values -3 */
	0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,







<
|







|

|


|
|
|
|
|
|


>
|
<
|
|
|
|
|







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

#ifndef OF_INFLATE64_STREAM_M
# import "OFInflateStream.h"
#else
# import "OFInflate64Stream.h"
# define OFInflateStream OFInflate64Stream
#endif

#import "OFHuffmanTree.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"

#ifndef OF_INFLATE64_STREAM_M
# define bufferSize OFInflateStreamBufferSize
#else
# define bufferSize OFInflate64StreamBufferSize
#endif

enum State {
	stateBlockHeader,
	stateUncompressedBlockHeader,
	stateUncompressedBlock,
	stateHuffmanTree,
	stateHuffmanBlock
};

enum HuffmanState {
	huffmanStateWriteValue,

	huffmanStateAwaitCode,
	huffmanStateAwaitLengthExtraBits,
	huffmanStateAwaitDistance,
	huffmanStateAwaitDistanceExtraBits,
	huffmanStateProcessPair
};

#ifndef OF_INFLATE64_STREAM_M
static const uint8_t numDistanceCodes = 30;
static const uint8_t lengthCodes[29] = {
	/* indices are -257, values -3 */
	0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
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
	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
	10, 11, 11, 12, 12, 13, 13, 14, 14
};
#endif
static const uint8_t codeLengthsOrder[19] = {
	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
static struct of_huffman_tree *fixedLitLenTree, *fixedDistTree;

@implementation OFInflateStream
static OF_INLINE bool
tryReadBits(OFInflateStream *stream, uint16_t *bits, uint8_t count)
{
	uint16_t ret = stream->_savedBits;

	assert(stream->_savedBitsLength < count);

	for (uint_fast8_t i = stream->_savedBitsLength; i < count; i++) {
		if OF_UNLIKELY (stream->_bitIndex == 8) {
			if OF_LIKELY (stream->_bufferIndex <
			    stream->_bufferLength)
				stream->_byte =
				    stream->_buffer[stream->_bufferIndex++];
			else {
				size_t length = [stream->_stream
				    readIntoBuffer: stream->_buffer
					    length: BUFFER_SIZE];

				if OF_UNLIKELY (length < 1) {
					stream->_savedBits = ret;
					stream->_savedBitsLength = i;
					return false;
				}








|


















|







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
	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
	10, 11, 11, 12, 12, 13, 13, 14, 14
};
#endif
static const uint8_t codeLengthsOrder[19] = {
	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
static OFHuffmanTree fixedLitLenTree, fixedDistTree;

@implementation OFInflateStream
static OF_INLINE bool
tryReadBits(OFInflateStream *stream, uint16_t *bits, uint8_t count)
{
	uint16_t ret = stream->_savedBits;

	assert(stream->_savedBitsLength < count);

	for (uint_fast8_t i = stream->_savedBitsLength; i < count; i++) {
		if OF_UNLIKELY (stream->_bitIndex == 8) {
			if OF_LIKELY (stream->_bufferIndex <
			    stream->_bufferLength)
				stream->_byte =
				    stream->_buffer[stream->_bufferIndex++];
			else {
				size_t length = [stream->_stream
				    readIntoBuffer: stream->_buffer
					    length: bufferSize];

				if OF_UNLIKELY (length < 1) {
					stream->_savedBits = ret;
					stream->_savedBitsLength = i;
					return false;
				}

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
	for (uint16_t i = 144; i <= 255; i++)
		lengths[i] = 9;
	for (uint16_t i = 256; i <= 279; i++)
		lengths[i] = 7;
	for (uint16_t i = 280; i <= 287; i++)
		lengths[i] = 8;

	fixedLitLenTree = of_huffman_tree_construct(lengths, 288);

	for (uint16_t i = 0; i <= 31; i++)
		lengths[i] = 5;

	fixedDistTree = of_huffman_tree_construct(lengths, 32);
}

+ (instancetype)streamWithStream: (OFStream *)stream
{
	return [[[self alloc] initWithStream: stream] autorelease];
}








|




|







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
	for (uint16_t i = 144; i <= 255; i++)
		lengths[i] = 9;
	for (uint16_t i = 256; i <= 279; i++)
		lengths[i] = 7;
	for (uint16_t i = 280; i <= 287; i++)
		lengths[i] = 8;

	fixedLitLenTree = OFHuffmanTreeNew(lengths, 288);

	for (uint16_t i = 0; i <= 31; i++)
		lengths[i] = 5;

	fixedDistTree = OFHuffmanTreeNew(lengths, 32);
}

+ (instancetype)streamWithStream: (OFStream *)stream
{
	return [[[self alloc] initWithStream: stream] autorelease];
}

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
		_bitIndex = 8;

#ifdef OF_INFLATE64_STREAM_M
		_slidingWindowMask = 0xFFFF;
#else
		_slidingWindowMask = 0x7FFF;
#endif
		_slidingWindow = [self allocZeroedMemoryWithSize:
		    _slidingWindowMask + 1];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_stream != nil)
		[self close];



	if (_state == HUFFMAN_TREE)


		if (_context.huffmanTree.codeLenTree != NULL)
			of_huffman_tree_release(
			    _context.huffmanTree.codeLenTree);

	if (_state == HUFFMAN_TREE || _state == HUFFMAN_BLOCK) {

		if (_context.huffman.litLenTree != fixedLitLenTree)
			of_huffman_tree_release(_context.huffman.litLenTree);
		if (_context.huffman.distTree != fixedDistTree)
			of_huffman_tree_release(_context.huffman.distTree);
	}

	[super dealloc];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer_
			  length: (size_t)length







<
|













>
>
|
>
>

<
|
|
|
>

|

|







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
		_bitIndex = 8;

#ifdef OF_INFLATE64_STREAM_M
		_slidingWindowMask = 0xFFFF;
#else
		_slidingWindowMask = 0x7FFF;
#endif

		_slidingWindow = OFAllocZeroedMemory(_slidingWindowMask + 1, 1);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_stream != nil)
		[self close];

	OFFreeMemory(_slidingWindow);

	if (_state == stateHuffmanTree) {
		OFFreeMemory(_context.huffmanTree.lengths);

		if (_context.huffmanTree.codeLenTree != NULL)

			OFHuffmanTreeFree(_context.huffmanTree.codeLenTree);
	}

	if (_state == stateHuffmanTree || _state == stateHuffmanBlock) {
		if (_context.huffman.litLenTree != fixedLitLenTree)
			OFHuffmanTreeFree(_context.huffman.litLenTree);
		if (_context.huffman.distTree != fixedDistTree)
			OFHuffmanTreeFree(_context.huffman.distTree);
	}

	[super dealloc];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer_
			  length: (size_t)length
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
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_atEndOfStream)
		return 0;

start:
	switch ((enum state)_state) {
	case BLOCK_HEADER:
		if OF_UNLIKELY (_inLastBlock) {
			[_stream unreadFromBuffer: _buffer + _bufferIndex
					   length: _bufferLength -
						   _bufferIndex];
			_bufferIndex = _bufferLength = 0;

			_atEndOfStream = true;
			return bytesWritten;
		}

		if OF_UNLIKELY (!tryReadBits(self, &bits, 3))
			return bytesWritten;

		_inLastBlock = (bits & 1);

		switch (bits >> 1) {
		case 0: /* No compression */
			_state = UNCOMPRESSED_BLOCK_HEADER;
			_bitIndex = 8;
			_context.uncompressedHeader.position = 0;
			memset(_context.uncompressedHeader.length, 0, 4);
			break;
		case 1: /* Fixed Huffman */
			_state = HUFFMAN_BLOCK;
			_context.huffman.state = AWAIT_CODE;
			_context.huffman.litLenTree = fixedLitLenTree;
			_context.huffman.distTree = fixedDistTree;
			_context.huffman.treeIter = fixedLitLenTree;
			break;
		case 2: /* Dynamic Huffman */
			_state = HUFFMAN_TREE;
			_context.huffmanTree.lengths = NULL;
			_context.huffmanTree.receivedCount = 0;
			_context.huffmanTree.value = 0xFE;
			_context.huffmanTree.litLenCodesCount = 0xFF;
			_context.huffmanTree.distCodesCount = 0xFF;
			_context.huffmanTree.codeLenCodesCount = 0xFF;
			break;
		default:
			@throw [OFInvalidFormatException exception];
		}

		goto start;
	case UNCOMPRESSED_BLOCK_HEADER:
#define CTX _context.uncompressedHeader
		/* FIXME: This can be done more efficiently than unreading */
		[_stream unreadFromBuffer: _buffer + _bufferIndex
				   length: _bufferLength - _bufferIndex];
		_bufferIndex = _bufferLength = 0;

		CTX.position += [_stream
		    readIntoBuffer: CTX.length + CTX.position
			    length: 4 - CTX.position];

		if OF_UNLIKELY (CTX.position < 4)
			return bytesWritten;

		if OF_UNLIKELY ((CTX.length[0] | (CTX.length[1] << 8)) !=
		    (uint16_t)~(CTX.length[2] | (CTX.length[3] << 8)))
			@throw [OFInvalidFormatException exception];

		_state = UNCOMPRESSED_BLOCK;

		/*
		 * Do not reorder! _context.uncompressed.position and
		 * _context.uncompressedHeader.length overlap!
		 */
		_context.uncompressed.length =
		    CTX.length[0] | (CTX.length[1] << 8);
		_context.uncompressed.position = 0;

		goto start;
#undef CTX
	case UNCOMPRESSED_BLOCK:
#define CTX _context.uncompressed
		if OF_UNLIKELY (length == 0)
			return bytesWritten;

		tmp = (length < (size_t)CTX.length - CTX.position
		    ? (uint16_t)length : CTX.length - CTX.position);








|
|

















|





|
|





|












|

















|











|







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
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_atEndOfStream)
		return 0;

start:
	switch ((enum State)_state) {
	case stateBlockHeader:
		if OF_UNLIKELY (_inLastBlock) {
			[_stream unreadFromBuffer: _buffer + _bufferIndex
					   length: _bufferLength -
						   _bufferIndex];
			_bufferIndex = _bufferLength = 0;

			_atEndOfStream = true;
			return bytesWritten;
		}

		if OF_UNLIKELY (!tryReadBits(self, &bits, 3))
			return bytesWritten;

		_inLastBlock = (bits & 1);

		switch (bits >> 1) {
		case 0: /* No compression */
			_state = stateUncompressedBlockHeader;
			_bitIndex = 8;
			_context.uncompressedHeader.position = 0;
			memset(_context.uncompressedHeader.length, 0, 4);
			break;
		case 1: /* Fixed Huffman */
			_state = stateHuffmanBlock;
			_context.huffman.state = huffmanStateAwaitCode;
			_context.huffman.litLenTree = fixedLitLenTree;
			_context.huffman.distTree = fixedDistTree;
			_context.huffman.treeIter = fixedLitLenTree;
			break;
		case 2: /* Dynamic Huffman */
			_state = stateHuffmanTree;
			_context.huffmanTree.lengths = NULL;
			_context.huffmanTree.receivedCount = 0;
			_context.huffmanTree.value = 0xFE;
			_context.huffmanTree.litLenCodesCount = 0xFF;
			_context.huffmanTree.distCodesCount = 0xFF;
			_context.huffmanTree.codeLenCodesCount = 0xFF;
			break;
		default:
			@throw [OFInvalidFormatException exception];
		}

		goto start;
	case stateUncompressedBlockHeader:
#define CTX _context.uncompressedHeader
		/* FIXME: This can be done more efficiently than unreading */
		[_stream unreadFromBuffer: _buffer + _bufferIndex
				   length: _bufferLength - _bufferIndex];
		_bufferIndex = _bufferLength = 0;

		CTX.position += [_stream
		    readIntoBuffer: CTX.length + CTX.position
			    length: 4 - CTX.position];

		if OF_UNLIKELY (CTX.position < 4)
			return bytesWritten;

		if OF_UNLIKELY ((CTX.length[0] | (CTX.length[1] << 8)) !=
		    (uint16_t)~(CTX.length[2] | (CTX.length[3] << 8)))
			@throw [OFInvalidFormatException exception];

		_state = stateUncompressedBlock;

		/*
		 * Do not reorder! _context.uncompressed.position and
		 * _context.uncompressedHeader.length overlap!
		 */
		_context.uncompressed.length =
		    CTX.length[0] | (CTX.length[1] << 8);
		_context.uncompressed.position = 0;

		goto start;
#undef CTX
	case stateUncompressedBlock:
#define CTX _context.uncompressed
		if OF_UNLIKELY (length == 0)
			return bytesWritten;

		tmp = (length < (size_t)CTX.length - CTX.position
		    ? (uint16_t)length : CTX.length - CTX.position);

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
		_slidingWindowIndex = slidingWindowIndex;

		length -= tmp;
		bytesWritten += tmp;

		CTX.position += tmp;
		if OF_UNLIKELY (CTX.position == CTX.length)
			_state = BLOCK_HEADER;

		goto start;
#undef CTX
	case HUFFMAN_TREE:
#define CTX _context.huffmanTree
		if OF_LIKELY (CTX.value == 0xFE) {
			if OF_LIKELY (CTX.litLenCodesCount == 0xFF) {
				if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
					return bytesWritten;

				if OF_UNLIKELY (bits > 29)







|



|







339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
		_slidingWindowIndex = slidingWindowIndex;

		length -= tmp;
		bytesWritten += tmp;

		CTX.position += tmp;
		if OF_UNLIKELY (CTX.position == CTX.length)
			_state = stateBlockHeader;

		goto start;
#undef CTX
	case stateHuffmanTree:
#define CTX _context.huffmanTree
		if OF_LIKELY (CTX.value == 0xFE) {
			if OF_LIKELY (CTX.litLenCodesCount == 0xFF) {
				if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
					return bytesWritten;

				if OF_UNLIKELY (bits > 29)
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
				if OF_UNLIKELY (!tryReadBits(self, &bits, 4))
					return bytesWritten;

				CTX.codeLenCodesCount = bits;
			}

			if OF_LIKELY (CTX.lengths == NULL)
				CTX.lengths = [self
				    allocZeroedMemoryWithSize: 19];

			for (uint16_t i = CTX.receivedCount;
			    i < CTX.codeLenCodesCount + 4; i++) {
				if OF_UNLIKELY (!tryReadBits(self, &bits, 3)) {
					CTX.receivedCount = i;
					return bytesWritten;
				}

				CTX.lengths[codeLengthsOrder[i]] = bits;
			}

			CTX.codeLenTree = of_huffman_tree_construct(
			    CTX.lengths, 19);
			CTX.treeIter = CTX.codeLenTree;

			[self freeMemory: CTX.lengths];
			CTX.lengths = NULL;
			CTX.receivedCount = 0;
			CTX.value = 0xFF;
		}

		if OF_LIKELY (CTX.lengths == NULL)
			CTX.lengths = [self allocMemoryWithSize:
			    CTX.litLenCodesCount + CTX.distCodesCount + 258];

		for (uint16_t i = CTX.receivedCount;
		    i < CTX.litLenCodesCount + CTX.distCodesCount + 258;) {
			uint8_t j, count;

			if OF_LIKELY (CTX.value == 0xFF) {
				if OF_UNLIKELY (!of_huffman_tree_walk(self,
				    tryReadBits, &CTX.treeIter, &value)) {
					CTX.receivedCount = i;
					return bytesWritten;
				}

				CTX.treeIter = CTX.codeLenTree;








|
<











<
|


|






|
|






|







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
				if OF_UNLIKELY (!tryReadBits(self, &bits, 4))
					return bytesWritten;

				CTX.codeLenCodesCount = bits;
			}

			if OF_LIKELY (CTX.lengths == NULL)
				CTX.lengths = OFAllocZeroedMemory(19, 1);


			for (uint16_t i = CTX.receivedCount;
			    i < CTX.codeLenCodesCount + 4; i++) {
				if OF_UNLIKELY (!tryReadBits(self, &bits, 3)) {
					CTX.receivedCount = i;
					return bytesWritten;
				}

				CTX.lengths[codeLengthsOrder[i]] = bits;
			}


			CTX.codeLenTree = OFHuffmanTreeNew(CTX.lengths, 19);
			CTX.treeIter = CTX.codeLenTree;

			OFFreeMemory(CTX.lengths);
			CTX.lengths = NULL;
			CTX.receivedCount = 0;
			CTX.value = 0xFF;
		}

		if OF_LIKELY (CTX.lengths == NULL)
			CTX.lengths = OFAllocMemory(
			    CTX.litLenCodesCount + CTX.distCodesCount + 258, 1);

		for (uint16_t i = CTX.receivedCount;
		    i < CTX.litLenCodesCount + CTX.distCodesCount + 258;) {
			uint8_t j, count;

			if OF_LIKELY (CTX.value == 0xFF) {
				if OF_UNLIKELY (!OFHuffmanTreeWalk(self,
				    tryReadBits, &CTX.treeIter, &value)) {
					CTX.receivedCount = i;
					return bytesWritten;
				}

				CTX.treeIter = CTX.codeLenTree;

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

			for (j = 0; j < count; j++)
				CTX.lengths[i++] = value;

			CTX.value = 0xFF;
		}

		of_huffman_tree_release(CTX.codeLenTree);
		CTX.codeLenTree = NULL;

		CTX.litLenTree = of_huffman_tree_construct(CTX.lengths,
		    CTX.litLenCodesCount + 257);
		CTX.distTree = of_huffman_tree_construct(
		    CTX.lengths + CTX.litLenCodesCount + 257,
		    CTX.distCodesCount + 1);

		[self freeMemory: CTX.lengths];

		/*
		 * litLenTree and distTree are at the same location in
		 * _context.huffman and _context.huffmanTree, thus no need to
		 * set them.
		 */
		_state = HUFFMAN_BLOCK;
		_context.huffman.state = AWAIT_CODE;
		_context.huffman.treeIter = CTX.litLenTree;

		goto start;
#undef CTX
	case HUFFMAN_BLOCK:
#define CTX _context.huffman
		for (;;) {
			uint8_t extraBits, lengthCodeIndex;

			if OF_UNLIKELY (CTX.state == WRITE_VALUE) {
				if OF_UNLIKELY (length == 0)
					return bytesWritten;

				buffer[bytesWritten++] = CTX.value;
				length--;

				_slidingWindow[_slidingWindowIndex] = CTX.value;
				_slidingWindowIndex =
				    (_slidingWindowIndex + 1) &
				    _slidingWindowMask;

				CTX.state = AWAIT_CODE;
				CTX.treeIter = CTX.litLenTree;
			}

			if OF_UNLIKELY (CTX.state == AWAIT_LENGTH_EXTRA_BITS) {

				if OF_UNLIKELY (!tryReadBits(self, &bits,
				    CTX.extraBits))
					return bytesWritten;

				CTX.length += bits;

				CTX.state = AWAIT_DISTANCE;
				CTX.treeIter = CTX.distTree;
			}

			/* Distance of length distance pair */
			if (CTX.state == AWAIT_DISTANCE) {
				if OF_UNLIKELY (!of_huffman_tree_walk(self,
				    tryReadBits, &CTX.treeIter, &value))
					return bytesWritten;

				if OF_UNLIKELY (value >= numDistanceCodes)
					@throw [OFInvalidFormatException
					    exception];

				CTX.distance = distanceCodes[value];
				extraBits = distanceExtraBits[value];

				if (extraBits > 0) {
					if OF_UNLIKELY (!tryReadBits(self,
					    &bits, extraBits)) {

						CTX.state =
						    AWAIT_DISTANCE_EXTRA_BITS;

						CTX.extraBits = extraBits;
						return bytesWritten;
					}

					CTX.distance += bits;
				}

				CTX.state = PROCESS_PAIR;
			} else if (CTX.state == AWAIT_DISTANCE_EXTRA_BITS) {

				if OF_UNLIKELY (!tryReadBits(self, &bits,
				    CTX.extraBits))
					return bytesWritten;

				CTX.distance += bits;

				CTX.state = PROCESS_PAIR;
			}

			/* Length distance pair */
			if (CTX.state == PROCESS_PAIR) {
				for (uint_fast16_t j = 0; j < CTX.length; j++) {
					uint16_t idx;

					if OF_UNLIKELY (length == 0) {
						CTX.length -= j;
						return bytesWritten;
					}







|


|

|



|






|
|




|




|











|



|
>






|




|
|













>
|
<
>







|
|
>






|



|







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

			for (j = 0; j < count; j++)
				CTX.lengths[i++] = value;

			CTX.value = 0xFF;
		}

		OFHuffmanTreeFree(CTX.codeLenTree);
		CTX.codeLenTree = NULL;

		CTX.litLenTree = OFHuffmanTreeNew(CTX.lengths,
		    CTX.litLenCodesCount + 257);
		CTX.distTree = OFHuffmanTreeNew(
		    CTX.lengths + CTX.litLenCodesCount + 257,
		    CTX.distCodesCount + 1);

		OFFreeMemory(CTX.lengths);

		/*
		 * litLenTree and distTree are at the same location in
		 * _context.huffman and _context.huffmanTree, thus no need to
		 * set them.
		 */
		_state = stateHuffmanBlock;
		_context.huffman.state = huffmanStateAwaitCode;
		_context.huffman.treeIter = CTX.litLenTree;

		goto start;
#undef CTX
	case stateHuffmanBlock:
#define CTX _context.huffman
		for (;;) {
			uint8_t extraBits, lengthCodeIndex;

			if OF_UNLIKELY (CTX.state == huffmanStateWriteValue) {
				if OF_UNLIKELY (length == 0)
					return bytesWritten;

				buffer[bytesWritten++] = CTX.value;
				length--;

				_slidingWindow[_slidingWindowIndex] = CTX.value;
				_slidingWindowIndex =
				    (_slidingWindowIndex + 1) &
				    _slidingWindowMask;

				CTX.state = huffmanStateAwaitCode;
				CTX.treeIter = CTX.litLenTree;
			}

			if OF_UNLIKELY (CTX.state ==
			    huffmanStateAwaitLengthExtraBits) {
				if OF_UNLIKELY (!tryReadBits(self, &bits,
				    CTX.extraBits))
					return bytesWritten;

				CTX.length += bits;

				CTX.state = huffmanStateAwaitDistance;
				CTX.treeIter = CTX.distTree;
			}

			/* Distance of length distance pair */
			if (CTX.state == huffmanStateAwaitDistance) {
				if OF_UNLIKELY (!OFHuffmanTreeWalk(self,
				    tryReadBits, &CTX.treeIter, &value))
					return bytesWritten;

				if OF_UNLIKELY (value >= numDistanceCodes)
					@throw [OFInvalidFormatException
					    exception];

				CTX.distance = distanceCodes[value];
				extraBits = distanceExtraBits[value];

				if (extraBits > 0) {
					if OF_UNLIKELY (!tryReadBits(self,
					    &bits, extraBits)) {
#define HSADEB huffmanStateAwaitDistanceExtraBits
						CTX.state = HSADEB;

#undef HSADEB
						CTX.extraBits = extraBits;
						return bytesWritten;
					}

					CTX.distance += bits;
				}

				CTX.state = huffmanStateProcessPair;
			} else if (CTX.state ==
			    huffmanStateAwaitDistanceExtraBits) {
				if OF_UNLIKELY (!tryReadBits(self, &bits,
				    CTX.extraBits))
					return bytesWritten;

				CTX.distance += bits;

				CTX.state = huffmanStateProcessPair;
			}

			/* Length distance pair */
			if (CTX.state == huffmanStateProcessPair) {
				for (uint_fast16_t j = 0; j < CTX.length; j++) {
					uint16_t idx;

					if OF_UNLIKELY (length == 0) {
						CTX.length -= j;
						return bytesWritten;
					}
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
					_slidingWindow[_slidingWindowIndex] =
					    value;
					_slidingWindowIndex =
					    (_slidingWindowIndex + 1) &
					    _slidingWindowMask;
				}

				CTX.state = AWAIT_CODE;
				CTX.treeIter = CTX.litLenTree;
			}

			if OF_UNLIKELY (!of_huffman_tree_walk(self, tryReadBits,
			    &CTX.treeIter, &value))
				return bytesWritten;

			/* End of block */
			if OF_UNLIKELY (value == 256) {
				if (CTX.litLenTree != fixedLitLenTree)
					of_huffman_tree_release(CTX.litLenTree);
				if (CTX.distTree != fixedDistTree)
					of_huffman_tree_release(CTX.distTree);

				_state = BLOCK_HEADER;
				goto start;
			}

			/* Literal byte */
			if OF_LIKELY (value < 256) {
				if OF_UNLIKELY (length == 0) {
					CTX.state = WRITE_VALUE;
					CTX.value = value;
					return bytesWritten;
				}

				buffer[bytesWritten++] = value;
				length--;








|



|






|

|

|






|







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
					_slidingWindow[_slidingWindowIndex] =
					    value;
					_slidingWindowIndex =
					    (_slidingWindowIndex + 1) &
					    _slidingWindowMask;
				}

				CTX.state = huffmanStateAwaitCode;
				CTX.treeIter = CTX.litLenTree;
			}

			if OF_UNLIKELY (!OFHuffmanTreeWalk(self, tryReadBits,
			    &CTX.treeIter, &value))
				return bytesWritten;

			/* End of block */
			if OF_UNLIKELY (value == 256) {
				if (CTX.litLenTree != fixedLitLenTree)
					OFHuffmanTreeFree(CTX.litLenTree);
				if (CTX.distTree != fixedDistTree)
					OFHuffmanTreeFree(CTX.distTree);

				_state = stateBlockHeader;
				goto start;
			}

			/* Literal byte */
			if OF_LIKELY (value < 256) {
				if OF_UNLIKELY (length == 0) {
					CTX.state = huffmanStateWriteValue;
					CTX.value = value;
					return bytesWritten;
				}

				buffer[bytesWritten++] = value;
				length--;

636
637
638
639
640
641
642
643

644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
			CTX.length = lengthCodes[lengthCodeIndex] + 3;
			extraBits = lengthExtraBits[lengthCodeIndex];

			if (extraBits > 0) {
				if OF_UNLIKELY (!tryReadBits(self, &bits,
				    extraBits)) {
					CTX.extraBits = extraBits;
					CTX.state = AWAIT_LENGTH_EXTRA_BITS;

					return bytesWritten;
				}

				CTX.length += bits;
			}

			CTX.treeIter = CTX.distTree;
			CTX.state = AWAIT_DISTANCE;
		}

		break;
#undef CTX
	}

	OF_UNREACHABLE







|
>







|







637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
			CTX.length = lengthCodes[lengthCodeIndex] + 3;
			extraBits = lengthExtraBits[lengthCodeIndex];

			if (extraBits > 0) {
				if OF_UNLIKELY (!tryReadBits(self, &bits,
				    extraBits)) {
					CTX.extraBits = extraBits;
					CTX.state =
					    huffmanStateAwaitLengthExtraBits;
					return bytesWritten;
				}

				CTX.length += bits;
			}

			CTX.treeIter = CTX.distTree;
			CTX.state = huffmanStateAwaitDistance;
		}

		break;
#undef CTX
	}

	OF_UNREACHABLE

Modified src/OFInvertedCharacterSet.h from [08af99fb50] to [d4b8559d77].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCharacterSet.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFInvertedCharacterSet: OFCharacterSet
{
	OFCharacterSet *_characterSet;
	bool (*_characterIsMember)(id, SEL, of_unichar_t);
}

- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet;
@end

OF_ASSUME_NONNULL_END

<
<
|




















|






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


 * 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 "OFCharacterSet.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFInvertedCharacterSet: OFCharacterSet
{
	OFCharacterSet *_characterSet;
	bool (*_characterIsMember)(id, SEL, OFUnichar);
}

- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet;
@end

OF_ASSUME_NONNULL_END

Modified src/OFInvertedCharacterSet.m from [33cd491c87] to [1e78ebb0a4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet
{
	self = [super init];

	@try {
		_characterSet = [characterSet retain];
		_characterIsMember = (bool (*)(id, SEL, of_unichar_t))
		    [_characterSet methodForSelector:
		    @selector(characterIsMember:)];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_characterSet release];

	[super dealloc];
}

- (bool)characterIsMember: (of_unichar_t)character
{
	return !_characterIsMember(_characterSet, @selector(characterIsMember:),
	    character);
}

- (OFCharacterSet *)invertedSet
{
	return [[_characterSet retain] autorelease];
}
@end







|

















|










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

- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet
{
	self = [super init];

	@try {
		_characterSet = [characterSet retain];
		_characterIsMember = (bool (*)(id, SEL, OFUnichar))
		    [_characterSet methodForSelector:
		    @selector(characterIsMember:)];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_characterSet release];

	[super dealloc];
}

- (bool)characterIsMember: (OFUnichar)character
{
	return !_characterIsMember(_characterSet, @selector(characterIsMember:),
	    character);
}

- (OFCharacterSet *)invertedSet
{
	return [[_characterSet retain] autorelease];
}
@end

Modified src/OFInvocation.h from [86507cef33] to [324ba0dca4].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

OF_ASSUME_NONNULL_BEGIN

#ifdef OF_APPLE_RUNTIME
# ifdef OF_X86_64
#  define OF_INVOCATION_CAN_INVOKE
# endif
#else
# ifdef OF_ELF
#  ifdef OF_X86_64
#   define OF_INVOCATION_CAN_INVOKE
#  endif
# endif
#endif

@class OFMethodSignature;
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableData;

/**
 * @class OFInvocation OFInvocation.h ObjFW/OFInvocation.h
 *

<
<
|

















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







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


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

OF_ASSUME_NONNULL_BEGIN













@class OFMethodSignature;
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableData;

/**
 * @class OFInvocation OFInvocation.h ObjFW/OFInvocation.h
 *
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

/**
 * @brief Sets the argument for the specified index.
 *
 * @param buffer The buffer in which the argument is stored
 * @param index The index of the argument to set
 */
- (void)setArgument: (const void *)buffer
	    atIndex: (size_t)index;

/**
 * @brief Gets the argument for the specified index.
 *
 * @param buffer The buffer in which the argument is stored
 * @param index The index of the argument to get
 */
- (void)getArgument: (void *)buffer
	    atIndex: (size_t)index;

/**
 * @brief Sets the return value.
 *
 * @param buffer The buffer in which the return value is stored
 */
- (void)setReturnValue: (const void *)buffer;

/**
 * @brief Gets the return value.
 *
 * @param buffer The buffer in which the return value is stored
 */
- (void)getReturnValue: (void *)buffer;

#ifdef OF_INVOCATION_CAN_INVOKE
/**
 * @brief Invokes the method.
 */
- (void)invoke;
#endif
@end

OF_ASSUME_NONNULL_END







|
<







|
<














<
<
<
<
<
<
<



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

/**
 * @brief Sets the argument for the specified index.
 *
 * @param buffer The buffer in which the argument is stored
 * @param index The index of the argument to set
 */
- (void)setArgument: (const void *)buffer atIndex: (size_t)index;


/**
 * @brief Gets the argument for the specified index.
 *
 * @param buffer The buffer in which the argument is stored
 * @param index The index of the argument to get
 */
- (void)getArgument: (void *)buffer atIndex: (size_t)index;


/**
 * @brief Sets the return value.
 *
 * @param buffer The buffer in which the return value is stored
 */
- (void)setReturnValue: (const void *)buffer;

/**
 * @brief Gets the return value.
 *
 * @param buffer The buffer in which the return value is stored
 */
- (void)getReturnValue: (void *)buffer;







@end

OF_ASSUME_NONNULL_END

Modified src/OFInvocation.m from [f9deea1e14] to [5937b080ad].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <string.h>

#import "OFInvocation.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFMethodSignature.h"

#ifdef OF_INVOCATION_CAN_INVOKE
extern void of_invocation_invoke(OFInvocation *);
#endif

@implementation OFInvocation
@synthesize methodSignature = _methodSignature;

+ (instancetype)invocationWithMethodSignature: (OFMethodSignature *)signature
{
	return [[[self alloc] initWithMethodSignature: signature] autorelease];
}







<
<
<
<







18
19
20
21
22
23
24




25
26
27
28
29
30
31
#include <string.h>

#import "OFInvocation.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFMethodSignature.h"





@implementation OFInvocation
@synthesize methodSignature = _methodSignature;

+ (instancetype)invocationWithMethodSignature: (OFMethodSignature *)signature
{
	return [[[self alloc] initWithMethodSignature: signature] autorelease];
}
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
		_arguments = [[OFMutableArray alloc] init];

		for (size_t i = 0; i < numberOfArguments; i++) {
			OFMutableData *data;

			typeEncoding = [_methodSignature
			    argumentTypeAtIndex: i];
			typeSize = of_sizeof_type_encoding(typeEncoding);

			data = [OFMutableData dataWithItemSize: typeSize
						      capacity: 1];
			[data increaseCountBy: 1];
			[_arguments addObject: data];
		}

		typeEncoding = _methodSignature.methodReturnType;
		typeSize = of_sizeof_type_encoding(typeEncoding);

		if (typeSize > 0) {
			_returnValue = [[OFMutableData alloc]
			    initWithItemSize: typeSize
				    capacity: 1];
			[_returnValue increaseCountBy: 1];
		}







|








|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
		_arguments = [[OFMutableArray alloc] init];

		for (size_t i = 0; i < numberOfArguments; i++) {
			OFMutableData *data;

			typeEncoding = [_methodSignature
			    argumentTypeAtIndex: i];
			typeSize = OFSizeOfTypeEncoding(typeEncoding);

			data = [OFMutableData dataWithItemSize: typeSize
						      capacity: 1];
			[data increaseCountBy: 1];
			[_arguments addObject: data];
		}

		typeEncoding = _methodSignature.methodReturnType;
		typeSize = OFSizeOfTypeEncoding(typeEncoding);

		if (typeSize > 0) {
			_returnValue = [[OFMutableData alloc]
			    initWithItemSize: typeSize
				    capacity: 1];
			[_returnValue increaseCountBy: 1];
		}
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
	[_methodSignature release];
	[_arguments release];
	[_returnValue release];

	[super dealloc];
}

- (void)setArgument: (const void *)buffer
	    atIndex: (size_t)idx
{
	OFMutableData *data = [_arguments objectAtIndex: idx];

	memcpy(data.mutableItems, buffer, data.itemSize);
}

- (void)getArgument: (void *)buffer
	    atIndex: (size_t)idx
{
	OFData *data = [_arguments objectAtIndex: idx];

	memcpy(buffer, data.items, data.itemSize);
}

- (void)setReturnValue: (const void *)buffer
{
	memcpy(_returnValue.mutableItems, buffer, _returnValue.itemSize);
}

- (void)getReturnValue: (void *)buffer
{
	memcpy(buffer, _returnValue.items, _returnValue.itemSize);
}

#ifdef OF_INVOCATION_CAN_INVOKE
- (void)invoke
{
	of_invocation_invoke(self);
}
#endif
@end







|
<


<



|
<


<












<
<
<
<
<
<
<

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
	[_methodSignature release];
	[_arguments release];
	[_returnValue release];

	[super dealloc];
}

- (void)setArgument: (const void *)buffer atIndex: (size_t)idx

{
	OFMutableData *data = [_arguments objectAtIndex: idx];

	memcpy(data.mutableItems, buffer, data.itemSize);
}

- (void)getArgument: (void *)buffer atIndex: (size_t)idx

{
	OFData *data = [_arguments objectAtIndex: idx];

	memcpy(buffer, data.items, data.itemSize);
}

- (void)setReturnValue: (const void *)buffer
{
	memcpy(_returnValue.mutableItems, buffer, _returnValue.itemSize);
}

- (void)getReturnValue: (void *)buffer
{
	memcpy(buffer, _returnValue.items, _returnValue.itemSize);
}







@end

Modified src/OFJSONRepresentation.h from [eef7794ab0] to [2bd85161f3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

@class OFString;

OF_ASSUME_NONNULL_BEGIN




enum {

	OF_JSON_REPRESENTATION_PRETTY	  = 0x01,

	OF_JSON_REPRESENTATION_JSON5	  = 0x02,
	OF_JSON_REPRESENTATION_IDENTIFIER = 0x10
};


/**
 * @protocol OFJSONRepresentation
 *	     OFJSONRepresentation.h ObjFW/OFJSONRepresentation.h
 *
 * @brief A protocol implemented by classes that support encoding to a JSON
 *	  representation.

<
<
|



















>
>
>
|
>
|
>
|
|
<
>







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


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

@class OFString;

OF_ASSUME_NONNULL_BEGIN

/**
 * @brief Options to change the behavior when creating a JSON representation.
 */
typedef enum {
	/** Optimize for readability */
	OFJSONRepresentationOptionPretty       = 0x01,
	/** Generate JSON5 */
	OFJSONRepresentationOptionJSON5	       = 0x02,
	OFJSONRepresentationOptionIsIdentifier = 0x10

} OFJSONRepresentationOptions;

/**
 * @protocol OFJSONRepresentation
 *	     OFJSONRepresentation.h ObjFW/OFJSONRepresentation.h
 *
 * @brief A protocol implemented by classes that support encoding to a JSON
 *	  representation.
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
 * @brief The JSON representation of the object as a string.
 */
@property (readonly, nonatomic) OFString *JSONRepresentation;

/**
 * @brief Returns the JSON representation of the object as a string.
 *
 * @param options The options to use when creating a JSON representation.@n
 *		  Possible values are:
 *		  Value                           | Description
 *		  --------------------------------|-------------------------
 *		  `OF_JSON_REPRESENTATION_PRETTY` | Optimize for readability
 *		  `OF_JSON_REPRESENTATION_JSON5`  | Generate JSON5
 *
 * @return The JSON representation of the object as a string
 */
- (OFString *)JSONRepresentationWithOptions: (int)options;

@end

OF_ASSUME_NONNULL_END







|
<
<
<
<
<
<


|
>



46
47
48
49
50
51
52
53






54
55
56
57
58
59
60
 * @brief The JSON representation of the object as a string.
 */
@property (readonly, nonatomic) OFString *JSONRepresentation;

/**
 * @brief Returns the JSON representation of the object as a string.
 *
 * @param options The options to use when creating a JSON representation






 * @return The JSON representation of the object as a string
 */
- (OFString *)JSONRepresentationWithOptions:
    (OFJSONRepresentationOptions)options;
@end

OF_ASSUME_NONNULL_END

Modified src/OFKernelEventObserver.h from [74da8f3153] to [21aebcf8a0].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#ifdef OF_HAVE_SOCKETS
# import "socket.h"
#endif

#ifdef OF_AMIGAOS
# include <exec/types.h>
# include <exec/tasks.h>
#endif


<
<
|














<

|







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


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

#ifdef OF_HAVE_SOCKETS
# import "OFSocket.h"
#endif

#ifdef OF_AMIGAOS
# include <exec/types.h>
# include <exec/tasks.h>
#endif

120
121
122
123
124
125
126
127
128
129
130
131


132
133
134
135
136
137
138
139
140
@interface OFKernelEventObserver: OFObject
{
	OFMutableArray OF_GENERIC(id <OFReadyForReadingObserving>)
	    *_readObjects;
	OFMutableArray OF_GENERIC(id <OFReadyForWritingObserving>)
	    *_writeObjects;
	id <OFKernelEventObserverDelegate> _Nullable _delegate;
#if defined(OF_HAVE_PIPE)
	int _cancelFD[2];
#elif defined(OF_AMIGAOS)
	struct Task *_waitingTask;
	ULONG _cancelSignal;


#else
	of_socket_t _cancelFD[2];
	struct sockaddr_in _cancelAddr;
#endif
#ifdef OF_AMIGAOS
	ULONG _execSignalMask;
#endif
	OF_RESERVE_IVARS(OFKernelEventObserver, 4)
}







|
<
<


>
>

|







117
118
119
120
121
122
123
124


125
126
127
128
129
130
131
132
133
134
135
136
137
@interface OFKernelEventObserver: OFObject
{
	OFMutableArray OF_GENERIC(id <OFReadyForReadingObserving>)
	    *_readObjects;
	OFMutableArray OF_GENERIC(id <OFReadyForWritingObserving>)
	    *_writeObjects;
	id <OFKernelEventObserverDelegate> _Nullable _delegate;
#if defined(OF_AMIGAOS)


	struct Task *_waitingTask;
	ULONG _cancelSignal;
#elif defined(OF_HAVE_PIPE)
	int _cancelFD[2];
#else
	OFSocketHandle _cancelFD[2];
	struct sockaddr_in _cancelAddr;
#endif
#ifdef OF_AMIGAOS
	ULONG _execSignalMask;
#endif
	OF_RESERVE_IVARS(OFKernelEventObserver, 4)
}
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

/**
 * @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: (of_time_interval_t)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
 */







|







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

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

Modified src/OFKernelEventObserver.m from [0cbb2bba17] to [8d008316fd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#include <errno.h>

#import "OFKernelEventObserver.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFStream.h"
#import "OFStream+Private.h"
#ifndef OF_HAVE_PIPE
# import "OFStreamSocket.h"
#endif

#ifdef HAVE_KQUEUE
# import "OFKqueueKernelEventObserver.h"
#endif
#ifdef HAVE_EPOLL
# import "OFEpollKernelEventObserver.h"
#endif
#ifdef HAVE_POLL
# import "OFPollKernelEventObserver.h"
#endif
#ifdef HAVE_SELECT
# import "OFSelectKernelEventObserver.h"
#endif








#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFOutOfRangeException.h"

#import "socket.h"
#import "socket_helpers.h"

#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif

enum {
	QUEUE_ADD = 0,
	QUEUE_REMOVE = 1,
	QUEUE_READ = 0,
	QUEUE_WRITE = 2
};
#define QUEUE_ACTION (QUEUE_ADD | QUEUE_REMOVE)

@implementation OFKernelEventObserver
@synthesize delegate = _delegate;
#ifdef OF_AMIGAOS
@synthesize execSignalMask = _execSignalMask;
#endif

+ (void)initialize
{
	if (self != [OFKernelEventObserver class])
		return;

	if (!of_socket_init())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)observer
{
	return [[[self alloc] init] autorelease];







<
<
|
|

<



<
<
<






>
>
>
>
>
>
>





<
<
<




<
<
<
<
<
<
<
<











|







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

#include <errno.h>

#import "OFKernelEventObserver.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDate.h"


#ifdef HAVE_EPOLL
# import "OFEpollKernelEventObserver.h"
#endif

#ifdef HAVE_KQUEUE
# import "OFKqueueKernelEventObserver.h"
#endif



#ifdef HAVE_POLL
# import "OFPollKernelEventObserver.h"
#endif
#ifdef HAVE_SELECT
# import "OFSelectKernelEventObserver.h"
#endif
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFStream.h"
#import "OFStream+Private.h"
#ifndef OF_HAVE_PIPE
# import "OFStreamSocket.h"
#endif

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFOutOfRangeException.h"




#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif









@implementation OFKernelEventObserver
@synthesize delegate = _delegate;
#ifdef OF_AMIGAOS
@synthesize execSignalMask = _execSignalMask;
#endif

+ (void)initialize
{
	if (self != [OFKernelEventObserver class])
		return;

	if (!OFSocketInit())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)observer
{
	return [[[self alloc] init] autorelease];
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
    !defined(OF_NINTENDO_3DS)
		socklen_t cancelAddrLen;
#endif

		_readObjects = [[OFMutableArray alloc] init];
		_writeObjects = [[OFMutableArray alloc] init];

#if defined(OF_HAVE_PIPE)
		if (pipe(_cancelFD))
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
#elif !defined(OF_AMIGAOS)
		_cancelFD[0] = _cancelFD[1] = socket(AF_INET, SOCK_DGRAM, 0);

		if (_cancelFD[0] == INVALID_SOCKET)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		_cancelAddr.sin_family = AF_INET;
		_cancelAddr.sin_port = 0;
		_cancelAddr.sin_addr.s_addr = inet_addr((void *)"127.0.0.1");
# ifdef OF_WII
		_cancelAddr.sin_len = 8;
# endif

# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
		if (bind(_cancelFD[0], (struct sockaddr *)&_cancelAddr,
		    sizeof(_cancelAddr)) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		cancelAddrLen = sizeof(_cancelAddr);
		if (of_getsockname(_cancelFD[0],
		    (struct sockaddr *)&_cancelAddr, &cancelAddrLen) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
# else
		for (;;) {
			uint16_t rnd = 0;
			int ret;

			while (rnd < 1024)
				rnd = (uint16_t)rand();

			_cancelAddr.sin_port = OF_BSWAP16_IF_LE(rnd);
			ret = bind(_cancelFD[0],
			    (struct sockaddr *)&_cancelAddr,
			    sizeof(_cancelAddr));

			if (ret == 0)
				break;

			if (of_socket_errno() != EADDRINUSE)
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];
		}
# endif
#endif
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
#if defined(OF_HAVE_PIPE)
	close(_cancelFD[0]);
	if (_cancelFD[1] != _cancelFD[0])
		close(_cancelFD[1]);
#elif !defined(OF_AMIGAOS)
	closesocket(_cancelFD[0]);
	if (_cancelFD[1] != _cancelFD[0])
		closesocket(_cancelFD[1]);







|






|

















|











|







|















|







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
    !defined(OF_NINTENDO_3DS)
		socklen_t cancelAddrLen;
#endif

		_readObjects = [[OFMutableArray alloc] init];
		_writeObjects = [[OFMutableArray alloc] init];

#if defined(OF_HAVE_PIPE) && !defined(OF_AMIGAOS)
		if (pipe(_cancelFD))
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
#elif !defined(OF_AMIGAOS)
		_cancelFD[0] = _cancelFD[1] = socket(AF_INET, SOCK_DGRAM, 0);

		if (_cancelFD[0] == OFInvalidSocketHandle)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		_cancelAddr.sin_family = AF_INET;
		_cancelAddr.sin_port = 0;
		_cancelAddr.sin_addr.s_addr = inet_addr((void *)"127.0.0.1");
# ifdef OF_WII
		_cancelAddr.sin_len = 8;
# endif

# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
		if (bind(_cancelFD[0], (struct sockaddr *)&_cancelAddr,
		    sizeof(_cancelAddr)) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		cancelAddrLen = sizeof(_cancelAddr);
		if (OFGetSockName(_cancelFD[0],
		    (struct sockaddr *)&_cancelAddr, &cancelAddrLen) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
# else
		for (;;) {
			uint16_t rnd = 0;
			int ret;

			while (rnd < 1024)
				rnd = (uint16_t)rand();

			_cancelAddr.sin_port = OFToBigEndian16(rnd);
			ret = bind(_cancelFD[0],
			    (struct sockaddr *)&_cancelAddr,
			    sizeof(_cancelAddr));

			if (ret == 0)
				break;

			if (OFSocketErrNo() != EADDRINUSE)
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];
		}
# endif
#endif
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
#if defined(OF_HAVE_PIPE) && !defined(OF_AMIGAOS)
	close(_cancelFD[0]);
	if (_cancelFD[1] != _cancelFD[0])
		close(_cancelFD[1]);
#elif !defined(OF_AMIGAOS)
	closesocket(_cancelFD[0]);
	if (_cancelFD[1] != _cancelFD[0])
		closesocket(_cancelFD[1]);
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
}

- (void)observe
{
	[self observeForTimeInterval: -1];
}

- (void)observeForTimeInterval: (of_time_interval_t)timeInterval
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)observeUntilDate: (OFDate *)date
{
	[self observeForTimeInterval: date.timeIntervalSinceNow];
}

- (void)cancel
{
#if defined(OF_HAVE_PIPE)
	OF_ENSURE(write(_cancelFD[1], "", 1) > 0);
#elif defined(OF_AMIGAOS)
	Forbid();

	if (_waitingTask != NULL) {
		Signal(_waitingTask, (1ul << _cancelSignal));
		_waitingTask = NULL;
	}

	Permit();


#elif defined(OF_WII)
	OF_ENSURE(sendto(_cancelFD[1], "", 1, 0,
	    (struct sockaddr *)&_cancelAddr, 8) > 0);
#else
	OF_ENSURE(sendto(_cancelFD[1], (void *)"", 1, 0,
	    (struct sockaddr *)&_cancelAddr, sizeof(_cancelAddr)) > 0);
#endif
}
@end







|











|
<
<








>
>

|


|




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
}

- (void)observe
{
	[self observeForTimeInterval: -1];
}

- (void)observeForTimeInterval: (OFTimeInterval)timeInterval
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)observeUntilDate: (OFDate *)date
{
	[self observeForTimeInterval: date.timeIntervalSinceNow];
}

- (void)cancel
{
#if defined(OF_AMIGAOS)


	Forbid();

	if (_waitingTask != NULL) {
		Signal(_waitingTask, (1ul << _cancelSignal));
		_waitingTask = NULL;
	}

	Permit();
#elif defined(OF_HAVE_PIPE)
	OFEnsure(write(_cancelFD[1], "", 1) > 0);
#elif defined(OF_WII)
	OFEnsure(sendto(_cancelFD[1], "", 1, 0,
	    (struct sockaddr *)&_cancelAddr, 8) > 0);
#else
	OFEnsure(sendto(_cancelFD[1], (void *)"", 1, 0,
	    (struct sockaddr *)&_cancelAddr, sizeof(_cancelAddr)) > 0);
#endif
}
@end

Modified src/OFKeyValueCoding.h from [fc58ab9799] to [06cc2c1de6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

/**
 * @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`.
 *
 * By default, this throws an @ref OFInvalidArgumentException.
 *
 * @param key The key for which the value `nil` was specified
 */
- (void)setNilValueForKey: (OFString *)key;
@end

OF_ASSUME_NONNULL_END







|
<







|
<










|
<













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

/**
 * @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`.
 *
 * By default, this throws an @ref OFInvalidArgumentException.
 *
 * @param key The key for which the value `nil` was specified
 */
- (void)setNilValueForKey: (OFString *)key;
@end

OF_ASSUME_NONNULL_END

Modified src/OFKqueueKernelEventObserver.h from [bfbe37eedb] to [79cac968ba].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFKqueueKernelEventObserver.m from [56c8bac78d] to [bcb05813e9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#import "OFKqueueKernelEventObserver.h"
#import "OFArray.h"

#import "OFInitializationFailedException.h"
#import "OFObserveFailedException.h"
#import "OFOutOfRangeException.h"

#define EVENTLIST_SIZE 64

@implementation OFKqueueKernelEventObserver
- (instancetype)init
{
	self = [super init];

	@try {







|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#import "OFKqueueKernelEventObserver.h"
#import "OFArray.h"

#import "OFInitializationFailedException.h"
#import "OFObserveFailedException.h"
#import "OFOutOfRangeException.h"

#define eventListSize 64

@implementation OFKqueueKernelEventObserver
- (instancetype)init
{
	self = [super init];

	@try {
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
{
	struct kevent event;

	memset(&event, 0, sizeof(event));
	event.ident = object.fileDescriptorForReading;
	event.filter = EVFILT_READ;
	event.flags = EV_ADD;


#ifndef OF_NETBSD

	event.udata = object;
#else
	event.udata = (intptr_t)object;
#endif

	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;


#ifndef OF_NETBSD

	event.udata = object;
#else
	event.udata = (intptr_t)object;
#endif

	if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: errno];

	[super addObjectForWriting: object];
}







>
>
|
>
|
<
<
<
















>
>
|
>
|
<
<
<







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
{
	struct kevent event;

	memset(&event, 0, sizeof(event));
	event.ident = object.fileDescriptorForReading;
	event.filter = EVFILT_READ;
	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 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];
}
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
	if (kevent(_kernelQueue, &event, 1, NULL, 0, NULL) != 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: errno];

	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (of_time_interval_t)timeInterval
{
	struct timespec timeout;
	struct kevent eventList[EVENTLIST_SIZE];
	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, EVENTLIST_SIZE,
	    (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);
			OF_ENSURE(read(_cancelFD[0], &buffer, 1) == 1);

			continue;
		}

		pool = objc_autoreleasePoolPush();

		switch (eventList[i].filter) {







|


|








|


















|







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
	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);
			OFEnsure(read(_cancelFD[0], &buffer, 1) == 1);

			continue;
		}

		pool = objc_autoreleasePoolPush();

		switch (eventList[i].filter) {

Modified src/OFLHAArchive.h from [75a10f411b] to [eaf15d177e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *
 * @brief A class for accessing and manipulating LHA files.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFLHAArchive: OFObject
{
	OFStream *_stream;
	enum {
		OF_LHA_ARCHIVE_MODE_READ,
		OF_LHA_ARCHIVE_MODE_WRITE,
		OF_LHA_ARCHIVE_MODE_APPEND
	} _mode;
	of_string_encoding_t _encoding;
	OFStream *_Nullable _lastReturnedStream;
}

/**
 * @brief The encoding to use for the archive. Defaults to ISO 8859-1.
 */
@property (nonatomic) of_string_encoding_t 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







<
<
<
<
|
|






|







27
28
29
30
31
32
33




34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 *
 * @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
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
 * @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.







|
<











|
<







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
 * @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.
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 *
 * @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.







|
<







99
100
101
102
103
104
105
106

107
108
109
110
111
112
113
 *
 * @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.

Modified src/OFLHAArchive.m from [d4b33ee3bc] to [5dc375ab00].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"



#import "OFLHAArchive.h"
#import "OFLHAArchiveEntry.h"
#import "OFLHAArchiveEntry+Private.h"

#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFLHADecompressingStream.h"
#import "OFStream.h"
#import "OFSeekableStream.h"
#import "OFString.h"

#import "crc16.h"

#import "OFChecksumMismatchException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFWriteFailedException.h"







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;
	of_string_encoding_t _encoding;
	OFSeekableStream *_stream;
	of_offset_t _headerOffset;
	uint32_t _bytesWritten;
	uint16_t _CRC16;
}

- (instancetype)of_initWithStream: (OFSeekableStream *)stream
			    entry: (OFLHAArchiveEntry *)entry
			 encoding: (of_string_encoding_t)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
{
	self = [super init];

	@try {
		_stream = [stream retain];

		if ([mode isEqual: @"r"])
			_mode = OF_LHA_ARCHIVE_MODE_READ;
		else if ([mode isEqual: @"w"])
			_mode = OF_LHA_ARCHIVE_MODE_WRITE;
		else if ([mode isEqual: @"a"])
			_mode = OF_LHA_ARCHIVE_MODE_APPEND;
		else
			@throw [OFInvalidArgumentException exception];

		if ((_mode == OF_LHA_ARCHIVE_MODE_WRITE ||
		    _mode == OF_LHA_ARCHIVE_MODE_APPEND) &&
		    ![_stream isKindOfClass: [OFSeekableStream class]])
			@throw [OFInvalidArgumentException exception];

		if (_mode == OF_LHA_ARCHIVE_MODE_APPEND)
			[(OFSeekableStream *)_stream seekToOffset: 0
							   whence: SEEK_END];

		_encoding = OF_STRING_ENCODING_ISO_8859_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

<
<
|














>
>




>








<
<







>
>
>
>
>
>




















|

|






|





|
<

|
<



|
<

|
<








|
<







|

|

|



|
<



|



|









|
<




|
<

|
<


|
<







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


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

{
	self = [super init];

	@try {
		_stream = [stream retain];

		if ([mode isEqual: @"r"])
			_mode = modeRead;
		else if ([mode isEqual: @"w"])
			_mode = modeWrite;
		else if ([mode isEqual: @"a"])
			_mode = modeAppend;
		else
			@throw [OFInvalidArgumentException exception];

		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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

- (OFLHAArchiveEntry *)nextEntry
{
	OFLHAArchiveEntry *entry;
	char header[21];
	size_t headerLen;

	if (_mode != OF_LHA_ARCHIVE_MODE_READ)
		@throw [OFInvalidArgumentException exception];

	[(OFLHAArchiveFileReadStream *)_lastReturnedStream of_skip];
	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */







|







157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

- (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. */
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
			entry: entry];

	return entry;
}

- (OFStream *)streamForReadingCurrentEntry
{
	if (_mode != OF_LHA_ARCHIVE_MODE_READ)
		@throw [OFInvalidArgumentException exception];

	if (_lastReturnedStream == nil)
		@throw [OFInvalidArgumentException exception];

	return [[(OFLHAArchiveFileReadStream *)_lastReturnedStream
	    retain] autorelease];
}

- (OFStream *)streamForWritingEntry: (OFLHAArchiveEntry *)entry
{
	OFString *compressionMethod;

	if (_mode != OF_LHA_ARCHIVE_MODE_WRITE &&
	    _mode != OF_LHA_ARCHIVE_MODE_APPEND)
		@throw [OFInvalidArgumentException exception];

	compressionMethod = entry.compressionMethod;

	if (![compressionMethod isEqual: @"-lh0-"] &&
	    ![compressionMethod isEqual: @"-lhd-"])
		@throw [OFNotImplementedException exceptionWithSelector: _cmd







|













|
<







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

		@throw [OFInvalidArgumentException exception];

	compressionMethod = entry.compressionMethod;

	if (![compressionMethod isEqual: @"-lh0-"] &&
	    ![compressionMethod isEqual: @"-lhd-"])
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
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
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	size_t ret;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	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 = of_crc16(_CRC16, buffer, ret);

	if (_toRead == 0) {
		_atEndOfStream = true;

		if (_CRC16 != _entry.CRC16) {
			OFString *actualChecksum = [OFString stringWithFormat:
			    @"%04" PRIX16, _CRC16];







|
<















|
<


|







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
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	size_t ret;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	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) {
		_atEndOfStream = true;

		if (_CRC16 != _entry.CRC16) {
			OFString *actualChecksum = [OFString stringWithFormat:
			    @"%04" PRIX16, _CRC16];
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
		toRead =
		    _entry.compressedSize - decompressingStream.bytesConsumed;

		stream = _stream;
	}

	if ([stream isKindOfClass: [OFSeekableStream class]] &&
	    (sizeof(of_offset_t) > 4 || toRead < INT32_MAX))
		[(OFSeekableStream *)stream seekToOffset: (of_offset_t)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;
}








|
|









|
<







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

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
	[super close];
}
@end

@implementation OFLHAArchiveFileWriteStream
- (instancetype)of_initWithStream: (OFSeekableStream *)stream
			    entry: (OFLHAArchiveEntry *)entry
			 encoding: (of_string_encoding_t)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];
	} @catch (id e) {







|







|
<
|
<







435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450

451

452
453
454
455
456
457
458
	[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];
	} @catch (id e) {
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
		[self close];

	[_entry release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	uint32_t bytesWritten;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (UINT32_MAX - _bytesWritten < length)
		@throw [OFOutOfRangeException exception];

	@try {
		bytesWritten = (uint32_t)[_stream writeBuffer: buffer
						       length: length];
	} @catch (OFWriteFailedException *e) {


		_bytesWritten += e.bytesWritten;
		_CRC16 = of_crc16(_CRC16, buffer, e.bytesWritten);




		@throw e;
	}

	_bytesWritten += (uint32_t)bytesWritten;
	_CRC16 = of_crc16(_CRC16, buffer, bytesWritten);

	return bytesWritten;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _stream.atEndOfStream;
}

- (int)fileDescriptorForWriting
{
	return ((id <OFReadyForWritingObserving>)_stream)
	    .fileDescriptorForWriting;
}

- (void)close
{
	of_offset_t 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







|
<

<
<







|
<

>
>
|
|
>
>
>




|
|

|


















|








|
<
|
<
|
<
|
<







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
		[self close];

	[_entry release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{


	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (UINT32_MAX - _bytesWritten < length)
		@throw [OFOutOfRangeException exception];

	@try {
		[_stream writeBuffer: buffer length: length];

	} @catch (OFWriteFailedException *e) {
		OFEnsure(e.bytesWritten <= length);

		_bytesWritten += (uint32_t)e.bytesWritten;
		_CRC16 = OFCRC16(_CRC16, buffer, e.bytesWritten);

		if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN)
			return e.bytesWritten;

		@throw e;
	}

	_bytesWritten += (uint32_t)length;
	_CRC16 = OFCRC16(_CRC16, buffer, length);

	return length;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _stream.atEndOfStream;
}

- (int)fileDescriptorForWriting
{
	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

Modified src/OFLHAArchiveEntry+Private.h from [085a1438c0] to [dc492f96c5].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25
26
27
28
29
30
31
32

OF_ASSUME_NONNULL_BEGIN

OF_DIRECT_MEMBERS
@interface OFLHAArchiveEntry ()
- (instancetype)of_initWithHeader: (char [_Nonnull 21])header
			   stream: (OFStream *)stream
			 encoding: (of_string_encoding_t)encoding
    OF_METHOD_FAMILY(init);
- (void)of_writeToStream: (OFStream *)stream
		encoding: (of_string_encoding_t)encoding;
@end

OF_ASSUME_NONNULL_END







|


|



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

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

Modified src/OFLHAArchiveEntry.h from [091b8c9db3] to [e47bb8e429].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFLHAArchiveEntry.m from [61acef2124] to [45c1be7d3b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "OFLHAArchiveEntry.h"
#import "OFLHAArchiveEntry+Private.h"
#import "OFArray.h"

#import "OFData.h"
#import "OFDate.h"
#import "OFNumber.h"
#import "OFStream.h"
#import "OFString.h"

#import "crc16.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
#import "OFUnsupportedVersionException.h"

@implementation OFLHAArchiveEntry
static OFDate *

<
<
|




















>






<
<







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


 * 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 <string.h>

#import "OFLHAArchiveEntry.h"
#import "OFLHAArchiveEntry+Private.h"
#import "OFArray.h"
#import "OFCRC16.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFNumber.h"
#import "OFStream.h"
#import "OFString.h"



#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
#import "OFUnsupportedVersionException.h"

@implementation OFLHAArchiveEntry
static OFDate *
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

	return [OFDate dateWithLocalDateString: dateString
					format: @"%Y-%m-%d %H:%M:%S"];
}

static void
parseFileNameExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding)
{
	[entry->_fileName release];
	entry->_fileName = nil;

	entry->_fileName = [[OFString alloc]
	    initWithCString: (char *)extension.items + 1
		   encoding: encoding
		     length: [extension count] - 1];
}

static void
parseDirectoryNameExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding)
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *data = [[extension mutableCopy] autorelease];
	char *items = data.mutableItems;
	size_t count = data.count;
	OFMutableString *directoryName;








|












|







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

	return [OFDate dateWithLocalDateString: dateString
					format: @"%Y-%m-%d %H:%M:%S"];
}

static void
parseFileNameExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding)
{
	[entry->_fileName release];
	entry->_fileName = nil;

	entry->_fileName = [[OFString alloc]
	    initWithCString: (char *)extension.items + 1
		   encoding: encoding
		     length: [extension count] - 1];
}

static void
parseDirectoryNameExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding)
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *data = [[extension mutableCopy] autorelease];
	char *items = data.mutableItems;
	size_t count = data.count;
	OFMutableString *directoryName;

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
	entry->_directoryName = [directoryName copy];

	objc_autoreleasePoolPop(pool);
}

static void
parseCommentExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding)
{
	[entry->_fileComment release];
	entry->_fileComment = nil;

	entry->_fileComment = [[OFString alloc]
	    initWithCString: (char *)extension.items + 1
		   encoding: encoding
		     length: extension.count - 1];
}

static void
parsePermissionsExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding)
{
	uint16_t mode;

	if (extension.count != 3)
		@throw [OFInvalidFormatException exception];

	memcpy(&mode, (char *)extension.items + 1, 2);
	mode = OF_BSWAP16_IF_BE(mode);

	[entry->_mode release];
	entry->_mode = nil;

	entry->_mode = [[OFNumber alloc] initWithUnsignedShort: mode];
}

static void
parseGIDUIDExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding)
{
	uint16_t UID, GID;

	if (extension.count != 5)
		@throw [OFInvalidFormatException exception];

	memcpy(&GID, (char *)extension.items + 1, 2);
	GID = OF_BSWAP16_IF_BE(GID);

	memcpy(&UID, (char *)extension.items + 3, 2);
	UID = OF_BSWAP16_IF_BE(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,
    of_string_encoding_t 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,
    of_string_encoding_t 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,
    of_string_encoding_t encoding)
{
	uint32_t modificationDate;

	if (extension.count != 5)
		@throw [OFInvalidFormatException exception];

	memcpy(&modificationDate, (char *)extension.items + 1, 4);
	modificationDate = OF_BSWAP32_IF_BE(modificationDate);

	[entry->_modificationDate release];
	entry->_modificationDate = nil;

	entry->_modificationDate = [[OFDate alloc]
	    initWithTimeIntervalSince1970: modificationDate];
}

static bool
parseExtension(OFLHAArchiveEntry *entry, OFData *extension,
    of_string_encoding_t encoding, bool allowFileName)
{
	void (*function)(OFLHAArchiveEntry *, OFData *, of_string_encoding_t) =
	    NULL;

	switch (*(char *)[extension itemAtIndex: 0]) {
	case 0x01:
		if (allowFileName)
			function = parseFileNameExtension;
		break;







|












|







|









|







|


|













|












|












|







|










|

|







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
	entry->_directoryName = [directoryName copy];

	objc_autoreleasePoolPop(pool);
}

static void
parseCommentExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding)
{
	[entry->_fileComment release];
	entry->_fileComment = nil;

	entry->_fileComment = [[OFString alloc]
	    initWithCString: (char *)extension.items + 1
		   encoding: encoding
		     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,
    OFStringEncoding encoding)
{
	uint32_t modificationDate;

	if (extension.count != 5)
		@throw [OFInvalidFormatException exception];

	memcpy(&modificationDate, (char *)extension.items + 1, 4);
	modificationDate = OFFromLittleEndian32(modificationDate);

	[entry->_modificationDate release];
	entry->_modificationDate = nil;

	entry->_modificationDate = [[OFDate alloc]
	    initWithTimeIntervalSince1970: modificationDate];
}

static bool
parseExtension(OFLHAArchiveEntry *entry, OFData *extension,
    OFStringEncoding encoding, bool allowFileName)
{
	void (*function)(OFLHAArchiveEntry *, OFData *, OFStringEncoding) =
	    NULL;

	switch (*(char *)[extension itemAtIndex: 0]) {
	case 0x01:
		if (allowFileName)
			function = parseFileNameExtension;
		break;
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

	function(entry, extension, encoding);
	return true;
}

static void
readExtensions(OFLHAArchiveEntry *entry, OFStream *stream,
    of_string_encoding_t encoding, bool allowFileName)
{
	uint16_t size;

	while ((size = [stream readLittleEndianInt16]) > 0) {
		OFData *extension;

		if (size < 2)







|







237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

	function(entry, extension, encoding);
	return true;
}

static void
readExtensions(OFLHAArchiveEntry *entry, OFStream *stream,
    OFStringEncoding encoding, bool allowFileName)
{
	uint16_t size;

	while ((size = [stream readLittleEndianInt16]) > 0) {
		OFData *extension;

		if (size < 2)
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

			entry->_compressedSize -= size;
		}
	}
}

static void
getFileNameAndDirectoryName(OFLHAArchiveEntry *entry,
    of_string_encoding_t encoding,
    const char **fileName, size_t *fileNameLength,
    const char **directoryName, size_t *directoryNameLength)
{
	OFMutableData *data;
	char *cString;
	size_t length;
	size_t pos;







|
<







262
263
264
265
266
267
268
269

270
271
272
273
274
275
276

			entry->_compressedSize -= size;
		}
	}
}

static void
getFileNameAndDirectoryName(OFLHAArchiveEntry *entry, OFStringEncoding encoding,

    const char **fileName, size_t *fileNameLength,
    const char **directoryName, size_t *directoryNameLength)
{
	OFMutableData *data;
	char *cString;
	size_t length;
	size_t pos;
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
	}

	return self;
}

- (instancetype)of_initWithHeader: (char [21])header
			   stream: (OFStream *)stream
			 encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		uint32_t date;

		_compressionMethod = [[OFString alloc]
		    initWithCString: header + 2
			   encoding: OF_STRING_ENCODING_ASCII
			     length: 5];

		memcpy(&_compressedSize, header + 7, 4);
		_compressedSize = OF_BSWAP32_IF_BE(_compressedSize);

		memcpy(&_uncompressedSize, header + 11, 4);
		_uncompressedSize = OF_BSWAP32_IF_BE(_uncompressedSize);

		memcpy(&date, header + 15, 4);
		date = OF_BSWAP32_IF_BE(date);

		_headerLevel = header[20];
		_extensions = [[OFMutableArray alloc] init];

		switch (_headerLevel) {
		case 0:
		case 1:;







|








|



|


|


|







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
	}

	return self;
}

- (instancetype)of_initWithHeader: (char [21])header
			   stream: (OFStream *)stream
			 encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		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:;
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

- (OFArray OF_GENERIC(OFData *) *)extensions
{
	return _extensions;
}

- (void)of_writeToStream: (OFStream *)stream
		encoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *data = [OFMutableData dataWithCapacity: 24];
	const char *fileName, *directoryName;
	size_t fileNameLength, directoryNameLength;
	uint16_t tmp16;
	uint32_t tmp32;
	size_t headerSize;

	if ([_compressionMethod cStringLengthWithEncoding:
	    OF_STRING_ENCODING_ASCII] != 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: OF_STRING_ENCODING_ASCII]
		 count: 5];

	tmp32 = OF_BSWAP32_IF_BE(_compressedSize);
	[data addItems: &tmp32
		 count: sizeof(tmp32)];

	tmp32 = OF_BSWAP32_IF_BE(_uncompressedSize);
	[data addItems: &tmp32
		 count: sizeof(tmp32)];

	tmp32 = OF_BSWAP32_IF_BE((uint32_t)_date.timeIntervalSince1970);
	[data addItems: &tmp32
		 count: sizeof(tmp32)];

	/* Reserved */
	[data increaseCountBy: 1];

	/* Header level */
	[data addItem: "\x02"];

	/* CRC16 */
	tmp16 = OF_BSWAP16_IF_BE(_CRC16);
	[data addItems: &tmp16
		 count: sizeof(tmp16)];

	/* Operating system identifier */
	[data addItem: "U"];

	/* Common header. Contains CRC16, which is written at the end. */
	tmp16 = OF_BSWAP16_IF_BE(5);
	[data addItems: &tmp16
		 count: sizeof(tmp16)];
	[data addItem: "\x00"];
	[data increaseCountBy: 2];

	tmp16 = OF_BSWAP16_IF_BE((uint16_t)fileNameLength + 3);
	[data addItems: &tmp16
		 count: sizeof(tmp16)];
	[data addItem: "\x01"];
	[data addItems: fileName
		 count: fileNameLength];

	if (directoryNameLength > 0) {
		tmp16 = OF_BSWAP16_IF_BE((uint16_t)directoryNameLength + 3);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];
		[data addItem: "\x02"];
		[data addItems: directoryName
			 count: directoryNameLength];
	}

	if (_fileComment != nil) {
		size_t fileCommentLength =
		    [_fileComment cStringLengthWithEncoding: encoding];

		if (fileCommentLength > UINT16_MAX - 3)
			@throw [OFOutOfRangeException exception];

		tmp16 = OF_BSWAP16_IF_BE((uint16_t)fileCommentLength + 3);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];
		[data addItem: "\x3F"];
		[data addItems: [_fileComment cStringWithEncoding: encoding]
			 count: fileCommentLength];
	}

	if (_mode != nil) {
		tmp16 = OF_BSWAP16_IF_BE(5);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];
		[data addItem: "\x50"];

		tmp16 = OF_BSWAP16_IF_BE(_mode.unsignedShortValue);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];
	}

	if (_UID != nil || _GID != nil) {
		if (_UID == nil || _GID == nil)
			@throw [OFInvalidArgumentException exception];

		tmp16 = OF_BSWAP16_IF_BE(7);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];
		[data addItem: "\x51"];

		tmp16 = OF_BSWAP16_IF_BE(_GID.unsignedShortValue);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];

		tmp16 = OF_BSWAP16_IF_BE(_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 = OF_BSWAP16_IF_BE((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 = OF_BSWAP16_IF_BE((uint16_t)ownerLength + 3);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];
		[data addItem: "\x53"];
		[data addItems: [_owner cStringWithEncoding: encoding]
			 count: ownerLength];
	}

	if (_modificationDate != nil) {
		tmp16 = OF_BSWAP16_IF_BE(7);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];
		[data addItem: "\x54"];

		tmp32 = OF_BSWAP32_IF_BE(
		    (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];

		if (extensionLength > UINT16_MAX - 2)
			@throw [OFOutOfRangeException exception];

		tmp16 = OF_BSWAP16_IF_BE((uint16_t)extensionLength + 2);
		[data addItems: &tmp16
			 count: sizeof(tmp16)];
		[data addItems: extension.items
			 count: extension.count];
	}

	/* Zero-length extension to terminate */
	[data increaseCountBy: 2];

	headerSize = data.count;

	if (headerSize > UINT16_MAX)
		@throw [OFOutOfRangeException exception];

	/* Now fill in the size and CRC16 for the entire header */
	tmp16 = OF_BSWAP16_IF_BE(headerSize);
	memcpy([data mutableItemAtIndex: 0], &tmp16, sizeof(tmp16));

	tmp16 = of_crc16(0, data.items, data.count);
	tmp16 = OF_BSWAP16_IF_BE(tmp16);
	memcpy([data mutableItemAtIndex: 27], &tmp16, sizeof(tmp16));

	[stream writeData: data];

	objc_autoreleasePoolPop(pool);
}








|










|













|


|
|
<

|
|
<

|
|
<








|
|
<





|
|
<



|
|
<

|
<


|
|
<

|
<









|
|
<






|
|
<


|
|
<






|
|
<


|
|
<

|
|
<









|
|
<












|
|
<






|
|
<


|

|
<











|
|
<
|
<











|


|
|







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

- (OFArray OF_GENERIC(OFData *) *)extensions
{
	return _extensions;
}

- (void)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *data = [OFMutableData dataWithCapacity: 24];
	const char *fileName, *directoryName;
	size_t fileNameLength, directoryNameLength;
	uint16_t tmp16;
	uint32_t tmp32;
	size_t headerSize;

	if ([_compressionMethod cStringLengthWithEncoding:
	    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"];

	/* CRC16 */
	tmp16 = OFToLittleEndian16(_CRC16);
	[data addItems: &tmp16 count: sizeof(tmp16)];


	/* Operating system identifier */
	[data addItem: "U"];

	/* Common header. Contains CRC16, which is written at the end. */
	tmp16 = OFToLittleEndian16(5);
	[data addItems: &tmp16 count: sizeof(tmp16)];

	[data addItem: "\x00"];
	[data increaseCountBy: 2];

	tmp16 = OFToLittleEndian16((uint16_t)fileNameLength + 3);
	[data addItems: &tmp16 count: sizeof(tmp16)];

	[data addItem: "\x01"];
	[data addItems: fileName count: fileNameLength];


	if (directoryNameLength > 0) {
		tmp16 = OFToLittleEndian16((uint16_t)directoryNameLength + 3);
		[data addItems: &tmp16 count: sizeof(tmp16)];

		[data addItem: "\x02"];
		[data addItems: directoryName count: directoryNameLength];

	}

	if (_fileComment != nil) {
		size_t fileCommentLength =
		    [_fileComment cStringLengthWithEncoding: encoding];

		if (fileCommentLength > UINT16_MAX - 3)
			@throw [OFOutOfRangeException exception];

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

		if (extensionLength > UINT16_MAX - 2)
			@throw [OFOutOfRangeException exception];

		tmp16 = OFToLittleEndian16((uint16_t)extensionLength + 2);
		[data addItems: &tmp16 count: sizeof(tmp16)];

		[data addItems: extension.items count: extension.count];

	}

	/* Zero-length extension to terminate */
	[data increaseCountBy: 2];

	headerSize = data.count;

	if (headerSize > UINT16_MAX)
		@throw [OFOutOfRangeException exception];

	/* Now fill in the size and CRC16 for the entire header */
	tmp16 = OFToLittleEndian16(headerSize);
	memcpy([data mutableItemAtIndex: 0], &tmp16, sizeof(tmp16));

	tmp16 = OFCRC16(0, data.items, data.count);
	tmp16 = OFToLittleEndian16(tmp16);
	memcpy([data mutableItemAtIndex: 27], &tmp16, sizeof(tmp16));

	[stream writeData: data];

	objc_autoreleasePoolPop(pool);
}

Modified src/OFLHADecompressingStream.h from [cc62626ce1] to [1061027b73].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFStream.h"


OF_ASSUME_NONNULL_BEGIN

#define OF_LHA_DECOMPRESSING_STREAM_BUFFER_SIZE 4096

OF_DIRECT_MEMBERS
@interface OFLHADecompressingStream: OFStream
{
	OFStream *_stream;
	uint8_t _distanceBits, _dictionaryBits;
	unsigned char _buffer[OF_LHA_DECOMPRESSING_STREAM_BUFFER_SIZE];
	uint32_t _bytesConsumed;
	uint16_t _bufferIndex, _bufferLength;
	uint8_t _byte;
	uint8_t _bitIndex, _savedBitsLength;
	uint16_t _savedBits;
	unsigned char *_slidingWindow;
	uint32_t _slidingWindowIndex, _slidingWindowMask;
	int _state;
	uint16_t _symbolsLeft;

	struct of_huffman_tree *_Nullable _codeLenTree, *_Nullable _litLenTree;

	struct of_huffman_tree *_Nullable _distTree, *_Nullable _treeIter;
	uint16_t _codesCount, _codesReceived;
	bool _currentIsExtendedLength, _skip;
	uint8_t *_Nullable _codesLengths;
	uint16_t _length;
	uint32_t _distance;
}


<
<
|














>



|






|









>
|
>
|







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


 * 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 "OFStream.h"
#import "OFHuffmanTree.h"

OF_ASSUME_NONNULL_BEGIN

#define OFLHADecompressingStreamBufferSize 4096

OF_DIRECT_MEMBERS
@interface OFLHADecompressingStream: OFStream
{
	OFStream *_stream;
	uint8_t _distanceBits, _dictionaryBits;
	unsigned char _buffer[OFLHADecompressingStreamBufferSize];
	uint32_t _bytesConsumed;
	uint16_t _bufferIndex, _bufferLength;
	uint8_t _byte;
	uint8_t _bitIndex, _savedBitsLength;
	uint16_t _savedBits;
	unsigned char *_slidingWindow;
	uint32_t _slidingWindowIndex, _slidingWindowMask;
	int _state;
	uint16_t _symbolsLeft;
	OFHuffmanTree _Nullable _codeLenTree;
	OFHuffmanTree _Nullable _litLenTree;
	OFHuffmanTree _Nullable _distTree;
	OFHuffmanTree _Nullable _treeIter;
	uint16_t _codesCount, _codesReceived;
	bool _currentIsExtendedLength, _skip;
	uint8_t *_Nullable _codesLengths;
	uint16_t _length;
	uint32_t _distance;
}

Modified src/OFLHADecompressingStream.m from [16b7cb0b85] to [1c43fdd671].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <assert.h>

#import "OFLHADecompressingStream.h"
#import "OFKernelEventObserver.h"

#import "huffman_tree.h"

#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"

enum state {
	STATE_BLOCK_HEADER,
	STATE_CODE_LEN_CODES_COUNT,
	STATE_CODE_LEN_TREE,
	STATE_CODE_LEN_TREE_SINGLE,
	STATE_LITLEN_CODES_COUNT,
	STATE_LITLEN_TREE,
	STATE_LITLEN_TREE_SINGLE,
	STATE_DIST_CODES_COUNT,
	STATE_DIST_TREE,
	STATE_DIST_TREE_SINGLE,
	STATE_BLOCK_LITLEN,
	STATE_BLOCK_DIST_LENGTH,
	STATE_BLOCK_DIST_LENGTH_EXTRA,
	STATE_BLOCK_LEN_DIST_PAIR
};

@implementation OFLHADecompressingStream
@synthesize bytesConsumed = _bytesConsumed;

static OF_INLINE bool
tryReadBits(OFLHADecompressingStream *stream, uint16_t *bits, uint8_t count)
{
	uint16_t ret = stream->_savedBits;

	assert(stream->_savedBitsLength < count);

	for (uint_fast8_t i = stream->_savedBitsLength; i < count; i++) {
		if OF_UNLIKELY (stream->_bitIndex == 8) {
			if OF_LIKELY (stream->_bufferIndex <
			    stream->_bufferLength)
				stream->_byte =
				    stream->_buffer[stream->_bufferIndex++];
			else {
				const size_t bufferLength =
				    OF_LHA_DECOMPRESSING_STREAM_BUFFER_SIZE;
				size_t length = [stream->_stream
				    readIntoBuffer: stream->_buffer
					    length: bufferLength];

				stream->_bytesConsumed += (uint32_t)length;

				if OF_UNLIKELY (length < 1) {

<
<
|




















|




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




















|







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


 * 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 <assert.h>

#import "OFLHADecompressingStream.h"
#import "OFKernelEventObserver.h"

#import "OFHuffmanTree.h"

#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"

enum State {
	stateBlockHeader,
	stateCodeLenCodesCount,
	stateCodeLenTree,
	stateCodeLenTreeSingle,
	stateLitLenCodesCount,
	stateLitLenTree,
	stateLitLenTreeSingle,
	stateDistCodesCount,
	stateDistTree,
	stateDistTreeSingle,
	stateBlockLitLen,
	stateBlockDistLength,
	stateBlockDistLengthExtra,
	stateBlockLenDistPair
};

@implementation OFLHADecompressingStream
@synthesize bytesConsumed = _bytesConsumed;

static OF_INLINE bool
tryReadBits(OFLHADecompressingStream *stream, uint16_t *bits, uint8_t count)
{
	uint16_t ret = stream->_savedBits;

	assert(stream->_savedBitsLength < count);

	for (uint_fast8_t i = stream->_savedBitsLength; i < count; i++) {
		if OF_UNLIKELY (stream->_bitIndex == 8) {
			if OF_LIKELY (stream->_bufferIndex <
			    stream->_bufferLength)
				stream->_byte =
				    stream->_buffer[stream->_bufferIndex++];
			else {
				const size_t bufferLength =
				    OFLHADecompressingStreamBufferSize;
				size_t length = [stream->_stream
				    readIntoBuffer: stream->_buffer
					    length: bufferLength];

				stream->_bytesConsumed += (uint32_t)length;

				if OF_UNLIKELY (length < 1) {
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
		/* 0-7 address the bit, 8 means fetch next byte */
		_bitIndex = 8;

		_distanceBits = distanceBits;
		_dictionaryBits = dictionaryBits;

		_slidingWindowMask = (1u << dictionaryBits) - 1;
		_slidingWindow = [self allocMemoryWithSize:
		    _slidingWindowMask + 1];
		memset(_slidingWindow, ' ', _slidingWindowMask + 1);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_stream != nil)
		[self close];



	if (_codeLenTree != NULL)
		of_huffman_tree_release(_codeLenTree);
	if (_litLenTree != NULL)
		of_huffman_tree_release(_litLenTree);
	if (_distTree != NULL)
		of_huffman_tree_release(_distTree);



	[super dealloc];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer_
			  length: (size_t)length
{
	unsigned char *buffer = buffer_;
	uint16_t bits = 0, value = 0;
	size_t bytesWritten = 0;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_stream.atEndOfStream && _bufferLength - _bufferIndex == 0 &&
	    _state == STATE_BLOCK_HEADER)
		return 0;

start:
	switch ((enum state)_state) {
	case STATE_BLOCK_HEADER:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 16))
			return bytesWritten;

		_symbolsLeft = bits;

		_state = STATE_CODE_LEN_CODES_COUNT;
		goto start;
	case STATE_CODE_LEN_CODES_COUNT:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
			return bytesWritten;

		if OF_UNLIKELY (bits > 20)
			@throw [OFInvalidFormatException exception];

		if OF_UNLIKELY (bits == 0) {
			_state = STATE_CODE_LEN_TREE_SINGLE;
			goto start;
		}

		_codesCount = bits;
		_codesReceived = 0;
		_codesLengths = [self allocZeroedMemoryWithSize: bits];
		_skip = true;

		_state = STATE_CODE_LEN_TREE;
		goto start;
	case STATE_CODE_LEN_TREE:
		while (_codesReceived < _codesCount) {
			if OF_UNLIKELY (_currentIsExtendedLength) {
				if OF_UNLIKELY (!tryReadBits(self, &bits, 1))
					return bytesWritten;

				if OF_UNLIKELY (bits == 0) {
					_codesReceived++;







<
|














>
>

|

|

|
>
>















|



|
|





|

|







|





|


|

|







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
		/* 0-7 address the bit, 8 means fetch next byte */
		_bitIndex = 8;

		_distanceBits = distanceBits;
		_dictionaryBits = dictionaryBits;

		_slidingWindowMask = (1u << dictionaryBits) - 1;

		_slidingWindow = OFAllocMemory(_slidingWindowMask + 1, 1);
		memset(_slidingWindow, ' ', _slidingWindowMask + 1);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_stream != nil)
		[self close];

	OFFreeMemory(_slidingWindow);

	if (_codeLenTree != NULL)
		OFHuffmanTreeFree(_codeLenTree);
	if (_litLenTree != NULL)
		OFHuffmanTreeFree(_litLenTree);
	if (_distTree != NULL)
		OFHuffmanTreeFree(_distTree);

	OFFreeMemory(_codesLengths);

	[super dealloc];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer_
			  length: (size_t)length
{
	unsigned char *buffer = buffer_;
	uint16_t bits = 0, value = 0;
	size_t bytesWritten = 0;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_stream.atEndOfStream && _bufferLength - _bufferIndex == 0 &&
	    _state == stateBlockHeader)
		return 0;

start:
	switch ((enum State)_state) {
	case stateBlockHeader:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 16))
			return bytesWritten;

		_symbolsLeft = bits;

		_state = stateCodeLenCodesCount;
		goto start;
	case stateCodeLenCodesCount:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
			return bytesWritten;

		if OF_UNLIKELY (bits > 20)
			@throw [OFInvalidFormatException exception];

		if OF_UNLIKELY (bits == 0) {
			_state = stateCodeLenTreeSingle;
			goto start;
		}

		_codesCount = bits;
		_codesReceived = 0;
		_codesLengths = OFAllocZeroedMemory(bits, 1);
		_skip = true;

		_state = stateCodeLenTree;
		goto start;
	case stateCodeLenTree:
		while (_codesReceived < _codesCount) {
			if OF_UNLIKELY (_currentIsExtendedLength) {
				if OF_UNLIKELY (!tryReadBits(self, &bits, 1))
					return bytesWritten;

				if OF_UNLIKELY (bits == 0) {
					_codesReceived++;
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
			if OF_UNLIKELY (bits == 7) {
				_currentIsExtendedLength = true;
				continue;
			} else
				_codesReceived++;
		}

		_codeLenTree = of_huffman_tree_construct(_codesLengths,
		    _codesCount);
		[self freeMemory: _codesLengths];

		_state = STATE_LITLEN_CODES_COUNT;
		goto start;
	case STATE_CODE_LEN_TREE_SINGLE:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
			return bytesWritten;

		_codeLenTree = of_huffman_tree_construct_single(bits);

		_state = STATE_LITLEN_CODES_COUNT;
		goto start;
	case STATE_LITLEN_CODES_COUNT:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 9))
			return bytesWritten;

		if OF_UNLIKELY (bits > 510)
			@throw [OFInvalidFormatException exception];

		if OF_UNLIKELY (bits == 0) {
			of_huffman_tree_release(_codeLenTree);
			_codeLenTree = NULL;

			_state = STATE_LITLEN_TREE_SINGLE;
			goto start;
		}

		_codesCount = bits;
		_codesReceived = 0;
		_codesLengths = [self allocZeroedMemoryWithSize: bits];
		_skip = false;

		_treeIter = _codeLenTree;
		_state = STATE_LITLEN_TREE;
		goto start;
	case STATE_LITLEN_TREE:
		while (_codesReceived < _codesCount) {
			if OF_UNLIKELY (_skip) {
				uint16_t skipCount;

				switch (_codesLengths[_codesReceived]) {
				case 0:
					skipCount = 1;







|
|
|

|

|



|

|

|







|


|





|



|

|







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
			if OF_UNLIKELY (bits == 7) {
				_currentIsExtendedLength = true;
				continue;
			} else
				_codesReceived++;
		}

		_codeLenTree = OFHuffmanTreeNew(_codesLengths, _codesCount);
		OFFreeMemory(_codesLengths);
		_codesLengths = NULL;

		_state = stateLitLenCodesCount;
		goto start;
	case stateCodeLenTreeSingle:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 5))
			return bytesWritten;

		_codeLenTree = OFHuffmanTreeNewSingle(bits);

		_state = stateLitLenCodesCount;
		goto start;
	case stateLitLenCodesCount:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 9))
			return bytesWritten;

		if OF_UNLIKELY (bits > 510)
			@throw [OFInvalidFormatException exception];

		if OF_UNLIKELY (bits == 0) {
			OFHuffmanTreeFree(_codeLenTree);
			_codeLenTree = NULL;

			_state = stateLitLenTreeSingle;
			goto start;
		}

		_codesCount = bits;
		_codesReceived = 0;
		_codesLengths = OFAllocZeroedMemory(bits, 1);
		_skip = false;

		_treeIter = _codeLenTree;
		_state = stateLitLenTree;
		goto start;
	case stateLitLenTree:
		while (_codesReceived < _codesCount) {
			if OF_UNLIKELY (_skip) {
				uint16_t skipCount;

				switch (_codesLengths[_codesReceived]) {
				case 0:
					skipCount = 1;
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
					if OF_UNLIKELY (!tryReadBits(self,
					    &bits, 9))
						return bytesWritten;

					skipCount = bits + 20;
					break;
				default:
					OF_ENSURE(0);
				}

				if OF_UNLIKELY (_codesReceived + skipCount >
				    _codesCount)
					@throw [OFInvalidFormatException
					    exception];

				for (uint_fast16_t j = 0; j < skipCount; j++)
					_codesLengths[_codesReceived++] = 0;

				_skip = false;
				continue;
			}

			if (!of_huffman_tree_walk(self, tryReadBits,
			    &_treeIter, &value))
				return bytesWritten;

			_treeIter = _codeLenTree;

			if (value < 3) {
				_codesLengths[_codesReceived] = value;
				_skip = true;
			} else
				_codesLengths[_codesReceived++] = value - 2;
		}

		_litLenTree = of_huffman_tree_construct(_codesLengths,
		    _codesCount);
		[self freeMemory: _codesLengths];

		of_huffman_tree_release(_codeLenTree);
		_codeLenTree = NULL;

		_state = STATE_DIST_CODES_COUNT;
		goto start;
	case STATE_LITLEN_TREE_SINGLE:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 9))
			return bytesWritten;

		_litLenTree = of_huffman_tree_construct_single(bits);

		_state = STATE_DIST_CODES_COUNT;
		goto start;
	case STATE_DIST_CODES_COUNT:
		if OF_UNLIKELY (!tryReadBits(self, &bits, _distanceBits))
			return bytesWritten;

		if OF_UNLIKELY (bits > _dictionaryBits)
			@throw [OFInvalidFormatException exception];

		if OF_UNLIKELY (bits == 0) {
			_state = STATE_DIST_TREE_SINGLE;
			goto start;
		}

		_codesCount = bits;
		_codesReceived = 0;
		_codesLengths = [self allocZeroedMemoryWithSize: bits];

		_treeIter = _codeLenTree;
		_state = STATE_DIST_TREE;
		goto start;
	case STATE_DIST_TREE:
		while (_codesReceived < _codesCount) {
			if OF_UNLIKELY (_currentIsExtendedLength) {
				if OF_UNLIKELY (!tryReadBits(self, &bits, 1))
					return bytesWritten;

				if OF_UNLIKELY (bits == 0) {
					_codesReceived++;







|














|
|











|
|
|

|


|

|



|

|

|







|





|


|

|







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
					if OF_UNLIKELY (!tryReadBits(self,
					    &bits, 9))
						return bytesWritten;

					skipCount = bits + 20;
					break;
				default:
					OFEnsure(0);
				}

				if OF_UNLIKELY (_codesReceived + skipCount >
				    _codesCount)
					@throw [OFInvalidFormatException
					    exception];

				for (uint_fast16_t j = 0; j < skipCount; j++)
					_codesLengths[_codesReceived++] = 0;

				_skip = false;
				continue;
			}

			if (!OFHuffmanTreeWalk(self, tryReadBits, &_treeIter,
			    &value))
				return bytesWritten;

			_treeIter = _codeLenTree;

			if (value < 3) {
				_codesLengths[_codesReceived] = value;
				_skip = true;
			} else
				_codesLengths[_codesReceived++] = value - 2;
		}

		_litLenTree = OFHuffmanTreeNew(_codesLengths, _codesCount);
		OFFreeMemory(_codesLengths);
		_codesLengths = NULL;

		OFHuffmanTreeFree(_codeLenTree);
		_codeLenTree = NULL;

		_state = stateDistCodesCount;
		goto start;
	case stateLitLenTreeSingle:
		if OF_UNLIKELY (!tryReadBits(self, &bits, 9))
			return bytesWritten;

		_litLenTree = OFHuffmanTreeNewSingle(bits);

		_state = stateDistCodesCount;
		goto start;
	case stateDistCodesCount:
		if OF_UNLIKELY (!tryReadBits(self, &bits, _distanceBits))
			return bytesWritten;

		if OF_UNLIKELY (bits > _dictionaryBits)
			@throw [OFInvalidFormatException exception];

		if OF_UNLIKELY (bits == 0) {
			_state = stateDistTreeSingle;
			goto start;
		}

		_codesCount = bits;
		_codesReceived = 0;
		_codesLengths = OFAllocZeroedMemory(bits, 1);

		_treeIter = _codeLenTree;
		_state = stateDistTree;
		goto start;
	case stateDistTree:
		while (_codesReceived < _codesCount) {
			if OF_UNLIKELY (_currentIsExtendedLength) {
				if OF_UNLIKELY (!tryReadBits(self, &bits, 1))
					return bytesWritten;

				if OF_UNLIKELY (bits == 0) {
					_codesReceived++;
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
			if OF_UNLIKELY (bits == 7) {
				_currentIsExtendedLength = true;
				continue;
			} else
				_codesReceived++;
		}

		_distTree = of_huffman_tree_construct(_codesLengths,
		    _codesCount);
		[self freeMemory: _codesLengths];

		_treeIter = _litLenTree;
		_state = STATE_BLOCK_LITLEN;
		goto start;
	case STATE_DIST_TREE_SINGLE:
		if OF_UNLIKELY (!tryReadBits(self, &bits, _distanceBits))
			return bytesWritten;

		_distTree = of_huffman_tree_construct_single(bits);

		_treeIter = _litLenTree;
		_state = STATE_BLOCK_LITLEN;
		goto start;
	case STATE_BLOCK_LITLEN:
		if OF_UNLIKELY (_symbolsLeft == 0) {
			of_huffman_tree_release(_litLenTree);
			of_huffman_tree_release(_distTree);
			_litLenTree = _distTree = NULL;

			_state = STATE_BLOCK_HEADER;

			/*
			 * We must return here, as there is no indication
			 * whether this was the last block. Whoever called this
			 * method needs to check if everything has been read
			 * already and only call read again if that is not the
			 * case.







|
|
|


|

|



|


|

|

|
|


|







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
			if OF_UNLIKELY (bits == 7) {
				_currentIsExtendedLength = true;
				continue;
			} else
				_codesReceived++;
		}

		_distTree = OFHuffmanTreeNew(_codesLengths, _codesCount);
		OFFreeMemory(_codesLengths);
		_codesLengths = NULL;

		_treeIter = _litLenTree;
		_state = stateBlockLitLen;
		goto start;
	case stateDistTreeSingle:
		if OF_UNLIKELY (!tryReadBits(self, &bits, _distanceBits))
			return bytesWritten;

		_distTree = OFHuffmanTreeNewSingle(bits);

		_treeIter = _litLenTree;
		_state = stateBlockLitLen;
		goto start;
	case stateBlockLitLen:
		if OF_UNLIKELY (_symbolsLeft == 0) {
			OFHuffmanTreeFree(_litLenTree);
			OFHuffmanTreeFree(_distTree);
			_litLenTree = _distTree = NULL;

			_state = stateBlockHeader;

			/*
			 * We must return here, as there is no indication
			 * whether this was the last block. Whoever called this
			 * method needs to check if everything has been read
			 * already and only call read again if that is not the
			 * case.
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

			return bytesWritten;
		}

		if OF_UNLIKELY (length == 0)
			return bytesWritten;

		if OF_UNLIKELY (!of_huffman_tree_walk(self, tryReadBits,
		    &_treeIter, &value))
			return bytesWritten;

		if OF_LIKELY (value < 256) {
			buffer[bytesWritten++] = value;
			length--;

			_slidingWindow[_slidingWindowIndex] = value;
			_slidingWindowIndex = (_slidingWindowIndex + 1) &
			    _slidingWindowMask;

			_symbolsLeft--;
			_treeIter = _litLenTree;
		} else {
			_length = value - 253;
			_treeIter = _distTree;
			_state = STATE_BLOCK_DIST_LENGTH;
		}

		goto start;
	case STATE_BLOCK_DIST_LENGTH:
		if OF_UNLIKELY (!of_huffman_tree_walk(self, tryReadBits,
		    &_treeIter, &value))
			return bytesWritten;

		_distance = value;

		_state = (value < 2
		    ? STATE_BLOCK_LEN_DIST_PAIR
		    : STATE_BLOCK_DIST_LENGTH_EXTRA);
		goto start;
	case STATE_BLOCK_DIST_LENGTH_EXTRA:
		if OF_UNLIKELY (!tryReadBits(self, &bits, _distance - 1))
			return bytesWritten;

		_distance = bits + (1u << (_distance - 1));

		_state = STATE_BLOCK_LEN_DIST_PAIR;
		goto start;
	case STATE_BLOCK_LEN_DIST_PAIR:
		for (uint_fast16_t i = 0; i < _length; i++) {
			uint32_t idx;

			if OF_UNLIKELY (length == 0) {
				_length -= i;
				return bytesWritten;
			}







|
















|



|
|






|
<

|





|

|







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

			return bytesWritten;
		}

		if OF_UNLIKELY (length == 0)
			return bytesWritten;

		if OF_UNLIKELY (!OFHuffmanTreeWalk(self, tryReadBits,
		    &_treeIter, &value))
			return bytesWritten;

		if OF_LIKELY (value < 256) {
			buffer[bytesWritten++] = value;
			length--;

			_slidingWindow[_slidingWindowIndex] = value;
			_slidingWindowIndex = (_slidingWindowIndex + 1) &
			    _slidingWindowMask;

			_symbolsLeft--;
			_treeIter = _litLenTree;
		} else {
			_length = value - 253;
			_treeIter = _distTree;
			_state = stateBlockDistLength;
		}

		goto start;
	case stateBlockDistLength:
		if OF_UNLIKELY (!OFHuffmanTreeWalk(self, tryReadBits,
		    &_treeIter, &value))
			return bytesWritten;

		_distance = value;

		_state = (value < 2
		    ? stateBlockLenDistPair : stateBlockDistLengthExtra);

		goto start;
	case stateBlockDistLengthExtra:
		if OF_UNLIKELY (!tryReadBits(self, &bits, _distance - 1))
			return bytesWritten;

		_distance = bits + (1u << (_distance - 1));

		_state = stateBlockLenDistPair;
		goto start;
	case stateBlockLenDistPair:
		for (uint_fast16_t i = 0; i < _length; i++) {
			uint32_t idx;

			if OF_UNLIKELY (length == 0) {
				_length -= i;
				return bytesWritten;
			}
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
			_slidingWindowIndex = (_slidingWindowIndex + 1) &
			    _slidingWindowMask;
		}

		_symbolsLeft--;

		_treeIter = _litLenTree;
		_state = STATE_BLOCK_LITLEN;
		goto start;
	}

	OF_UNREACHABLE
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return (_stream.atEndOfStream &&
	    _bufferLength - _bufferIndex == 0 && _state == STATE_BLOCK_HEADER);
}

- (int)fileDescriptorForReading
{
	return ((id <OFReadyForReadingObserving>)_stream)
	    .fileDescriptorForReading;
}







|












|







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
			_slidingWindowIndex = (_slidingWindowIndex + 1) &
			    _slidingWindowMask;
		}

		_symbolsLeft--;

		_treeIter = _litLenTree;
		_state = stateBlockLitLen;
		goto start;
	}

	OF_UNREACHABLE
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return (_stream.atEndOfStream &&
	    _bufferLength - _bufferIndex == 0 && _state == stateBlockHeader);
}

- (int)fileDescriptorForReading
{
	return ((id <OFReadyForReadingObserving>)_stream)
	    .fileDescriptorForReading;
}

Modified src/OFList.h from [75efc796d9] to [f2ae387af6].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCollection.h"
#import "OFEnumerator.h"
#import "OFSerialization.h"

OF_ASSUME_NONNULL_BEGIN


typedef struct of_list_object_t of_list_object_t;








/**
 * @struct of_list_object_t OFList.h ObjFW/OFList.h
 *
 * @brief A list object.
 *
 * A struct that contains a pointer to the next list object, the previous list
 * object and the object.
 */




struct of_list_object_t {







	/** A pointer to the next list object in the list */


	of_list_object_t *_Nullable next;





	/** A pointer to the previous list object in the list */


	of_list_object_t *_Nullable previous;

	/** The object for the list object */





	id __unsafe_unretained object;
};






/**
 * @class OFList OFList.h ObjFW/OFList.h
 *
 * @brief A class which provides easy to use double-linked lists.
 */
@interface OFList OF_GENERIC(ObjectType): OFObject <OFCopying, OFCollection,
    OFSerialization>
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# define ObjectType id
#endif
{
	of_list_object_t *_Nullable _firstListObject;
	of_list_object_t *_Nullable _lastListObject;
	size_t _count;
	unsigned long  _mutations;
	OF_RESERVE_IVARS(OFList, 4)
}

/**
 * @brief The first list object of the list.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
    of_list_object_t *firstListObject;

/**
 * @brief The first object of the list or `nil`.
 *
 * @warning The returned object is *not* retained and autoreleased for
 *	    performance reasons!
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) ObjectType firstObject;

/**
 * @brief The last list object of the list.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
    of_list_object_t *lastListObject;

/**
 * @brief The last object of the list or `nil`.
 *
 * @warning The returned object is *not* retained and autoreleased for
 *	    performance reasons!
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) ObjectType lastObject;

/**
 * @brief Creates a new OFList.
 *
 * @return A new autoreleased OFList
 */
+ (instancetype)list;

/**
 * @brief Appends an object to the list.
 *
 * @param object The object to append
 * @return An of_list_object_t, needed to identify the object inside the list.
 *	   For example, if you want to remove an object from the list, you need
 *	   its of_list_object_t.
 */
- (of_list_object_t *)appendObject: (ObjectType)object;

/**
 * @brief Prepends an object to the list.
 *
 * @param object The object to prepend
 * @return An of_list_object_t, needed to identify the object inside the list.
 *	   For example, if you want to remove an object from the list, you need
 *	   its of_list_object_t.
 */
- (of_list_object_t *)prependObject: (ObjectType)object;

/**
 * @brief Inserts an object before another list object.
 *
 * @param object The object to insert
 * @param listObject The of_list_object_t of the object before which it should
 *	  be inserted
 * @return An of_list_object_t, needed to identify the object inside the list.
 *	   For example, if you want to remove an object from the list, you need
 *	   its of_list_object_t.
 */
- (of_list_object_t *)insertObject: (ObjectType)object
		  beforeListObject: (of_list_object_t *)listObject;

/**
 * @brief Inserts an object after another list object.
 *
 * @param object The object to insert
 * @param listObject The of_list_object_t of the object after which it should be
 *	  inserted
 * @return An of_list_object_t, needed to identify the object inside the list.
 *	   For example, if you want to remove an object from the list, you need
 *	   its of_list_object_t.
 */
- (of_list_object_t *)insertObject: (ObjectType)object
		   afterListObject: (of_list_object_t *)listObject;

/**
 * @brief Removes the object with the specified list object from the list.
 *
 * @param listObject The list object returned by append / prepend
 */
- (void)removeListObject: (of_list_object_t *)listObject;

/**
 * @brief Checks whether the list contains an object equal to the specified
 *	  object.
 *
 * @param object The object which is checked for being in the list
 * @return A boolean whether the list contains the specified object

<
<
|




















>
|
>
>
>
>
>
>
>
>

|

|

<
|

>
>
>
>
|
>
>
>
>
>
>
>
|
>
>
|
>
>
>
>
>
|
>
>
|
>
|
>
>
>
>
>
|
<
>
>
>
>
>












|
|

|






|
<












|
<




















|

|

|





|

|

|





|
|
|

|

|
|





|

|

|

|
|




|

|







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


 * 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 "OFCollection.h"
#import "OFEnumerator.h"
#import "OFSerialization.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

/*
 * Make clang's -Wdocumentation shut about about using @struct on someting it
 * thinks is not a struct. Doxygen requires it this way.
 */
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdocumentation"
#endif
/**
 * @struct OFListItem OFList.h ObjFW/OFList.h
 *
 * @brief A list item.
 *

 * See @ref OFListItemNext, @ref OFListItemPrevious and @ref OFListItemObject.
 */
typedef struct _OFListItem *OFListItem;
#ifdef __clang__
# pragma clang diagnostic pop
#endif

#ifdef __cplusplus
extern "C" {
#endif
/*!
 * @brief Returns the next list item of the list item.
 *
 * @param listItem The list item for which the next list item should be returned
 * @return The next list item of the list item
 */
extern OFListItem _Nullable OFListItemNext(OFListItem _Nonnull listItem);

/*!
 * @brief Returns the previous list item of the list item.
 *
 * @param listItem The list item for which the previous list item should be
 *		   returned
 * @return The previous list item of the list item
 */
extern OFListItem _Nullable OFListItemPrevious(OFListItem _Nonnull listItem);

/*!
 * @brief Returns the object of the list item.
 *
 * @warning The returned object is not retained and autoreleased - this is the
 *	    caller's responsibility!
 *
 * @param listItem The list item for which the object should be returned
 * @return The object of the list item

 */
extern id _Nonnull OFListItemObject(OFListItem _Nonnull listItem);
#ifdef __cplusplus
}
#endif

/**
 * @class OFList OFList.h ObjFW/OFList.h
 *
 * @brief A class which provides easy to use double-linked lists.
 */
@interface OFList OF_GENERIC(ObjectType): OFObject <OFCopying, OFCollection,
    OFSerialization>
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# define ObjectType id
#endif
{
	OFListItem _Nullable _firstListItem;
	OFListItem _Nullable _lastListItem;
	size_t _count;
	unsigned long _mutations;
	OF_RESERVE_IVARS(OFList, 4)
}

/**
 * @brief The first list object of the list.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFListItem firstListItem;


/**
 * @brief The first object of the list or `nil`.
 *
 * @warning The returned object is *not* retained and autoreleased for
 *	    performance reasons!
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) ObjectType firstObject;

/**
 * @brief The last list object of the list.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFListItem lastListItem;


/**
 * @brief The last object of the list or `nil`.
 *
 * @warning The returned object is *not* retained and autoreleased for
 *	    performance reasons!
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) ObjectType lastObject;

/**
 * @brief Creates a new OFList.
 *
 * @return A new autoreleased OFList
 */
+ (instancetype)list;

/**
 * @brief Appends an object to the list.
 *
 * @param object The object to append
 * @return An OFListItem, needed to identify the object inside the list.
 *	   For example, if you want to remove an object from the list, you need
 *	   its OFListItem.
 */
- (OFListItem)appendObject: (ObjectType)object;

/**
 * @brief Prepends an object to the list.
 *
 * @param object The object to prepend
 * @return An OFListItem, needed to identify the object inside the list.
 *	   For example, if you want to remove an object from the list, you need
 *	   its OFListItem.
 */
- (OFListItem)prependObject: (ObjectType)object;

/**
 * @brief Inserts an object before another list object.
 *
 * @param object The object to insert
 * @param listItem The OFListItem of the object before which it should be
 *		   inserted
 * @return An OFListItem, needed to identify the object inside the list.
 *	   For example, if you want to remove an object from the list, you need
 *	   its OFListItem.
 */
- (OFListItem)insertObject: (ObjectType)object
	    beforeListItem: (OFListItem)listItem;

/**
 * @brief Inserts an object after another list object.
 *
 * @param object The object to insert
 * @param listItem The OFListItem of the object after which it should be
 *	  inserted
 * @return An OFListItem, needed to identify the object inside the list.
 *	   For example, if you want to remove an object from the list, you need
 *	   its OFListItem.
 */
- (OFListItem)insertObject: (ObjectType)object
	     afterListItem: (OFListItem)listItem;

/**
 * @brief Removes the object with the specified list object from the list.
 *
 * @param listItem The list object returned by append / prepend
 */
- (void)removeListItem: (OFListItem)listItem;

/**
 * @brief Checks whether the list contains an object equal to the specified
 *	  object.
 *
 * @param object The object which is checked for being in the list
 * @return A boolean whether the list contains the specified object

Modified src/OFList.m from [2cea85f754] to [18864abbc2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
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
#import "OFList.h"
#import "OFString.h"
#import "OFXMLElement.h"
#import "OFArray.h"

#import "OFEnumerationMutationException.h"
#import "OFInvalidArgumentException.h"






OF_DIRECT_MEMBERS
@interface OFListEnumerator: OFEnumerator
{
	OFList *_list;
	of_list_object_t *_Nullable _current;
	unsigned long _mutations;
	unsigned long *_Nullable _mutationsPtr;
}

- (instancetype)initWithList: (OFList *)list
	    mutationsPointer: (unsigned long *)mutationsPtr;
@end



















@implementation OFList
@synthesize firstListObject = _firstListObject;
@synthesize lastListObject = _lastListObject;

+ (instancetype)list
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		for (OFXMLElement *child in
		    [element elementsForNamespace: OF_SERIALIZATION_NS]) {
			void *pool2 = objc_autoreleasePoolPush();

			[self appendObject: child.objectByDeserializing];

			objc_autoreleasePoolPop(pool2);
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{

	for (of_list_object_t *iter = _firstListObject;
	    iter != NULL; iter = iter->next)
		[iter->object release];




	[super dealloc];
}

- (of_list_object_t *)appendObject: (id)object
{
	of_list_object_t *listObject;

	listObject = [self allocMemoryWithSize: sizeof(of_list_object_t)];
	listObject->object = [object retain];
	listObject->next = NULL;
	listObject->previous = _lastListObject;

	if (_lastListObject != NULL)
		_lastListObject->next = listObject;

	_lastListObject = listObject;

	if (_firstListObject == NULL)
		_firstListObject = listObject;

	_count++;
	_mutations++;

	return listObject;
}

- (of_list_object_t *)prependObject: (id)object
{
	of_list_object_t *listObject;


	listObject = [self allocMemoryWithSize: sizeof(of_list_object_t)];




	listObject->object = [object retain];
	listObject->next = _firstListObject;
	listObject->previous = NULL;

	if (_firstListObject != NULL)
		_firstListObject->previous = listObject;

	_firstListObject = listObject;
	if (_lastListObject == NULL)
		_lastListObject = listObject;

	_count++;
	_mutations++;

	return listObject;
}

- (of_list_object_t *)insertObject: (id)object
		  beforeListObject: (of_list_object_t *)listObject
{
	of_list_object_t *newListObject;

	newListObject = [self allocMemoryWithSize: sizeof(of_list_object_t)];
	newListObject->object = [object retain];
	newListObject->next = listObject;
	newListObject->previous = listObject->previous;

	if (listObject->previous != NULL)
		listObject->previous->next = newListObject;




	listObject->previous = newListObject;

	if (listObject == _firstListObject)
		_firstListObject = newListObject;

	_count++;
	_mutations++;

	return newListObject;
}

- (of_list_object_t *)insertObject: (id)object
		   afterListObject: (of_list_object_t *)listObject
{
	of_list_object_t *newListObject;

	newListObject = [self allocMemoryWithSize: sizeof(of_list_object_t)];
	newListObject->object = [object retain];
	newListObject->next = listObject->next;
	newListObject->previous = listObject;

	if (listObject->next != NULL)
		listObject->next->previous = newListObject;

	listObject->next = newListObject;

	if (listObject == _lastListObject)
		_lastListObject = newListObject;

	_count++;
	_mutations++;

	return newListObject;
}

- (void)removeListObject: (of_list_object_t *)listObject
{
	if (listObject->previous != NULL)
		listObject->previous->next = listObject->next;
	if (listObject->next != NULL)
		listObject->next->previous = listObject->previous;

	if (_firstListObject == listObject)
		_firstListObject = listObject->next;
	if (_lastListObject == listObject)
		_lastListObject = listObject->previous;

	_count--;
	_mutations++;

	[listObject->object release];

	[self freeMemory: listObject];
}

- (id)firstObject
{
	return (_firstListObject != NULL ? _firstListObject->object : nil);
}

- (id)lastObject
{
	return (_lastListObject != NULL ? _lastListObject->object : nil);
}

- (size_t)count
{
	return _count;
}

- (bool)isEqual: (id)object
{
	OFList *list;
	of_list_object_t *iter, *iter2;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFList class]])
		return false;

	list = object;

	if (list.count != _count)
		return false;

	for (iter = _firstListObject, iter2 = list.firstListObject;
	    iter != NULL && iter2 != NULL;
	    iter = iter->next, iter2 = iter2->next)
		if (![iter->object isEqual: iter2->object])
			return false;

	/* One is bigger than the other even though we checked the count */
	assert(iter == NULL && iter2 == NULL);

	return true;
}

- (bool)containsObject: (id)object
{
	if (_count == 0)
		return false;

	for (of_list_object_t *iter = _firstListObject;
	    iter != NULL; iter = iter->next)
		if ([iter->object isEqual: object])
			return true;

	return false;
}

- (bool)containsObjectIdenticalTo: (id)object
{
	if (_count == 0)
		return false;

	for (of_list_object_t *iter = _firstListObject;
	    iter != NULL; iter = iter->next)
		if (iter->object == object)
			return true;

	return false;
}

- (void)removeAllObjects
{
	of_list_object_t *iter, *next;

	_mutations++;

	for (iter = _firstListObject; iter != NULL; iter = next) {
		next = iter->next;

		[iter->object release];

		[self freeMemory: iter];
	}

	_firstListObject = _lastListObject = NULL;
}

- (id)copy
{
	OFList *copy = [[[self class] alloc] init];
	of_list_object_t *listObject, *previous;

	listObject = NULL;
	previous = NULL;

	@try {
		for (of_list_object_t *iter = _firstListObject;
		    iter != NULL; iter = iter->next) {
			listObject = [copy allocMemoryWithSize:
			    sizeof(of_list_object_t)];
			listObject->object = [iter->object retain];
			listObject->next = NULL;
			listObject->previous = previous;

			if (copy->_firstListObject == NULL)
				copy->_firstListObject = listObject;
			if (previous != NULL)
				previous->next = listObject;

			copy->_count++;

			previous = listObject;
		}
	} @catch (id e) {
		[copy release];
		@throw e;
	}

	copy->_lastListObject = listObject;

	return copy;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	for (of_list_object_t *iter = _firstListObject;
	    iter != NULL; iter = iter->next)
		OF_HASH_ADD_HASH(hash, [iter->object hash]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	OFMutableString *ret;

	if (_count == 0)
		return @"[]";

	ret = [OFMutableString stringWithString: @"[\n"];

	for (of_list_object_t *iter = _firstListObject;
	    iter != NULL; iter = iter->next) {
		void *pool = objc_autoreleasePoolPush();

		[ret appendString: [iter->object description]];

		if (iter->next != NULL)
			[ret appendString: @",\n"];

		objc_autoreleasePoolPop(pool);
	}
	[ret replaceOccurrencesOfString: @"\n"
			     withString: @"\n\t"];
	[ret appendString: @"\n]"];

	[ret makeImmutable];

	return ret;
}

- (OFXMLElement *)XMLElementBySerializing
{
	OFXMLElement *element =
	    [OFXMLElement elementWithName: self.className
				namespace: OF_SERIALIZATION_NS];

	for (of_list_object_t *iter = _firstListObject;
	    iter != NULL; iter = iter->next) {
		void *pool = objc_autoreleasePoolPush();

		[element addChild: [iter->object XMLElementBySerializing]];

		objc_autoreleasePoolPop(pool);
	}

	return element;
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	of_list_object_t *listObject;

	memcpy(&listObject, state->extra, sizeof(listObject));

	state->itemsPtr = objects;
	state->mutationsPtr = &_mutations;

	if (state->state == 0) {
		listObject = _firstListObject;
		state->state = 1;
	}

	for (int i = 0; i < count; i++) {
		if (listObject == NULL)
			return i;

		objects[i] = listObject->object;
		listObject = listObject->next;
	}

	memcpy(state->extra, &listObject, sizeof(listObject));

	return count;
}

- (OFEnumerator *)objectEnumerator
{
	return [[[OFListEnumerator alloc]
		initWithList: self
	    mutationsPointer: &_mutations] autorelease];

}
@end

@implementation OFListEnumerator
- (instancetype)initWithList: (OFList *)list
	    mutationsPointer: (unsigned long *)mutationsPtr
{
	self = [super init];

	_list = [list retain];
	_current = _list.firstListObject;
	_mutations = *mutationsPtr;
	_mutationsPtr = mutationsPtr;

	return self;
}

- (void)dealloc







>
>
>
>
>





|








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

|
<














|



|


















>
|
|

>
>
|
>



|

|

<
|
|
|

|
|

|

|
|



<
<
<
<
<
<
|
>
|
|
>
>
>
>
|
|
|

|
|

|
|
|




|


|
<

|

<
|
|
|
<
<
<

>
>
>
|

|
|




|


|
<

|

<
|
|
|

|
|

|

|
|




|


|

|
|
|
|

|
|
|
|




|
<
|




|




|










|












|
















<
|











<
|








|



|
<
<

>
|


|





<
<
<
|


|

<
|
|
|
|

|
|

|



|






|






|

|

<
|
|

|













|










|
<











|

|











|



|

|





|




|


|
|


|






|
<
|
>










|







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
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
#import "OFList.h"
#import "OFString.h"
#import "OFXMLElement.h"
#import "OFArray.h"

#import "OFEnumerationMutationException.h"
#import "OFInvalidArgumentException.h"

struct _OFListItem {
	struct _OFListItem *previous, *next;
	id object;
};

OF_DIRECT_MEMBERS
@interface OFListEnumerator: OFEnumerator
{
	OFList *_list;
	OFListItem _Nullable _current;
	unsigned long _mutations;
	unsigned long *_Nullable _mutationsPtr;
}

- (instancetype)initWithList: (OFList *)list
	    mutationsPointer: (unsigned long *)mutationsPtr;
@end

OFListItem
OFListItemNext(OFListItem listItem)
{
	return listItem->next;
}

OFListItem
OFListItemPrevious(OFListItem listItem)
{
	return listItem->previous;
}

id
OFListItemObject(OFListItem listItem)
{
	return listItem->object;
}

@implementation OFList
@synthesize firstListItem = _firstListItem, lastListItem = _lastListItem;


+ (instancetype)list
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		for (OFXMLElement *child in
		    [element elementsForNamespace: OFSerializationNS]) {
			void *pool2 = objc_autoreleasePoolPush();

			[self appendObject: child.objectByDeserializing];

			objc_autoreleasePoolPop(pool2);
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	OFListItem next;

	for (OFListItem iter = _firstListItem; iter != NULL; iter = next) {
		[iter->object release];
		next = iter->next;
		OFFreeMemory(iter);
	}

	[super dealloc];
}

- (OFListItem)appendObject: (id)object
{
	OFListItem listItem = OFAllocMemory(1, sizeof(*listItem));


	listItem->object = [object retain];
	listItem->next = NULL;
	listItem->previous = _lastListItem;

	if (_lastListItem != NULL)
		_lastListItem->next = listItem;

	_lastListItem = listItem;

	if (_firstListItem == NULL)
		_firstListItem = listItem;

	_count++;
	_mutations++;







	return listItem;
}

- (OFListItem)prependObject: (id)object
{
	OFListItem listItem = OFAllocMemory(1, sizeof(*listItem));

	listItem->object = [object retain];
	listItem->next = _firstListItem;
	listItem->previous = NULL;

	if (_firstListItem != NULL)
		_firstListItem->previous = listItem;

	_firstListItem = listItem;
	if (_lastListItem == NULL)
		_lastListItem = listItem;

	_count++;
	_mutations++;

	return listItem;
}

- (OFListItem)insertObject: (id)object beforeListItem: (OFListItem)listItem

{
	OFListItem newListItem = OFAllocMemory(1, sizeof(*newListItem));


	newListItem->object = [object retain];
	newListItem->next = listItem;
	newListItem->previous = listItem->previous;




	if (listItem->previous != NULL)
		listItem->previous->next = newListItem;

	listItem->previous = newListItem;

	if (listItem == _firstListItem)
		_firstListItem = newListItem;

	_count++;
	_mutations++;

	return newListItem;
}

- (OFListItem)insertObject: (id)object afterListItem: (OFListItem)listItem

{
	OFListItem newListItem = OFAllocMemory(1, sizeof(*newListItem));


	newListItem->object = [object retain];
	newListItem->next = listItem->next;
	newListItem->previous = listItem;

	if (listItem->next != NULL)
		listItem->next->previous = newListItem;

	listItem->next = newListItem;

	if (listItem == _lastListItem)
		_lastListItem = newListItem;

	_count++;
	_mutations++;

	return newListItem;
}

- (void)removeListItem: (OFListItem)listItem
{
	if (listItem->previous != NULL)
		listItem->previous->next = listItem->next;
	if (listItem->next != NULL)
		listItem->next->previous = listItem->previous;

	if (_firstListItem == listItem)
		_firstListItem = listItem->next;
	if (_lastListItem == listItem)
		_lastListItem = listItem->previous;

	_count--;
	_mutations++;

	[listItem->object release];

	OFFreeMemory(listItem);
}

- (id)firstObject
{
	return (_firstListItem != NULL ? _firstListItem->object : nil);
}

- (id)lastObject
{
	return (_lastListItem != NULL ? _lastListItem->object : nil);
}

- (size_t)count
{
	return _count;
}

- (bool)isEqual: (id)object
{
	OFList *list;
	OFListItem iter, iter2;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFList class]])
		return false;

	list = object;

	if (list.count != _count)
		return false;

	for (iter = _firstListItem, iter2 = list.firstListItem;
	    iter != NULL && iter2 != NULL;
	    iter = iter->next, iter2 = iter2->next)
		if (![iter->object isEqual: iter2->object])
			return false;

	/* One is bigger than the other even though we checked the count */
	assert(iter == NULL && iter2 == NULL);

	return true;
}

- (bool)containsObject: (id)object
{
	if (_count == 0)
		return false;


	for (OFListItem iter = _firstListItem; iter != NULL; iter = iter->next)
		if ([iter->object isEqual: object])
			return true;

	return false;
}

- (bool)containsObjectIdenticalTo: (id)object
{
	if (_count == 0)
		return false;


	for (OFListItem iter = _firstListItem; iter != NULL; iter = iter->next)
		if (iter->object == object)
			return true;

	return false;
}

- (void)removeAllObjects
{
	OFListItem next;

	_mutations++;

	for (OFListItem iter = _firstListItem; iter != NULL; iter = next) {


		[iter->object release];
		next = iter->next;
		OFFreeMemory(iter);
	}

	_firstListItem = _lastListItem = NULL;
}

- (id)copy
{
	OFList *copy = [[[self class] alloc] init];



	OFListItem listItem = NULL, previous = NULL;

	@try {
		for (OFListItem iter = _firstListItem;
		    iter != NULL; iter = iter->next) {

			listItem = OFAllocMemory(1, sizeof(*listItem));
			listItem->object = [iter->object retain];
			listItem->next = NULL;
			listItem->previous = previous;

			if (copy->_firstListItem == NULL)
				copy->_firstListItem = listItem;
			if (previous != NULL)
				previous->next = listItem;

			copy->_count++;

			previous = listItem;
		}
	} @catch (id e) {
		[copy release];
		@throw e;
	}

	copy->_lastListItem = listItem;

	return copy;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);


	for (OFListItem iter = _firstListItem; iter != NULL; iter = iter->next)
		OFHashAddHash(&hash, [iter->object hash]);

	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)description
{
	OFMutableString *ret;

	if (_count == 0)
		return @"[]";

	ret = [OFMutableString stringWithString: @"[\n"];

	for (OFListItem iter = _firstListItem;
	    iter != NULL; iter = iter->next) {
		void *pool = objc_autoreleasePoolPush();

		[ret appendString: [iter->object description]];

		if (iter->next != NULL)
			[ret appendString: @",\n"];

		objc_autoreleasePoolPop(pool);
	}
	[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];

	[ret appendString: @"\n]"];

	[ret makeImmutable];

	return ret;
}

- (OFXMLElement *)XMLElementBySerializing
{
	OFXMLElement *element =
	    [OFXMLElement elementWithName: self.className
				namespace: OFSerializationNS];

	for (OFListItem iter = _firstListItem;
	    iter != NULL; iter = iter->next) {
		void *pool = objc_autoreleasePoolPush();

		[element addChild: [iter->object XMLElementBySerializing]];

		objc_autoreleasePoolPop(pool);
	}

	return element;
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	OFListItem listItem;

	memcpy(&listItem, state->extra, sizeof(listItem));

	state->itemsPtr = objects;
	state->mutationsPtr = &_mutations;

	if (state->state == 0) {
		listItem = _firstListItem;
		state->state = 1;
	}

	for (int i = 0; i < count; i++) {
		if (listItem == NULL)
			return i;

		objects[i] = listItem->object;
		listItem = listItem->next;
	}

	memcpy(state->extra, &listItem, sizeof(listItem));

	return count;
}

- (OFEnumerator *)objectEnumerator
{
	return [[[OFListEnumerator alloc] initWithList: self

				      mutationsPointer: &_mutations]
	    autorelease];
}
@end

@implementation OFListEnumerator
- (instancetype)initWithList: (OFList *)list
	    mutationsPointer: (unsigned long *)mutationsPtr
{
	self = [super init];

	_list = [list retain];
	_current = _list.firstListItem;
	_mutations = *mutationsPtr;
	_mutationsPtr = mutationsPtr;

	return self;
}

- (void)dealloc

Modified src/OFLocale.h from [24af3246f9] to [fcc8f077d2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *
 * @brief A class for querying the locale and retrieving localized strings.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFLocale: OFObject
{
	OFString *_Nullable _language, *_Nullable _territory;
	of_string_encoding_t _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) of_string_encoding_t 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`.







|









|







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
 *
 * @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`.
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
 * @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) of_string_encoding_t encoding;

/**
 * @brief The decimal point of the system's locale.
 */
@property (readonly, nonatomic) OFString *decimalPoint;

/**







|







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

/**
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
 * 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
 */
+ (of_string_encoding_t)encoding;

/**
 * @brief Returns the decimal point of the system's locale.
 *
 * @return The decimal point of the system's locale
 */
+ (nullable OFString *)decimalPoint;







|







123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
 * 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;
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
 * @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 be come the current locale, and
 *	    call this method.
 */
- (instancetype)init;

#ifdef OF_HAVE_FILES
/**
 * @brief Adds a directory to scan for language files.







|







148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
 * @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.

Modified src/OFLocale.m from [b6b16348ac] to [ccac2d9a08].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#endif

static OFLocale *currentLocale = nil;
static OFDictionary *operatorPrecedences = nil;

#ifndef OF_AMIGAOS
static void
parseLocale(char *locale, of_string_encoding_t *encoding,
    OFString **language, OFString **territory)
{
	if ((locale = of_strdup(locale)) == NULL)
		return;

	@try {
		const of_string_encoding_t enc = OF_STRING_ENCODING_ASCII;
		char *tmp;

		/* We don't care for extras behind the @ */
		if ((tmp = strrchr(locale, '@')) != NULL)
			*tmp = '\0';

		/* Encoding */
		if ((tmp = strrchr(locale, '.')) != NULL) {
			*tmp++ = '\0';

			@try {
				if (encoding != NULL)
					*encoding = of_string_parse_encoding(
					    [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 {
		free(locale);
	}
}
#endif

static bool
evaluateCondition(OFString *condition, OFDictionary *variables)
{

	OFMutableArray *tokens, *operators, *stack;

	/* Empty condition is the fallback that's always true */
	if (condition.length == 0)
		return true;

	/*
	 * Dirty hack to allow not needing spaces after "!" or "(" and spaces
	 * before ")".
	 * TODO: Replace with a proper tokenizer.
	 */
	condition = [condition stringByReplacingOccurrencesOfString: @"!"
							 withString: @"! "];
	condition = [condition stringByReplacingOccurrencesOfString: @"("
							 withString: @"( "];
	condition = [condition stringByReplacingOccurrencesOfString: @")"
							 withString: @" )"];

	/* Substitute variables and convert to RPN first */
	tokens = [OFMutableArray array];
	operators = [OFMutableArray array];
	for (OFString *token in [condition
	    componentsSeparatedByString: @" "
				options: OF_STRING_SKIP_EMPTY]) {
		unsigned precedence;
		of_unichar_t c;

		if ([token isEqual: @"("]) {
			[operators addObject: @"("];
			continue;
		}

		if ([token isEqual: @")"]) {







|


|
<


|












|



















|





|

>











<
|
<
|
<
|






|

|







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
#endif

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;

		/* We don't care for extras behind the @ */
		if ((tmp = strrchr(locale, '@')) != NULL)
			*tmp = '\0';

		/* Encoding */
		if ((tmp = strrchr(locale, '.')) != NULL) {
			*tmp++ = '\0';

			@try {
				if (encoding != NULL)
					*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
evaluateCondition(OFString *condition_, OFDictionary *variables)
{
	OFMutableString *condition = [[condition_ mutableCopy] autorelease];
	OFMutableArray *tokens, *operators, *stack;

	/* Empty condition is the fallback that's always true */
	if (condition.length == 0)
		return true;

	/*
	 * Dirty hack to allow not needing spaces after "!" or "(" and spaces
	 * before ")".
	 * TODO: Replace with a proper tokenizer.
	 */

	[condition replaceOccurrencesOfString: @"!" withString: @"! "];

	[condition replaceOccurrencesOfString: @"(" withString: @"( "];

	[condition replaceOccurrencesOfString: @")" withString: @" )"];

	/* Substitute variables and convert to RPN first */
	tokens = [OFMutableArray array];
	operators = [OFMutableArray array];
	for (OFString *token in [condition
	    componentsSeparatedByString: @" "
				options: OFStringSkipEmptyComponents]) {
		unsigned precedence;
		OFUnichar c;

		if ([token isEqual: @"("]) {
			[operators addObject: @"("];
			continue;
		}

		if ([token isEqual: @")"]) {
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
				var = [OFNumber numberWithBool:
				    [first isEqual: second]];
			else if ([token isEqual: @"!="])
				var = [OFNumber numberWithBool:
				    ![first isEqual: second]];
			else if ([token isEqual: @"<"])
				var = [OFNumber numberWithBool: [first
				    compare: second] == OF_ORDERED_ASCENDING];
			else if ([token isEqual: @"<="])
				var = [OFNumber numberWithBool: [first
				    compare: second] != OF_ORDERED_DESCENDING];
			else if ([token isEqual: @">"])
				var = [OFNumber numberWithBool: [first
				    compare: second] == OF_ORDERED_DESCENDING];
			else if ([token isEqual: @">="])
				var = [OFNumber numberWithBool: [first
				    compare: second] != OF_ORDERED_ASCENDING];
			else if ([token isEqual: @"+"])
				var = [OFNumber numberWithDouble:
				    [first doubleValue] + [second doubleValue]];
			else if ([token isEqual: @"%"])
				var = [OFNumber numberWithLongLong:
				    [first longLongValue] %
				    [second longLongValue]];
			else if ([token isEqual: @"&&"])
				var = [OFNumber numberWithBool:
				    [first boolValue] && [second boolValue]];
			else if ([token isEqual: @"||"])
				var = [OFNumber numberWithBool:
				    [first boolValue] || [second boolValue]];
			else
				OF_ENSURE(0);

			[stack replaceObjectAtIndex: stackSize - 2
					 withObject: var];
			[stack removeLastObject];
		} else if (precedence == 1) {
			stackSize = stack.count;
			first = stack.lastObject;

			if ([token isEqual: @"!"])
				var = [OFNumber numberWithBool:
				    ![first boolValue]];
			else if ([token isEqual: @"is_real"])
				var = [OFNumber numberWithBool:
				    ([first doubleValue] !=
				    [first longLongValue])];
			else
				OF_ENSURE(0);

			[stack replaceObjectAtIndex: stackSize - 1
					 withObject: var];
		} else
			[stack addObject: token];
	}








|


|


|


|














|
















|







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
				var = [OFNumber numberWithBool:
				    [first isEqual: second]];
			else if ([token isEqual: @"!="])
				var = [OFNumber numberWithBool:
				    ![first isEqual: second]];
			else if ([token isEqual: @"<"])
				var = [OFNumber numberWithBool: [first
				    compare: second] == OFOrderedAscending];
			else if ([token isEqual: @"<="])
				var = [OFNumber numberWithBool: [first
				    compare: second] != OFOrderedDescending];
			else if ([token isEqual: @">"])
				var = [OFNumber numberWithBool: [first
				    compare: second] == OFOrderedDescending];
			else if ([token isEqual: @">="])
				var = [OFNumber numberWithBool: [first
				    compare: second] != OFOrderedAscending];
			else if ([token isEqual: @"+"])
				var = [OFNumber numberWithDouble:
				    [first doubleValue] + [second doubleValue]];
			else if ([token isEqual: @"%"])
				var = [OFNumber numberWithLongLong:
				    [first longLongValue] %
				    [second longLongValue]];
			else if ([token isEqual: @"&&"])
				var = [OFNumber numberWithBool:
				    [first boolValue] && [second boolValue]];
			else if ([token isEqual: @"||"])
				var = [OFNumber numberWithBool:
				    [first boolValue] || [second boolValue]];
			else
				OFEnsure(0);

			[stack replaceObjectAtIndex: stackSize - 2
					 withObject: var];
			[stack removeLastObject];
		} else if (precedence == 1) {
			stackSize = stack.count;
			first = stack.lastObject;

			if ([token isEqual: @"!"])
				var = [OFNumber numberWithBool:
				    ![first boolValue]];
			else if ([token isEqual: @"is_real"])
				var = [OFNumber numberWithBool:
				    ([first doubleValue] !=
				    [first longLongValue])];
			else
				OFEnsure(0);

			[stack replaceObjectAtIndex: stackSize - 1
					 withObject: var];
		} else
			[stack addObject: token];
	}

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
}

+ (OFString *)territory
{
	return currentLocale.territory;
}

+ (of_string_encoding_t)encoding
{
	return currentLocale.encoding;
}

+ (OFString *)decimalPoint
{
	return currentLocale.decimalPoint;







|







346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
}

+ (OFString *)territory
{
	return currentLocale.territory;
}

+ (OFStringEncoding)encoding
{
	return currentLocale.encoding;
}

+ (OFString *)decimalPoint
{
	return currentLocale.decimalPoint;
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
#ifndef OF_AMIGAOS
		char *locale, *messagesLocale = NULL;

		if (currentLocale != nil)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		_encoding = OF_STRING_ENCODING_UTF_8;
		_decimalPoint = @".";
		_localizedStrings = [[OFMutableArray alloc] init];

		if ((locale = setlocale(LC_ALL, "")) != NULL)
			_decimalPoint = [[OFString alloc]
			    initWithCString: localeconv()->decimal_point
				   encoding: _encoding];







|







375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
#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];
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
# if defined(OF_MORPHOS)
		if (GetVar("CODEPAGE", buffer, sizeof(buffer), 0) > 0) {
# elif defined(OF_AMIGAOS4)
		if (GetVar("Charset", buffer, sizeof(buffer), 0) > 0) {
# else
		if (0) {
# endif
			of_string_encoding_t ASCII = OF_STRING_ENCODING_ASCII;

			@try {
				_encoding = of_string_parse_encoding(
				    [OFString stringWithCString: buffer
						       encoding: ASCII]);
			} @catch (OFInvalidArgumentException *e) {
				_encoding = OF_STRING_ENCODING_ISO_8859_1;
			}
		} else
			_encoding = OF_STRING_ENCODING_ISO_8859_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







|


|



|


|







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
# if defined(OF_MORPHOS)
		if (GetVar("CODEPAGE", buffer, sizeof(buffer), 0) > 0) {
# elif defined(OF_AMIGAOS4)
		if (GetVar("Charset", buffer, sizeof(buffer), 0) > 0) {
# else
		if (0) {
# endif
			OFStringEncoding ASCII = OFStringEncodingASCII;

			@try {
				_encoding = OFStringEncodingParseName(
				    [OFString stringWithCString: buffer
						       encoding: ASCII]);
			} @catch (OFInvalidArgumentException *e) {
				_encoding = OFStringEncodingISO8859_1;
			}
		} 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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471

		if ((locale = OpenLocale(NULL)) != NULL) {
			@try {
				uint32_t territory;
				size_t length;

				territory =
				    OF_BSWAP32_IF_LE(locale->loc_CountryCode);

				for (length = 0; length < 4; length++)
					if (((char *)&territory)[length] == 0)
						break;

				_territory = [[OFString alloc]
				    initWithCString: (char *)&territory







|







452
453
454
455
456
457
458
459
460
461
462
463
464
465
466

		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
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
	OFConstantString *name;
	const char *UTF8String = NULL;
	size_t last, UTF8StringLength;
	int state = 0;

	variables = [OFMutableDictionary dictionary];
	while ((name = va_arg(arguments, OFConstantString *)) != nil)
		[variables setObject: va_arg(arguments, id)
			      forKey: name];

	for (OFDictionary *strings in _localizedStrings) {
		id string = [strings objectForKey: ID];

		if (string == nil)
			continue;








|
<







564
565
566
567
568
569
570
571

572
573
574
575
576
577
578
	OFConstantString *name;
	const char *UTF8String = NULL;
	size_t last, UTF8StringLength;
	int state = 0;

	variables = [OFMutableDictionary dictionary];
	while ((name = va_arg(arguments, OFConstantString *)) != nil)
		[variables setObject: va_arg(arguments, id) forKey: name];


	for (OFDictionary *strings in _localizedStrings) {
		id string = [strings objectForKey: ID];

		if (string == nil)
			continue;

Modified src/OFLocking.h from [9eac423122] to [ae2e49ea45].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMD5Hash.h from [115122e9c6] to [cdd5a3515e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCryptoHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFMD5Hash OFMD5Hash.h ObjFW/OFMD5Hash.h
 *
 * @brief A class which provides methods to create an MD5 hash.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFMD5Hash: OFObject <OFCryptoHash>
{
	OFSecureData *_iVarsData;
	struct of_md5_hash_ivars {
		uint32_t state[4];
		uint64_t bits;
		union of_md5_hash_buffer {
			unsigned char bytes[64];
			uint32_t words[16];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

<
<
|













|











|


|


|











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


 * 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 "OFCryptographicHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFMD5Hash OFMD5Hash.h ObjFW/OFMD5Hash.h
 *
 * @brief A class which provides methods to create an MD5 hash.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFMD5Hash: OFObject <OFCryptographicHash>
{
	OFSecureData *_iVarsData;
	struct {
		uint32_t state[4];
		uint64_t bits;
		union {
			unsigned char bytes[64];
			uint32_t words[16];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFMD5Hash.m from [f7acc8c4b8] to [190d96fa7c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25

26
27
28
29

30
31
32
33
34
35
36

#include <string.h>

#import "OFMD5Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"

#import "OFOutOfRangeException.h"

#define DIGEST_SIZE 16
#define BLOCK_SIZE 64


OF_DIRECT_MEMBERS
@interface OFMD5Hash ()
- (void)of_resetState;
@end

#define F(a, b, c) (((a) & (b)) | (~(a) & (c)))







>


<
|
>







17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35

#include <string.h>

#import "OFMD5Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"
#import "OFHashNotCalculatedException.h"
#import "OFOutOfRangeException.h"


static const size_t digestSize = 16;
static const size_t blockSize = 64;

OF_DIRECT_MEMBERS
@interface OFMD5Hash ()
- (void)of_resetState;
@end

#define F(a, b, c) (((a) & (b)) | (~(a) & (c)))
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
};

static OF_INLINE void
byteSwapVectorIfBE(uint32_t *vector, uint_fast8_t length)
{
#ifdef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OF_BSWAP32(vector[i]);
#endif
}

static void
processBlock(uint32_t *state, uint32_t *buffer)
{
	uint32_t new[4];
	uint_fast8_t i = 0;

	new[0] = state[0];
	new[1] = state[1];
	new[2] = state[2];
	new[3] = state[3];

	byteSwapVectorIfBE(buffer, 16);

#define LOOP_BODY(f)							      \
	{								      \
		uint32_t tmp = new[3];					      \
		tmp = new[3];						      \
		new[0] += f(new[1], new[2], new[3]) +			      \
		    buffer[wordOrder[i]] + table[i];			      \
		new[3] = new[2];					      \
		new[2] = new[1];					      \

		new[1] += OF_ROL(new[0], rotateBits[(i % 4) + (i / 16) * 4]); \
		new[0] = tmp;\
	}

	for (; i < 16; i++)
		LOOP_BODY(F)
	for (; i < 32; i++)
		LOOP_BODY(G)
	for (; i < 48; i++)







|
















|
|
|
|
|
|
|
|
>
|
|







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

static OF_INLINE void
byteSwapVectorIfBE(uint32_t *vector, uint_fast8_t length)
{
#ifdef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OFByteSwap32(vector[i]);
#endif
}

static void
processBlock(uint32_t *state, uint32_t *buffer)
{
	uint32_t new[4];
	uint_fast8_t i = 0;

	new[0] = state[0];
	new[1] = state[1];
	new[2] = state[2];
	new[3] = state[3];

	byteSwapVectorIfBE(buffer, 16);

#define LOOP_BODY(f)						\
	{							\
		uint32_t tmp = new[3];				\
		tmp = new[3];					\
		new[0] += f(new[1], new[2], new[3]) +		\
		    buffer[wordOrder[i]] + table[i];		\
		new[3] = new[2];				\
		new[2] = new[1];				\
		new[1] += OFRotateLeft(new[0],			\
		    rotateBits[(i % 4) + (i / 16) * 4]);	\
		new[0] = tmp;					\
	}

	for (; i < 16; i++)
		LOOP_BODY(F)
	for (; i < 32; i++)
		LOOP_BODY(G)
	for (; i < 48; i++)
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

@implementation OFMD5Hash
@synthesize calculated = _calculated;
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

+ (size_t)digestSize
{
	return DIGEST_SIZE;
}

+ (size_t)blockSize
{
	return BLOCK_SIZE;
}

+ (instancetype)cryptoHashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{







|




|


|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

@implementation OFMD5Hash
@synthesize calculated = _calculated;
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

+ (size_t)digestSize
{
	return digestSize;
}

+ (size_t)blockSize
{
	return blockSize;
}

+ (instancetype)hashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
	[_iVarsData release];

	[super dealloc];
}

- (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (size_t)blockSize
{
	return BLOCK_SIZE;
}

- (id)copy
{
	OFMD5Hash *copy = [[OFMD5Hash alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];







|




|







178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
	[_iVarsData release];

	[super dealloc];
}

- (size_t)digestSize
{
	return digestSize;
}

- (size_t)blockSize
{
	return blockSize;
}

- (id)copy
{
	OFMD5Hash *copy = [[OFMD5Hash alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
{
	_iVars->state[0] = 0x67452301;
	_iVars->state[1] = 0xEFCDAB89;
	_iVars->state[2] = 0x98BADCFE;
	_iVars->state[3] = 0x10325476;
}

- (void)updateWithBuffer: (const void *)buffer_
		  length: (size_t)length
{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];








|
<







206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
{
	_iVars->state[0] = 0x67452301;
	_iVars->state[1] = 0xEFCDAB89;
	_iVars->state[2] = 0x98BADCFE;
	_iVars->state[3] = 0x10325476;
}

- (void)updateWithBuffer: (const void *)buffer_ length: (size_t)length

{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (_calculated)


		return (const unsigned char *)_iVars->state;








	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	of_explicit_memset(_iVars->buffer.bytes + _iVars->bufferLength + 1, 0,
	    64 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 56) {
		processBlock(_iVars->state, _iVars->buffer.words);
		of_explicit_memset(_iVars->buffer.bytes, 0, 64);
	}

	_iVars->buffer.words[14] =
	    OF_BSWAP32_IF_BE((uint32_t)(_iVars->bits & 0xFFFFFFFF));
	_iVars->buffer.words[15] =
	    OF_BSWAP32_IF_BE((uint32_t)(_iVars->bits >> 32));

	processBlock(_iVars->state, _iVars->buffer.words);
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	byteSwapVectorIfBE(_iVars->state, 4);
	_calculated = true;

	return (const unsigned char *)_iVars->state;
}

- (void)reset
{
	[self of_resetState];
	_iVars->bits = 0;
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}
@end







|
>
>
|
|
>
>
>
>
>
>
>

|




|



|

|


|


<
<






|




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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (!_calculated)
		@throw [OFHashNotCalculatedException exceptionWithObject: self];

	return (const unsigned char *)_iVars->state;
}

- (void)calculate
{
	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	OFZeroMemory(_iVars->buffer.bytes + _iVars->bufferLength + 1,
	    64 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 56) {
		processBlock(_iVars->state, _iVars->buffer.words);
		OFZeroMemory(_iVars->buffer.bytes, 64);
	}

	_iVars->buffer.words[14] =
	    OFToLittleEndian32((uint32_t)(_iVars->bits & 0xFFFFFFFF));
	_iVars->buffer.words[15] =
	    OFToLittleEndian32((uint32_t)(_iVars->bits >> 32));

	processBlock(_iVars->state, _iVars->buffer.words);
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	byteSwapVectorIfBE(_iVars->state, 4);
	_calculated = true;


}

- (void)reset
{
	[self of_resetState];
	_iVars->bits = 0;
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}
@end

Modified src/OFMapTable+Private.h from [1364bfeb16] to [34b0bddd51].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMapTable.h from [bf028bd496] to [db5052bd42].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFEnumerator.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

/**
 * @struct of_map_table_functions_t OFMapTable.h ObjFW/OFMapTable.h
 *
 * @brief A struct describing the functions to be used by the map table.
 */
struct of_map_table_functions_t {
	/** The function to retain keys / objects */
	void *_Nullable (*_Nullable retain)(void *_Nullable object);
	/** The function to release keys / objects */
	void (*_Nullable release)(void *_Nullable object);
	/** The function to hash keys */
	unsigned long (*_Nullable hash)(void *_Nullable object);
	/** The function to compare keys / objects */
	bool (*_Nullable equal)(void *_Nullable object1,
	    void *_Nullable object2);
};
typedef struct of_map_table_functions_t of_map_table_functions_t;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block for enumerating an OFMapTable.
 *
 * @param key The current key
 * @param object The current object
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration
 */
typedef void (^of_map_table_enumeration_block_t)(void *_Nullable key,
    void *_Nullable object, bool *stop);

/**
 * @brief A block for replacing objects in an OFMapTable.
 *
 * @param key The key of the object to replace
 * @param object The object to replace
 * @return The object to replace the object with
 */
typedef void *_Nullable (^of_map_table_replace_block_t)(void *_Nullable key,
    void *_Nullable object);
#endif

@class OFMapTableEnumerator;

/**
 * @class OFMapTable OFMapTable.h ObjFW/OFMapTable.h
 *
 * @brief A class similar to OFDictionary, but providing more options how keys
 *	  and objects should be retained, released, compared and hashed.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFMapTable: OFObject <OFCopying, OFFastEnumeration>
{
	of_map_table_functions_t _keyFunctions, _objectFunctions;
	struct of_map_table_bucket *_Nonnull *_Nullable _buckets;
	unsigned long _count, _capacity;
	unsigned char _rotate;
	unsigned long _mutations;
}

/**
 * @brief The key functions used by the map table.
 */
@property (readonly, nonatomic) of_map_table_functions_t keyFunctions;

/**
 * @brief The object functions used by the map table.
 */
@property (readonly, nonatomic) of_map_table_functions_t objectFunctions;

/**
 * @brief The number of objects in the map table.
 */
@property (readonly, nonatomic) size_t count;

/**
 * @brief Creates a new OFMapTable with the specified key and object functions.
 *
 * @param keyFunctions A structure of functions for handling keys
 * @param objectFunctions A structure of functions for handling objects
 * @return A new autoreleased OFMapTable
 */
+ (instancetype)mapTableWithKeyFunctions: (of_map_table_functions_t)keyFunctions
			 objectFunctions: (of_map_table_functions_t)
					      objectFunctions;

/**
 * @brief Creates a new OFMapTable with the specified key functions, object
 *	  functions and capacity.
 *
 * @param keyFunctions A structure of functions for handling keys
 * @param objectFunctions A structure of functions for handling objects
 * @param capacity A hint about the count of elements expected to be in the map
 *	  table
 * @return A new autoreleased OFMapTable
 */
+ (instancetype)mapTableWithKeyFunctions: (of_map_table_functions_t)keyFunctions
			 objectFunctions: (of_map_table_functions_t)
					      objectFunctions
				capacity: (size_t)capacity;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFMapTable with the specified key
 *	  and object functions.
 *
 * @param keyFunctions A structure of functions for handling keys
 * @param objectFunctions A structure of functions for handling objects
 * @return An initialized OFMapTable
 */
- (instancetype)initWithKeyFunctions: (of_map_table_functions_t)keyFunctions
		     objectFunctions: (of_map_table_functions_t)objectFunctions;

/**
 * @brief Initializes an already allocated OFMapTable with the specified key
 *	  functions, object functions and capacity.
 *
 * @param keyFunctions A structure of functions for handling keys
 * @param objectFunctions A structure of functions for handling objects
 * @param capacity A hint about the count of elements expected to be in the map
 *	  table
 * @return An initialized OFMapTable
 */
- (instancetype)initWithKeyFunctions: (of_map_table_functions_t)keyFunctions
		     objectFunctions: (of_map_table_functions_t)objectFunctions
			    capacity: (size_t)capacity
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Returns the object for the given key or NULL if the key was not found.
 *
 * @param key The key whose object should be returned
 * @return The object for the given key or NULL if the key was not found
 */
- (nullable void *)objectForKey: (void *)key;

/**
 * @brief Sets an object for a key.
 *
 * @param key The key to set
 * @param object The object to set the key to
 */
- (void)setObject: (nullable void *)object
	   forKey: (nullable void *)key;

/**
 * @brief Removes the object for the specified key from the map table.
 *
 * @param key The key whose object should be removed
 */
- (void)removeObjectForKey: (nullable void *)key;







|



|









<
|










|









|














|
|
|







|




|













|
<
|











|
<
|












|
|











|
|

















|
<







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
#import "OFEnumerator.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

/**
 * @struct OFMapTableFunctions OFMapTable.h ObjFW/OFMapTable.h
 *
 * @brief A struct describing the functions to be used by the map table.
 */
typedef struct {
	/** The function to retain keys / objects */
	void *_Nullable (*_Nullable retain)(void *_Nullable object);
	/** The function to release keys / objects */
	void (*_Nullable release)(void *_Nullable object);
	/** The function to hash keys */
	unsigned long (*_Nullable hash)(void *_Nullable object);
	/** The function to compare keys / objects */
	bool (*_Nullable equal)(void *_Nullable object1,
	    void *_Nullable object2);

} OFMapTableFunctions;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block for enumerating an OFMapTable.
 *
 * @param key The current key
 * @param object The current object
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration
 */
typedef void (^OFMapTableEnumerationBlock)(void *_Nullable key,
    void *_Nullable object, bool *stop);

/**
 * @brief A block for replacing objects in an OFMapTable.
 *
 * @param key The key of the object to replace
 * @param object The object to replace
 * @return The object to replace the object with
 */
typedef void *_Nullable (^OFMapTableReplaceBlock)(void *_Nullable key,
    void *_Nullable object);
#endif

@class OFMapTableEnumerator;

/**
 * @class OFMapTable OFMapTable.h ObjFW/OFMapTable.h
 *
 * @brief A class similar to OFDictionary, but providing more options how keys
 *	  and objects should be retained, released, compared and hashed.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFMapTable: OFObject <OFCopying, OFFastEnumeration>
{
	OFMapTableFunctions _keyFunctions, _objectFunctions;
	struct OFMapTableBucket *_Nonnull *_Nullable _buckets;
	uint32_t _count, _capacity;
	unsigned char _rotate;
	unsigned long _mutations;
}

/**
 * @brief The key functions used by the map table.
 */
@property (readonly, nonatomic) OFMapTableFunctions keyFunctions;

/**
 * @brief The object functions used by the map table.
 */
@property (readonly, nonatomic) OFMapTableFunctions objectFunctions;

/**
 * @brief The number of objects in the map table.
 */
@property (readonly, nonatomic) size_t count;

/**
 * @brief Creates a new OFMapTable with the specified key and object functions.
 *
 * @param keyFunctions A structure of functions for handling keys
 * @param objectFunctions A structure of functions for handling objects
 * @return A new autoreleased OFMapTable
 */
+ (instancetype)mapTableWithKeyFunctions: (OFMapTableFunctions)keyFunctions

			 objectFunctions: (OFMapTableFunctions)objectFunctions;

/**
 * @brief Creates a new OFMapTable with the specified key functions, object
 *	  functions and capacity.
 *
 * @param keyFunctions A structure of functions for handling keys
 * @param objectFunctions A structure of functions for handling objects
 * @param capacity A hint about the count of elements expected to be in the map
 *	  table
 * @return A new autoreleased OFMapTable
 */
+ (instancetype)mapTableWithKeyFunctions: (OFMapTableFunctions)keyFunctions

			 objectFunctions: (OFMapTableFunctions)objectFunctions
				capacity: (size_t)capacity;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFMapTable with the specified key
 *	  and object functions.
 *
 * @param keyFunctions A structure of functions for handling keys
 * @param objectFunctions A structure of functions for handling objects
 * @return An initialized OFMapTable
 */
- (instancetype)initWithKeyFunctions: (OFMapTableFunctions)keyFunctions
		     objectFunctions: (OFMapTableFunctions)objectFunctions;

/**
 * @brief Initializes an already allocated OFMapTable with the specified key
 *	  functions, object functions and capacity.
 *
 * @param keyFunctions A structure of functions for handling keys
 * @param objectFunctions A structure of functions for handling objects
 * @param capacity A hint about the count of elements expected to be in the map
 *	  table
 * @return An initialized OFMapTable
 */
- (instancetype)initWithKeyFunctions: (OFMapTableFunctions)keyFunctions
		     objectFunctions: (OFMapTableFunctions)objectFunctions
			    capacity: (size_t)capacity
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Returns the object for the given key or NULL if the key was not found.
 *
 * @param key The key whose object should be returned
 * @return The object for the given key or NULL if the key was not found
 */
- (nullable void *)objectForKey: (void *)key;

/**
 * @brief Sets an object for a key.
 *
 * @param key The key to set
 * @param object The object to set the key to
 */
- (void)setObject: (nullable void *)object forKey: (nullable void *)key;


/**
 * @brief Removes the object for the specified key from the map table.
 *
 * @param key The key whose object should be removed
 */
- (void)removeObjectForKey: (nullable void *)key;
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

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each key / object pair.
 *
 * @param block The block to execute for each key / object pair.
 */
- (void)enumerateKeysAndObjectsUsingBlock:
    (of_map_table_enumeration_block_t)block;

/**
 * @brief Replaces each object with the object returned by the block.
 *
 * @param block The block which returns a new object for each object
 */
- (void)replaceObjectsUsingBlock: (of_map_table_replace_block_t)block;
#endif
@end

/**
 * @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 of_map_table_bucket *_Nonnull *_Nullable _buckets;
	unsigned long _capacity, _mutations, *_Nullable _mutationsPtr;
	unsigned long _position;
}

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Returns a pointer to the next object, or NULL if the enumeration
 *	  finished.







|
<






|












|
|
|







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

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each key / object pair.
 *
 * @param block The block to execute for each key / object pair.
 */
- (void)enumerateKeysAndObjectsUsingBlock: (OFMapTableEnumerationBlock)block;


/**
 * @brief Replaces each object with the object returned by the block.
 *
 * @param block The block which returns a new object for each object
 */
- (void)replaceObjectsUsingBlock: (OFMapTableReplaceBlock)block;
#endif
@end

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

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Returns a pointer to the next object, or NULL if the enumeration
 *	  finished.

Modified src/OFMapTable.m from [8775708778] to [155910f8b1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
26
27
28
29
30
31
32

33

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

#import "OFEnumerationMutationException.h"
#import "OFInvalidArgumentException.h"
#import "OFOutOfRangeException.h"


#define MIN_CAPACITY 16


struct of_map_table_bucket {
	void *key, *object;
	unsigned long hash;
};
static struct of_map_table_bucket deleted = { 0 };

static void *
defaultRetain(void *object)
{
	return object;
}








>
|
>

|

|

|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#import "OFMapTable+Private.h"
#import "OFEnumerator.h"

#import "OFEnumerationMutationException.h"
#import "OFInvalidArgumentException.h"
#import "OFOutOfRangeException.h"

extern uint32_t OFHashSeed;

static const uint32_t minCapacity = 16;

struct OFMapTableBucket {
	void *key, *object;
	uint32_t hash;
};
static struct OFMapTableBucket deletedBucket = { 0 };

static void *
defaultRetain(void *object)
{
	return object;
}

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

static bool
defaultEqual(void *object1, void *object2)
{
	return (object1 == object2);
}

OF_DIRECT_MEMBERS
@interface OFMapTable ()
- (void)of_setObject: (void *)object
	      forKey: (void *)key
		hash: (unsigned long)hash;
@end

OF_DIRECT_MEMBERS
@interface OFMapTableEnumerator ()
- (instancetype)of_initWithMapTable: (OFMapTable *)mapTable
			    buckets: (struct of_map_table_bucket **)buckets
			   capacity: (unsigned long)capacity
		   mutationsPointer: (unsigned long *)mutationsPtr
    OF_METHOD_FAMILY(init);
@end

@interface OFMapTableKeyEnumerator: OFMapTableEnumerator
@end

@interface OFMapTableObjectEnumerator: OFMapTableEnumerator
@end

@implementation OFMapTable
@synthesize keyFunctions = _keyFunctions, objectFunctions = _objectFunctions;

+ (instancetype)mapTableWithKeyFunctions: (of_map_table_functions_t)keyFunctions
			 objectFunctions: (of_map_table_functions_t)
					      objectFunctions
{
	return [[[self alloc]
	    initWithKeyFunctions: keyFunctions
		  objectFunctions: objectFunctions] autorelease];
}

+ (instancetype)mapTableWithKeyFunctions: (of_map_table_functions_t)keyFunctions
			 objectFunctions: (of_map_table_functions_t)
					      objectFunctions
				capacity: (size_t)capacity
{
	return [[[self alloc]
	    initWithKeyFunctions: keyFunctions
		 objectFunctions: objectFunctions
			capacity: capacity] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithKeyFunctions: (of_map_table_functions_t)keyFunctions
		     objectFunctions: (of_map_table_functions_t)objectFunctions
{
	return [self initWithKeyFunctions: keyFunctions
			  objectFunctions: objectFunctions
				 capacity: 0];
}

- (instancetype)initWithKeyFunctions: (of_map_table_functions_t)keyFunctions
		     objectFunctions: (of_map_table_functions_t)objectFunctions
			    capacity: (size_t)capacity
{
	self = [super init];

	@try {
		_keyFunctions = keyFunctions;
		_objectFunctions = objectFunctions;







<
<
<
<
<
<
<



|
|













|
<
|






|
<
|













|
|






|
|







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

static bool
defaultEqual(void *object1, void *object2)
{
	return (object1 == object2);
}








OF_DIRECT_MEMBERS
@interface OFMapTableEnumerator ()
- (instancetype)of_initWithMapTable: (OFMapTable *)mapTable
			    buckets: (struct OFMapTableBucket **)buckets
			   capacity: (uint32_t)capacity
		   mutationsPointer: (unsigned long *)mutationsPtr
    OF_METHOD_FAMILY(init);
@end

@interface OFMapTableKeyEnumerator: OFMapTableEnumerator
@end

@interface OFMapTableObjectEnumerator: OFMapTableEnumerator
@end

@implementation OFMapTable
@synthesize keyFunctions = _keyFunctions, objectFunctions = _objectFunctions;

+ (instancetype)mapTableWithKeyFunctions: (OFMapTableFunctions)keyFunctions

			 objectFunctions: (OFMapTableFunctions)objectFunctions
{
	return [[[self alloc]
	    initWithKeyFunctions: keyFunctions
		  objectFunctions: objectFunctions] autorelease];
}

+ (instancetype)mapTableWithKeyFunctions: (OFMapTableFunctions)keyFunctions

			 objectFunctions: (OFMapTableFunctions)objectFunctions
				capacity: (size_t)capacity
{
	return [[[self alloc]
	    initWithKeyFunctions: keyFunctions
		 objectFunctions: objectFunctions
			capacity: capacity] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithKeyFunctions: (OFMapTableFunctions)keyFunctions
		     objectFunctions: (OFMapTableFunctions)objectFunctions
{
	return [self initWithKeyFunctions: keyFunctions
			  objectFunctions: objectFunctions
				 capacity: 0];
}

- (instancetype)initWithKeyFunctions: (OFMapTableFunctions)keyFunctions
		     objectFunctions: (OFMapTableFunctions)objectFunctions
			    capacity: (size_t)capacity
{
	self = [super init];

	@try {
		_keyFunctions = keyFunctions;
		_objectFunctions = objectFunctions;
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
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
		SET_DEFAULT(_objectFunctions.retain, defaultRetain);
		SET_DEFAULT(_objectFunctions.release, defaultRelease);
		SET_DEFAULT(_objectFunctions.hash, defaultHash);
		SET_DEFAULT(_objectFunctions.equal, defaultEqual);

#undef SET_DEFAULT

		if (capacity > ULONG_MAX / sizeof(*_buckets) ||
		    capacity > ULONG_MAX / 8)
			@throw [OFOutOfRangeException exception];

		for (_capacity = 1; _capacity < capacity;) {
			if (_capacity > ULONG_MAX / 2)
				@throw [OFOutOfRangeException exception];

			_capacity *= 2;
		}

		if (capacity * 8 / _capacity >= 6)
			if (_capacity <= ULONG_MAX / 2)
				_capacity *= 2;

		if (_capacity < MIN_CAPACITY)
			_capacity = MIN_CAPACITY;

		_buckets = [self allocZeroedMemoryWithSize: sizeof(*_buckets)
						     count: _capacity];

		if (of_hash_seed != 0)
			_rotate = of_random16() & 31;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	for (unsigned long i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL && _buckets[i] != &deleted) {
			_keyFunctions.release(_buckets[i]->key);
			_objectFunctions.release(_buckets[i]->object);
		}

	}




	[super dealloc];
}


























































































































































- (bool)isEqual: (id)object
{
	OFMapTable *mapTable;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFMapTable class]])
		return false;

	mapTable = object;

	if (mapTable->_count != _count ||
	    mapTable->_keyFunctions.equal != _keyFunctions.equal ||
	    mapTable->_objectFunctions.equal != _objectFunctions.equal)
		return false;

	for (unsigned long i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL && _buckets[i] != &deleted) {
			void *objectIter =
			    [mapTable objectForKey: _buckets[i]->key];

			if (!_objectFunctions.equal(objectIter,
			    _buckets[i]->object))
				return false;
		}
	}

	return true;
}

- (unsigned long)hash
{
	unsigned long hash = 0;

	for (unsigned long i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL && _buckets[i] != &deleted) {
			hash ^= OF_ROR(_buckets[i]->hash, _rotate);
			hash ^= _objectFunctions.hash(_buckets[i]->object);
		}
	}

	return hash;
}

- (id)copy
{
	OFMapTable *copy = [[OFMapTable alloc]
	    initWithKeyFunctions: _keyFunctions
		 objectFunctions: _objectFunctions
			capacity: _capacity];

	@try {
		for (unsigned long i = 0; i < _capacity; i++)
			if (_buckets[i] != NULL && _buckets[i] != &deleted)
				[copy of_setObject: _buckets[i]->object
					    forKey: _buckets[i]->key
					      hash: OF_ROR(_buckets[i]->hash,
							_rotate)];
	} @catch (id e) {
		[copy release];
		@throw e;
	}

	return copy;
}

- (size_t)count
{
	return _count;
}

- (void *)objectForKey: (void *)key
{
	unsigned long i, hash, last;

	if (key == NULL)
		@throw [OFInvalidArgumentException exception];

	hash = OF_ROL(_keyFunctions.hash(key), _rotate);
	last = _capacity;

	for (i = hash & (_capacity - 1); i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deleted)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key))
			return _buckets[i]->object;
	}

	if (i < last)
		return nil;

	/* In case the last bucket is already used */
	last = hash & (_capacity - 1);

	for (i = 0; i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deleted)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key))
			return _buckets[i]->object;
	}

	return NULL;
}

- (void)of_resizeForCount: (unsigned long)count OF_DIRECT
{
	unsigned long fullness, capacity;
	struct of_map_table_bucket **buckets;

	if (count > ULONG_MAX / sizeof(*_buckets) || count > ULONG_MAX / 8)
		@throw [OFOutOfRangeException exception];

	fullness = count * 8 / _capacity;

	if (fullness >= 6) {
		if (_capacity > ULONG_MAX / 2)
			return;

		capacity = _capacity * 2;
	} else if (fullness <= 1)
		capacity = _capacity / 2;
	else
		return;

	/*
	 * Don't downsize if we have an initial capacity or if we would fall
	 * below the minimum capacity.
	 */
	if ((capacity < _capacity && count > _count) || capacity < MIN_CAPACITY)
		return;

	buckets = [self allocZeroedMemoryWithSize: sizeof(*buckets)
					    count: capacity];

	for (unsigned long i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL && _buckets[i] != &deleted) {
			unsigned long j, last;

			last = capacity;

			for (j = _buckets[i]->hash & (capacity - 1);
			    j < last && buckets[j] != NULL; j++);

			/* In case the last bucket is already used */
			if (j >= last) {
				last = _buckets[i]->hash & (capacity - 1);

				for (j = 0; j < last &&
				    buckets[j] != NULL; j++);
			}

			if (j >= last)
				@throw [OFOutOfRangeException exception];

			buckets[j] = _buckets[i];
		}
	}

	[self freeMemory: _buckets];
	_buckets = buckets;
	_capacity = capacity;
}

- (void)of_setObject: (void *)object
	      forKey: (void *)key
		hash: (unsigned long)hash
{
	unsigned long i, last;
	void *old;

	if (key == NULL || object == NULL)
		@throw [OFInvalidArgumentException exception];

	hash = OF_ROL(hash, _rotate);
	last = _capacity;

	for (i = hash & (_capacity - 1); i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deleted)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key))
			break;
	}

	/* In case the last bucket is already used */
	if (i >= last) {
		last = hash & (_capacity - 1);

		for (i = 0; i < last && _buckets[i] != NULL; i++) {
			if (_buckets[i] == &deleted)
				continue;

			if (_keyFunctions.equal(_buckets[i]->key, key))
				break;
		}
	}

	/* Key not in map table */
	if (i >= last || _buckets[i] == NULL || _buckets[i] == &deleted ||
	    !_keyFunctions.equal(_buckets[i]->key, key)) {
		struct of_map_table_bucket *bucket;

		[self of_resizeForCount: _count + 1];

		_mutations++;
		last = _capacity;

		for (i = hash & (_capacity - 1); i < last &&
		    _buckets[i] != NULL && _buckets[i] != &deleted; i++);

		/* In case the last bucket is already used */
		if (i >= last) {
			last = hash & (_capacity - 1);

			for (i = 0; i < last && _buckets[i] != NULL &&
			    _buckets[i] != &deleted; i++);
		}

		if (i >= last)
			@throw [OFOutOfRangeException exception];

		bucket = [self allocMemoryWithSize: sizeof(*bucket)];

		@try {
			bucket->key = _keyFunctions.retain(key);
		} @catch (id e) {
			[self freeMemory: bucket];
			@throw e;
		}

		@try {
			bucket->object = _objectFunctions.retain(object);
		} @catch (id e) {
			_keyFunctions.release(bucket->key);
			[self freeMemory: bucket];
			@throw e;
		}

		bucket->hash = hash;

		_buckets[i] = bucket;
		_count++;

		return;
	}

	old = _buckets[i]->object;
	_buckets[i]->object = _objectFunctions.retain(object);
	_objectFunctions.release(old);
}

- (void)setObject: (void *)object
	   forKey: (void *)key
{
	[self of_setObject: object
		    forKey: key
		      hash: _keyFunctions.hash(key)];
}

- (void)removeObjectForKey: (void *)key
{
	unsigned long i, hash, last;

	if (key == NULL)
		@throw [OFInvalidArgumentException exception];

	hash = OF_ROL(_keyFunctions.hash(key), _rotate);
	last = _capacity;

	for (i = hash & (_capacity - 1); i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deleted)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key)) {
			_mutations++;

			_keyFunctions.release(_buckets[i]->key);
			_objectFunctions.release(_buckets[i]->object);

			[self freeMemory: _buckets[i]];
			_buckets[i] = &deleted;

			_count--;
			[self of_resizeForCount: _count];

			return;
		}
	}

	if (i < last)
		return;

	/* In case the last bucket is already used */
	last = hash & (_capacity - 1);

	for (i = 0; i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deleted)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key)) {
			_keyFunctions.release(_buckets[i]->key);
			_objectFunctions.release(_buckets[i]->object);

			[self freeMemory: _buckets[i]];
			_buckets[i] = &deleted;

			_count--;
			_mutations++;
			[self of_resizeForCount: _count];

			return;
		}
	}
}

- (void)removeAllObjects
{
	for (unsigned long i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL) {
			if (_buckets[i] == &deleted) {
				_buckets[i] = NULL;
				continue;
			}

			_keyFunctions.release(_buckets[i]->key);
			_objectFunctions.release(_buckets[i]->object);

			[self freeMemory: _buckets[i]];
			_buckets[i] = NULL;
		}
	}

	_count = 0;
	_capacity = MIN_CAPACITY;
	_buckets = [self resizeMemory: _buckets
				 size: sizeof(*_buckets)
				count: _capacity];

	/*
	 * Get a new random value for _rotate, so that it is not less secure
	 * than creating a new hash map.
	 */
	if (of_hash_seed != 0)
		_rotate = of_random16() & 31;
}

- (bool)containsObject: (void *)object
{
	if (object == NULL || _count == 0)
		return false;

	for (unsigned long i = 0; i < _capacity; i++)
		if (_buckets[i] != NULL && _buckets[i] != &deleted)
			if (_objectFunctions.equal(_buckets[i]->object, object))
				return true;

	return false;
}

- (bool)containsObjectIdenticalTo: (void *)object
{
	if (object == NULL || _count == 0)
		return false;

	for (unsigned long i = 0; i < _capacity; i++)
		if (_buckets[i] != NULL && _buckets[i] != &deleted)
			if (_buckets[i]->object == object)
				return true;

	return false;
}

- (OFMapTableEnumerator *)keyEnumerator







|
|



|






|


|
|

|
<

|
|










|
|


|
>
|
>
>
>



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


















|
|

















|
|















|
|
|
|
|
|















|




|



|













|









<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<

<
<
|




|




|



|








|
|


|












|






|
|



|








|

|







|





|
<
|
<





|
|







|
|











|
|







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
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
		SET_DEFAULT(_objectFunctions.retain, defaultRetain);
		SET_DEFAULT(_objectFunctions.release, defaultRelease);
		SET_DEFAULT(_objectFunctions.hash, defaultHash);
		SET_DEFAULT(_objectFunctions.equal, defaultEqual);

#undef SET_DEFAULT

		if (capacity > UINT32_MAX / sizeof(*_buckets) ||
		    capacity > UINT32_MAX / 8)
			@throw [OFOutOfRangeException exception];

		for (_capacity = 1; _capacity < capacity;) {
			if (_capacity > UINT32_MAX / 2)
				@throw [OFOutOfRangeException exception];

			_capacity *= 2;
		}

		if (capacity * 8 / _capacity >= 6)
			if (_capacity <= UINT32_MAX / 2)
				_capacity *= 2;

		if (_capacity < minCapacity)
			_capacity = minCapacity;

		_buckets = OFAllocZeroedMemory(_capacity, sizeof(*_buckets));


		if (OFHashSeed != 0)
			_rotate = OFRandom16() & 31;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	for (uint32_t i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL && _buckets[i] != &deletedBucket) {
			_keyFunctions.release(_buckets[i]->key);
			_objectFunctions.release(_buckets[i]->object);

			OFFreeMemory(_buckets[i]);
		}
	}

	OFFreeMemory(_buckets);

	[super dealloc];
}

static void
resizeForCount(OFMapTable *self, uint32_t count)
{
	uint32_t fullness, capacity;
	struct OFMapTableBucket **buckets;

	if (count > UINT32_MAX / sizeof(*self->_buckets) ||
	    count > UINT32_MAX / 8)
		@throw [OFOutOfRangeException exception];

	fullness = count * 8 / self->_capacity;

	if (fullness >= 6) {
		if (self->_capacity > UINT32_MAX / 2)
			return;

		capacity = self->_capacity * 2;
	} else if (fullness <= 1)
		capacity = self->_capacity / 2;
	else
		return;

	/*
	 * Don't downsize if we have an initial capacity or if we would fall
	 * below the minimum capacity.
	 */
	if ((capacity < self->_capacity && count > self->_count) ||
	    capacity < minCapacity)
		return;

	buckets = OFAllocZeroedMemory(capacity, sizeof(*buckets));

	for (uint32_t i = 0; i < self->_capacity; i++) {
		if (self->_buckets[i] != NULL &&
		    self->_buckets[i] != &deletedBucket) {
			uint32_t j, last;

			last = capacity;

			for (j = self->_buckets[i]->hash & (capacity - 1);
			    j < last && buckets[j] != NULL; j++);

			/* In case the last bucket is already used */
			if (j >= last) {
				last = self->_buckets[i]->hash & (capacity - 1);

				for (j = 0; j < last &&
				    buckets[j] != NULL; j++);
			}

			if (j >= last)
				@throw [OFOutOfRangeException exception];

			buckets[j] = self->_buckets[i];
		}
	}

	OFFreeMemory(self->_buckets);
	self->_buckets = buckets;
	self->_capacity = capacity;
}

static void
setObject(OFMapTable *restrict self, void *key, void *object, uint32_t hash)
{
	uint32_t i, last;
	void *old;

	if (key == NULL || object == NULL)
		@throw [OFInvalidArgumentException exception];

	hash = OFRotateLeft(hash, self->_rotate);
	last = self->_capacity;

	for (i = hash & (self->_capacity - 1);
	    i < last && self->_buckets[i] != NULL; i++) {
		if (self->_buckets[i] == &deletedBucket)
			continue;

		if (self->_keyFunctions.equal(self->_buckets[i]->key, key))
			break;
	}

	/* In case the last bucket is already used */
	if (i >= last) {
		last = hash & (self->_capacity - 1);

		for (i = 0; i < last && self->_buckets[i] != NULL; i++) {
			if (self->_buckets[i] == &deletedBucket)
				continue;

			if (self->_keyFunctions.equal(
			    self->_buckets[i]->key, key))
				break;
		}
	}

	/* Key not in map table */
	if (i >= last || self->_buckets[i] == NULL ||
	    self->_buckets[i] == &deletedBucket ||
	    !self->_keyFunctions.equal(self->_buckets[i]->key, key)) {
		struct OFMapTableBucket *bucket;

		resizeForCount(self, self->_count + 1);

		self->_mutations++;
		last = self->_capacity;

		for (i = hash & (self->_capacity - 1); i < last &&
		    self->_buckets[i] != NULL &&
		    self->_buckets[i] != &deletedBucket; i++);

		/* In case the last bucket is already used */
		if (i >= last) {
			last = hash & (self->_capacity - 1);

			for (i = 0; i < last && self->_buckets[i] != NULL &&
			    self->_buckets[i] != &deletedBucket; i++);
		}

		if (i >= last)
			@throw [OFOutOfRangeException exception];

		bucket = OFAllocMemory(1, sizeof(*bucket));

		@try {
			bucket->key = self->_keyFunctions.retain(key);
		} @catch (id e) {
			OFFreeMemory(bucket);
			@throw e;
		}

		@try {
			bucket->object = self->_objectFunctions.retain(object);
		} @catch (id e) {
			self->_keyFunctions.release(bucket->key);
			OFFreeMemory(bucket);
			@throw e;
		}

		bucket->hash = hash;

		self->_buckets[i] = bucket;
		self->_count++;

		return;
	}

	old = self->_buckets[i]->object;
	self->_buckets[i]->object = self->_objectFunctions.retain(object);
	self->_objectFunctions.release(old);
}

- (bool)isEqual: (id)object
{
	OFMapTable *mapTable;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFMapTable class]])
		return false;

	mapTable = object;

	if (mapTable->_count != _count ||
	    mapTable->_keyFunctions.equal != _keyFunctions.equal ||
	    mapTable->_objectFunctions.equal != _objectFunctions.equal)
		return false;

	for (uint32_t i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL && _buckets[i] != &deletedBucket) {
			void *objectIter =
			    [mapTable objectForKey: _buckets[i]->key];

			if (!_objectFunctions.equal(objectIter,
			    _buckets[i]->object))
				return false;
		}
	}

	return true;
}

- (unsigned long)hash
{
	unsigned long hash = 0;

	for (unsigned long i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL && _buckets[i] != &deletedBucket) {
			hash ^= OFRotateRight(_buckets[i]->hash, _rotate);
			hash ^= _objectFunctions.hash(_buckets[i]->object);
		}
	}

	return hash;
}

- (id)copy
{
	OFMapTable *copy = [[OFMapTable alloc]
	    initWithKeyFunctions: _keyFunctions
		 objectFunctions: _objectFunctions
			capacity: _capacity];

	@try {
		for (uint32_t i = 0; i < _capacity; i++)
			if (_buckets[i] != NULL &&
			    _buckets[i] != &deletedBucket)
				setObject(copy, _buckets[i]->key,
				    _buckets[i]->object,
				    OFRotateRight(_buckets[i]->hash, _rotate));
	} @catch (id e) {
		[copy release];
		@throw e;
	}

	return copy;
}

- (size_t)count
{
	return _count;
}

- (void *)objectForKey: (void *)key
{
	uint32_t i, hash, last;

	if (key == NULL)
		@throw [OFInvalidArgumentException exception];

	hash = OFRotateLeft((uint32_t)_keyFunctions.hash(key), _rotate);
	last = _capacity;

	for (i = hash & (_capacity - 1); i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deletedBucket)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key))
			return _buckets[i]->object;
	}

	if (i < last)
		return nil;

	/* In case the last bucket is already used */
	last = hash & (_capacity - 1);

	for (i = 0; i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deletedBucket)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key))
			return _buckets[i]->object;
	}

	return NULL;
}




















































































































































- (void)setObject: (void *)object forKey: (void *)key

{


	setObject(self, key, object, (uint32_t)_keyFunctions.hash(key));
}

- (void)removeObjectForKey: (void *)key
{
	uint32_t i, hash, last;

	if (key == NULL)
		@throw [OFInvalidArgumentException exception];

	hash = OFRotateLeft((uint32_t)_keyFunctions.hash(key), _rotate);
	last = _capacity;

	for (i = hash & (_capacity - 1); i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deletedBucket)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key)) {
			_mutations++;

			_keyFunctions.release(_buckets[i]->key);
			_objectFunctions.release(_buckets[i]->object);

			OFFreeMemory(_buckets[i]);
			_buckets[i] = &deletedBucket;

			_count--;
			resizeForCount(self, _count);

			return;
		}
	}

	if (i < last)
		return;

	/* In case the last bucket is already used */
	last = hash & (_capacity - 1);

	for (i = 0; i < last && _buckets[i] != NULL; i++) {
		if (_buckets[i] == &deletedBucket)
			continue;

		if (_keyFunctions.equal(_buckets[i]->key, key)) {
			_keyFunctions.release(_buckets[i]->key);
			_objectFunctions.release(_buckets[i]->object);

			OFFreeMemory(_buckets[i]);
			_buckets[i] = &deletedBucket;

			_count--;
			_mutations++;
			resizeForCount(self, _count);

			return;
		}
	}
}

- (void)removeAllObjects
{
	for (uint32_t i = 0; i < _capacity; i++) {
		if (_buckets[i] != NULL) {
			if (_buckets[i] == &deletedBucket) {
				_buckets[i] = NULL;
				continue;
			}

			_keyFunctions.release(_buckets[i]->key);
			_objectFunctions.release(_buckets[i]->object);

			OFFreeMemory(_buckets[i]);
			_buckets[i] = NULL;
		}
	}

	_count = 0;
	_capacity = minCapacity;

	_buckets = OFResizeMemory(_buckets, _capacity, sizeof(*_buckets));


	/*
	 * Get a new random value for _rotate, so that it is not less secure
	 * than creating a new hash map.
	 */
	if (OFHashSeed != 0)
		_rotate = OFRandom16() & 31;
}

- (bool)containsObject: (void *)object
{
	if (object == NULL || _count == 0)
		return false;

	for (uint32_t i = 0; i < _capacity; i++)
		if (_buckets[i] != NULL && _buckets[i] != &deletedBucket)
			if (_objectFunctions.equal(_buckets[i]->object, object))
				return true;

	return false;
}

- (bool)containsObjectIdenticalTo: (void *)object
{
	if (object == NULL || _count == 0)
		return false;

	for (uint32_t i = 0; i < _capacity; i++)
		if (_buckets[i] != NULL && _buckets[i] != &deletedBucket)
			if (_buckets[i]->object == object)
				return true;

	return false;
}

- (OFMapTableEnumerator *)keyEnumerator
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
	return [[[OFMapTableObjectEnumerator alloc]
	    of_initWithMapTable: self
			buckets: _buckets
		       capacity: _capacity
	       mutationsPointer: &_mutations] autorelease];
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	unsigned long j = state->state;
	int i;

	for (i = 0; i < count; i++) {
		for (; j < _capacity && (_buckets[j] == NULL ||
		    _buckets[j] == &deleted); j++);

		if (j < _capacity) {
			objects[i] = _buckets[j]->key;
			j++;
		} else
			break;
	}

	state->state = j;
	state->itemsPtr = objects;
	state->mutationsPtr = &_mutations;

	return i;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateKeysAndObjectsUsingBlock:
    (of_map_table_enumeration_block_t)block
{
	bool stop = false;
	unsigned long mutations = _mutations;

	for (size_t i = 0; i < _capacity && !stop; i++) {
		if (_mutations != mutations)
			@throw [OFEnumerationMutationException
			    exceptionWithObject: self];

		if (_buckets[i] != NULL && _buckets[i] != &deleted)
			block(_buckets[i]->key, _buckets[i]->object, &stop);
	}
}

- (void)replaceObjectsUsingBlock: (of_map_table_replace_block_t)block
{
	unsigned long mutations = _mutations;

	for (size_t i = 0; i < _capacity; i++) {
		if (_mutations != mutations)
			@throw [OFEnumerationMutationException
			    exceptionWithObject: self];

		if (_buckets[i] != NULL && _buckets[i] != &deleted) {
			void *new;

			new = block(_buckets[i]->key, _buckets[i]->object);
			if (new == NULL)
				@throw [OFInvalidArgumentException exception];

			if (new != _buckets[i]->object) {







|








|
















|
<









|




|








|







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
	return [[[OFMapTableObjectEnumerator alloc]
	    of_initWithMapTable: self
			buckets: _buckets
		       capacity: _capacity
	       mutationsPointer: &_mutations] autorelease];
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	unsigned long j = state->state;
	int i;

	for (i = 0; i < count; i++) {
		for (; j < _capacity && (_buckets[j] == NULL ||
		    _buckets[j] == &deletedBucket); j++);

		if (j < _capacity) {
			objects[i] = _buckets[j]->key;
			j++;
		} else
			break;
	}

	state->state = j;
	state->itemsPtr = objects;
	state->mutationsPtr = &_mutations;

	return i;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateKeysAndObjectsUsingBlock: (OFMapTableEnumerationBlock)block

{
	bool stop = false;
	unsigned long mutations = _mutations;

	for (size_t i = 0; i < _capacity && !stop; i++) {
		if (_mutations != mutations)
			@throw [OFEnumerationMutationException
			    exceptionWithObject: self];

		if (_buckets[i] != NULL && _buckets[i] != &deletedBucket)
			block(_buckets[i]->key, _buckets[i]->object, &stop);
	}
}

- (void)replaceObjectsUsingBlock: (OFMapTableReplaceBlock)block
{
	unsigned long mutations = _mutations;

	for (size_t i = 0; i < _capacity; i++) {
		if (_mutations != mutations)
			@throw [OFEnumerationMutationException
			    exceptionWithObject: self];

		if (_buckets[i] != NULL && _buckets[i] != &deletedBucket) {
			void *new;

			new = block(_buckets[i]->key, _buckets[i]->object);
			if (new == NULL)
				@throw [OFInvalidArgumentException exception];

			if (new != _buckets[i]->object) {
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
@implementation OFMapTableEnumerator
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithMapTable: (OFMapTable *)mapTable
			    buckets: (struct of_map_table_bucket **)buckets
			   capacity: (unsigned long)capacity
		   mutationsPointer: (unsigned long *)mutationsPtr
{
	self = [super init];

	_mapTable = [mapTable retain];
	_buckets = buckets;
	_capacity = capacity;







|
|







647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
@implementation OFMapTableEnumerator
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithMapTable: (OFMapTable *)mapTable
			    buckets: (struct OFMapTableBucket **)buckets
			   capacity: (uint32_t)capacity
		   mutationsPointer: (unsigned long *)mutationsPtr
{
	self = [super init];

	_mapTable = [mapTable retain];
	_buckets = buckets;
	_capacity = capacity;
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
- (void **)nextObject
{
	if (*_mutationsPtr != _mutations)
		@throw [OFEnumerationMutationException
		    exceptionWithObject: _mapTable];

	for (; _position < _capacity && (_buckets[_position] == NULL ||
	    _buckets[_position] == &deleted); _position++);

	if (_position < _capacity)
		return &_buckets[_position++]->key;
	else
		return NULL;
}
@end

@implementation OFMapTableObjectEnumerator
- (void **)nextObject
{
	if (*_mutationsPtr != _mutations)
		@throw [OFEnumerationMutationException
		    exceptionWithObject: _mapTable];

	for (; _position < _capacity && (_buckets[_position] == NULL ||
	    _buckets[_position] == &deleted); _position++);

	if (_position < _capacity)
		return &_buckets[_position++]->object;
	else
		return NULL;
}
@end







|
















|







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
- (void **)nextObject
{
	if (*_mutationsPtr != _mutations)
		@throw [OFEnumerationMutationException
		    exceptionWithObject: _mapTable];

	for (; _position < _capacity && (_buckets[_position] == NULL ||
	    _buckets[_position] == &deletedBucket); _position++);

	if (_position < _capacity)
		return &_buckets[_position++]->key;
	else
		return NULL;
}
@end

@implementation OFMapTableObjectEnumerator
- (void **)nextObject
{
	if (*_mutationsPtr != _mutations)
		@throw [OFEnumerationMutationException
		    exceptionWithObject: _mapTable];

	for (; _position < _capacity && (_buckets[_position] == NULL ||
	    _buckets[_position] == &deletedBucket); _position++);

	if (_position < _capacity)
		return &_buckets[_position++]->object;
	else
		return NULL;
}
@end

Modified src/OFMapTableDictionary.h from [bf16e6f63c] to [23bbcf2cb0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMapTableDictionary.m from [d2c4c5ea8c] to [3e7005d616].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

static bool
equal(void *object1, void *object2)
{
	return [(id)object1 isEqual: (id)object2];
}

static const of_map_table_functions_t keyFunctions = {
	.retain = copy,
	.release = release,
	.hash = hash,
	.equal = equal
};
static const of_map_table_functions_t objectFunctions = {
	.retain = retain,
	.release = release,
	.hash = hash,
	.equal = equal
};

@implementation OFMapTableDictionary







|





|







55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

static bool
equal(void *object1, void *object2)
{
	return [(id)object1 isEqual: (id)object2];
}

static const OFMapTableFunctions keyFunctions = {
	.retain = copy,
	.release = release,
	.hash = hash,
	.equal = equal
};
static const OFMapTableFunctions objectFunctions = {
	.retain = retain,
	.release = release,
	.hash = hash,
	.equal = equal
};

@implementation OFMapTableDictionary
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
		OFEnumerator *keyEnumerator, *objectEnumerator;
		id key, object;

		keyEnumerator = [dictionary keyEnumerator];
		objectEnumerator = [dictionary objectEnumerator];
		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil)
			[_mapTable setObject: object
				      forKey: key];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)object
			forKey: (id)key
{
	self = [self initWithCapacity: 1];

	@try {
		[_mapTable setObject: object
			      forKey: key];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObjects: (id const *)objects
			forKeys: (id const *)keys
			  count: (size_t)count
{
	self = [self initWithCapacity: count];

	@try {
		size_t i;

		for (i = 0; i < count; i++)
			[_mapTable setObject: objects[i]
				      forKey: keys[i]];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithKey: (id)firstKey
		  arguments: (va_list)arguments
{
	self = [super init];

	@try {
		va_list argumentsCopy;
		id key, object;
		size_t i, count;







|
<










|
<




|
<


















|
<








|
<







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
		OFEnumerator *keyEnumerator, *objectEnumerator;
		id key, object;

		keyEnumerator = [dictionary keyEnumerator];
		objectEnumerator = [dictionary objectEnumerator];
		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil)
			[_mapTable setObject: object forKey: key];


		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)object forKey: (id)key

{
	self = [self initWithCapacity: 1];

	@try {
		[_mapTable setObject: object forKey: key];

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

	return self;
}

- (instancetype)initWithObjects: (id const *)objects
			forKeys: (id const *)keys
			  count: (size_t)count
{
	self = [self initWithCapacity: count];

	@try {
		size_t i;

		for (i = 0; i < count; i++)
			[_mapTable setObject: objects[i] forKey: keys[i]];

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

	return self;
}

- (instancetype)initWithKey: (id)firstKey arguments: (va_list)arguments

{
	self = [super init];

	@try {
		va_list argumentsCopy;
		id key, object;
		size_t i, count;
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
		count /= 2;

		_mapTable = [[OFMapTable alloc]
		    initWithKeyFunctions: keyFunctions
			 objectFunctions: objectFunctions
				capacity: count];

		[_mapTable setObject: object
			      forKey: key];

		for (i = 1; i < count; i++) {
			key = va_arg(arguments, id);
			object = va_arg(arguments, id);

			if (key == nil || object == nil)
				@throw [OFInvalidArgumentException exception];

			[_mapTable setObject: object
				      forKey: key];
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFArray *keys, *objects;
		OFEnumerator *keyEnumerator, *objectEnumerator;
		OFXMLElement *keyElement, *objectElement;

		keys = [element elementsForName: @"key"
				      namespace: OF_SERIALIZATION_NS];
		objects = [element elementsForName: @"object"
					 namespace: OF_SERIALIZATION_NS];

		if (keys.count != objects.count)
			@throw [OFInvalidFormatException exception];

		_mapTable = [[OFMapTable alloc]
		    initWithKeyFunctions: keyFunctions
			 objectFunctions: objectFunctions
				capacity: keys.count];

		keyEnumerator = [keys objectEnumerator];
		objectEnumerator = [objects objectEnumerator];
		while ((keyElement = [keyEnumerator nextObject]) != nil &&
		    (objectElement = [objectEnumerator nextObject]) != nil) {
			void *pool2 = objc_autoreleasePoolPush();
			OFXMLElement *key, *object;

			key = [keyElement elementsForNamespace:
			    OF_SERIALIZATION_NS].firstObject;
			object = [objectElement elementsForNamespace:
			    OF_SERIALIZATION_NS].firstObject;

			if (key == nil || object == nil)
				@throw [OFInvalidFormatException exception];

			[_mapTable setObject: object.objectByDeserializing
				      forKey: key.objectByDeserializing];








|
<








|
<




















|

|

















|

|







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
		count /= 2;

		_mapTable = [[OFMapTable alloc]
		    initWithKeyFunctions: keyFunctions
			 objectFunctions: objectFunctions
				capacity: count];

		[_mapTable setObject: object forKey: key];


		for (i = 1; i < count; i++) {
			key = va_arg(arguments, id);
			object = va_arg(arguments, id);

			if (key == nil || object == nil)
				@throw [OFInvalidArgumentException exception];

			[_mapTable setObject: object forKey: key];

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

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFArray *keys, *objects;
		OFEnumerator *keyEnumerator, *objectEnumerator;
		OFXMLElement *keyElement, *objectElement;

		keys = [element elementsForName: @"key"
				      namespace: OFSerializationNS];
		objects = [element elementsForName: @"object"
					 namespace: OFSerializationNS];

		if (keys.count != objects.count)
			@throw [OFInvalidFormatException exception];

		_mapTable = [[OFMapTable alloc]
		    initWithKeyFunctions: keyFunctions
			 objectFunctions: objectFunctions
				capacity: keys.count];

		keyEnumerator = [keys objectEnumerator];
		objectEnumerator = [objects objectEnumerator];
		while ((keyElement = [keyEnumerator nextObject]) != nil &&
		    (objectElement = [objectEnumerator nextObject]) != nil) {
			void *pool2 = objc_autoreleasePoolPush();
			OFXMLElement *key, *object;

			key = [keyElement elementsForNamespace:
			    OFSerializationNS].firstObject;
			object = [objectElement elementsForNamespace:
			    OFSerializationNS].firstObject;

			if (key == nil || object == nil)
				@throw [OFInvalidFormatException exception];

			[_mapTable setObject: object.objectByDeserializing
				      forKey: key.objectByDeserializing];

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
- (OFArray *)allKeys
{
	OFArray *ret;
	id *keys;
	size_t count;

	count = _mapTable.count;
	keys = [self allocMemoryWithSize: sizeof(*keys)
				   count: count];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFMapTableEnumerator *enumerator;
		void **keyPtr;
		size_t i;

		i = 0;
		enumerator = [_mapTable keyEnumerator];
		while ((keyPtr = [enumerator nextObject]) != NULL) {
			assert(i < count);

			keys[i++] = (id)*keyPtr;
		}

		objc_autoreleasePoolPop(pool);

		ret = [OFArray arrayWithObjects: keys
					  count: count];
	} @finally {
		[self freeMemory: keys];
	}

	return ret;
}

- (OFArray *)allObjects
{
	OFArray *ret;
	id *objects;
	size_t count;

	count = _mapTable.count;
	objects = [self allocMemoryWithSize: sizeof(*objects)
				      count: count];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFMapTableEnumerator *enumerator;
		void **objectPtr;
		size_t i;

		i = 0;
		enumerator = [_mapTable objectEnumerator];
		while ((objectPtr = [enumerator nextObject]) != NULL) {
			assert(i < count);

			objects[i++] = (id)*objectPtr;
		}

		objc_autoreleasePoolPop(pool);

		ret = [OFArray arrayWithObjects: objects
					  count: count];
	} @finally {
		[self freeMemory: objects];
	}

	return ret;
}

- (OFEnumerator *)keyEnumerator
{
	return [[[OFMapTableEnumeratorWrapper alloc]
	    initWithEnumerator: [_mapTable keyEnumerator]
			object: self] autorelease];
}

- (OFEnumerator *)objectEnumerator
{
	return [[[OFMapTableEnumeratorWrapper alloc]
	    initWithEnumerator: [_mapTable objectEnumerator]
			object: self] autorelease];
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	return [_mapTable countByEnumeratingWithState: state
					      objects: objects
						count: count];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateKeysAndObjectsUsingBlock:
    (of_dictionary_enumeration_block_t)block
{
	@try {
		[_mapTable enumerateKeysAndObjectsUsingBlock:
		    ^ (void *key, void *object, bool *stop) {
			block(key, object, stop);
		}];
	} @catch (OFEnumerationMutationException *e) {







|
<

















|
<

|












|
<

















|
<

|



















|









|
<







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
- (OFArray *)allKeys
{
	OFArray *ret;
	id *keys;
	size_t count;

	count = _mapTable.count;
	keys = OFAllocMemory(count, sizeof(*keys));


	@try {
		void *pool = objc_autoreleasePoolPush();
		OFMapTableEnumerator *enumerator;
		void **keyPtr;
		size_t i;

		i = 0;
		enumerator = [_mapTable keyEnumerator];
		while ((keyPtr = [enumerator nextObject]) != NULL) {
			assert(i < count);

			keys[i++] = (id)*keyPtr;
		}

		objc_autoreleasePoolPop(pool);

		ret = [OFArray arrayWithObjects: keys count: count];

	} @finally {
		OFFreeMemory(keys);
	}

	return ret;
}

- (OFArray *)allObjects
{
	OFArray *ret;
	id *objects;
	size_t count;

	count = _mapTable.count;
	objects = OFAllocMemory(count, sizeof(*objects));


	@try {
		void *pool = objc_autoreleasePoolPush();
		OFMapTableEnumerator *enumerator;
		void **objectPtr;
		size_t i;

		i = 0;
		enumerator = [_mapTable objectEnumerator];
		while ((objectPtr = [enumerator nextObject]) != NULL) {
			assert(i < count);

			objects[i++] = (id)*objectPtr;
		}

		objc_autoreleasePoolPop(pool);

		ret = [OFArray arrayWithObjects: objects count: count];

	} @finally {
		OFFreeMemory(objects);
	}

	return ret;
}

- (OFEnumerator *)keyEnumerator
{
	return [[[OFMapTableEnumeratorWrapper alloc]
	    initWithEnumerator: [_mapTable keyEnumerator]
			object: self] autorelease];
}

- (OFEnumerator *)objectEnumerator
{
	return [[[OFMapTableEnumeratorWrapper alloc]
	    initWithEnumerator: [_mapTable objectEnumerator]
			object: self] autorelease];
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	return [_mapTable countByEnumeratingWithState: state
					      objects: objects
						count: count];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateKeysAndObjectsUsingBlock: (OFDictionaryEnumerationBlock)block

{
	@try {
		[_mapTable enumerateKeysAndObjectsUsingBlock:
		    ^ (void *key, void *object, bool *stop) {
			block(key, object, stop);
		}];
	} @catch (OFEnumerationMutationException *e) {

Modified src/OFMapTableSet.h from [3804b5f67f] to [1664d9d036].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMapTableSet.m from [6da1f132a2] to [87bc750df2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

static bool
equal(void *object1, void *object2)
{
	return [(id)object1 isEqual: (id)object2];
}

static const of_map_table_functions_t keyFunctions = {
	.retain = retain,
	.release = release,
	.hash = hash,
	.equal = equal
};
static const of_map_table_functions_t objectFunctions = { NULL };

@implementation OFMapTableSet
- (instancetype)init
{
	return [self initWithCapacity: 0];
}








|





|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

static bool
equal(void *object1, void *object2)
{
	return [(id)object1 isEqual: (id)object2];
}

static const OFMapTableFunctions keyFunctions = {
	.retain = retain,
	.release = release,
	.hash = hash,
	.equal = equal
};
static const OFMapTableFunctions objectFunctions = { NULL };

@implementation OFMapTableSet
- (instancetype)init
{
	return [self initWithCapacity: 0];
}

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
		@throw e;
	}

	self = [self initWithCapacity: count];

	@try {
		for (id object in set)
			[_mapTable setObject: (void *)1
				      forKey: object];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







|
<







96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
		@throw e;
	}

	self = [self initWithCapacity: count];

	@try {
		for (id object in set)
			[_mapTable setObject: (void *)1 forKey: object];

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

	return self;
}
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
		@throw e;
	}

	self = [self initWithCapacity: count];

	@try {
		for (id object in array)
			[_mapTable setObject: (void *)1
				      forKey: object];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	self = [self initWithCapacity: count];

	@try {
		for (size_t i = 0; i < count; i++)
			[_mapTable setObject: (void *)1
				      forKey: objects[i]];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	self = [super init];

	@try {
		id object;
		va_list argumentsCopy;
		size_t count;

		va_copy(argumentsCopy, arguments);

		for (count = 1; va_arg(argumentsCopy, id) != nil; count++);

		_mapTable = [[OFMapTable alloc]
		    initWithKeyFunctions: keyFunctions
			 objectFunctions: objectFunctions
				capacity: count];

		[_mapTable setObject: (void *)1
			      forKey: firstObject];

		while ((object = va_arg(arguments, id)) != nil)
			[_mapTable setObject: (void *)1
				      forKey: object];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if ((![element.name isEqual: @"OFSet"] &&
		    ![element.name isEqual: @"OFMutableSet"]) ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		for (OFXMLElement *child in
		    [element elementsForNamespace: OF_SERIALIZATION_NS]) {
			void *pool2  = objc_autoreleasePoolPush();

			[_mapTable setObject: (void *)1
				      forKey: [child objectByDeserializing]];

			objc_autoreleasePoolPop(pool2);
		}







|
<








|
<





|
<








|
<

















|
<


|
<

















|



|







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
		@throw e;
	}

	self = [self initWithCapacity: count];

	@try {
		for (id object in array)
			[_mapTable setObject: (void *)1 forKey: object];

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

	return self;
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	self = [self initWithCapacity: count];

	@try {
		for (size_t i = 0; i < count; i++)
			[_mapTable setObject: (void *)1 forKey: objects[i]];

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

	return self;
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	self = [super init];

	@try {
		id object;
		va_list argumentsCopy;
		size_t count;

		va_copy(argumentsCopy, arguments);

		for (count = 1; va_arg(argumentsCopy, id) != nil; count++);

		_mapTable = [[OFMapTable alloc]
		    initWithKeyFunctions: keyFunctions
			 objectFunctions: objectFunctions
				capacity: count];

		[_mapTable setObject: (void *)1 forKey: firstObject];


		while ((object = va_arg(arguments, id)) != nil)
			[_mapTable setObject: (void *)1 forKey: object];

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

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [self init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if ((![element.name isEqual: @"OFSet"] &&
		    ![element.name isEqual: @"OFMutableSet"]) ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		for (OFXMLElement *child in
		    [element elementsForNamespace: OFSerializationNS]) {
			void *pool2  = objc_autoreleasePoolPush();

			[_mapTable setObject: (void *)1
				      forKey: [child objectByDeserializing]];

			objc_autoreleasePoolPop(pool2);
		}
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
- (OFEnumerator *)objectEnumerator
{
	return [[[OFMapTableEnumeratorWrapper alloc]
	    initWithEnumerator: [_mapTable keyEnumerator]
			object: self] autorelease];
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	return [_mapTable countByEnumeratingWithState: state
					      objects: objects
						count: count];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (of_set_enumeration_block_t)block
{
	@try {
		[_mapTable enumerateKeysAndObjectsUsingBlock:
		    ^ (void *key, void *object, bool *stop) {
			block(key, stop);
		}];
	} @catch (OFEnumerationMutationException *e) {
		@throw [OFEnumerationMutationException
		    exceptionWithObject: self];
	}
}
#endif
@end







|









|













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
- (OFEnumerator *)objectEnumerator
{
	return [[[OFMapTableEnumeratorWrapper alloc]
	    initWithEnumerator: [_mapTable keyEnumerator]
			object: self] autorelease];
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	return [_mapTable countByEnumeratingWithState: state
					      objects: objects
						count: count];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (OFSetEnumerationBlock)block
{
	@try {
		[_mapTable enumerateKeysAndObjectsUsingBlock:
		    ^ (void *key, void *object, bool *stop) {
			block(key, stop);
		}];
	} @catch (OFEnumerationMutationException *e) {
		@throw [OFEnumerationMutationException
		    exceptionWithObject: self];
	}
}
#endif
@end

Modified src/OFMessagePackExtension.h from [d7cac48dd4] to [45f0ba641f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 * @brief Creates a new OFMessagePackRepresentation with the specified type and
 *	  data.
 *
 * @param type The MessagePack extension type
 * @param data The data for the extension
 * @return A new, autoreleased OFMessagePackRepresentation
 */
+ (instancetype)extensionWithType: (int8_t)type
			     data: (OFData *)data;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFMessagePackRepresentation with the
 *	  specified type and data.
 *







|
<







48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
 * @brief Creates a new OFMessagePackRepresentation with the specified type and
 *	  data.
 *
 * @param type The MessagePack extension type
 * @param data The data for the extension
 * @return A new, autoreleased OFMessagePackRepresentation
 */
+ (instancetype)extensionWithType: (int8_t)type data: (OFData *)data;


- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFMessagePackRepresentation with the
 *	  specified type and data.
 *

Modified src/OFMessagePackExtension.m from [0041614de1] to [7009717fd9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFString.h"

#import "OFInvalidArgumentException.h"

@implementation OFMessagePackExtension
@synthesize type = _type, data = _data;

+ (instancetype)extensionWithType: (int8_t)type
			     data: (OFData *)data
{
	return [[[self alloc] initWithType: type
				      data: data] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithType: (int8_t)type
			data: (OFData *)data
{
	self = [super init];

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








|
<

|
<







|
<







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
#import "OFString.h"

#import "OFInvalidArgumentException.h"

@implementation OFMessagePackExtension
@synthesize type = _type, data = _data;

+ (instancetype)extensionWithType: (int8_t)type data: (OFData *)data

{
	return [[[self alloc] initWithType: type data: data] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithType: (int8_t)type data: (OFData *)data

{
	self = [super init];

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

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
		uint16_t length;

		ret = [OFMutableData dataWithCapacity: count + 4];

		prefix = 0xC8;
		[ret addItem: &prefix];

		length = OF_BSWAP16_IF_LE((uint16_t)count);
		[ret addItems: &length
			count: 2];

		[ret addItem: &_type];
	} else {
		uint32_t length;

		ret = [OFMutableData dataWithCapacity: count + 6];

		prefix = 0xC9;
		[ret addItem: &prefix];

		length = OF_BSWAP32_IF_LE((uint32_t)count);
		[ret addItems: &length
			count: 4];

		[ret addItem: &_type];
	}

	[ret addItems: _data.items
		count: _data.count];

	[ret makeImmutable];

	return ret;
}

- (OFString *)description
{







|
|
<










|
|
<




|
<
<







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
		uint16_t length;

		ret = [OFMutableData dataWithCapacity: count + 4];

		prefix = 0xC8;
		[ret addItem: &prefix];

		length = OFToBigEndian16((uint16_t)count);
		[ret addItems: &length count: 2];


		[ret addItem: &_type];
	} else {
		uint32_t length;

		ret = [OFMutableData dataWithCapacity: count + 6];

		prefix = 0xC9;
		[ret addItem: &prefix];

		length = OFToBigEndian32((uint32_t)count);
		[ret addItems: &length count: 4];


		[ret addItem: &_type];
	}

	[ret addItems: _data.items count: _data.count];


	[ret makeImmutable];

	return ret;
}

- (OFString *)description
{
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 false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD(hash, (uint8_t)_type);
	OF_HASH_ADD_HASH(hash, _data.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
	return [self retain];
}
@end







|

|

|
|

|









166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
		return false;

	return true;
}

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

Modified src/OFMessagePackRepresentation.h from [6eeb25f988] to [d2e100cc65].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMethodSignature.h from [b2c0b2e4d6] to [6c7eab90df].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#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 of_sizeof_type_encoding(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 of_alignof_type_encoding(const char *type);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|







|





90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#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

Modified src/OFMethodSignature.m from [cf206c5116] to [e6d0a405ec].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

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

#import "macros.h"

static size_t alignofEncoding(const char **type, size_t *length, bool inStruct);

static size_t sizeofEncoding(const char **type, size_t *length);

static size_t
alignofArray(const char **type, size_t *length)
{
	size_t align;

	assert(*length > 0);

	(*type)++;
	(*length)--;

	while (*length > 0 && of_ascii_isdigit(**type)) {
		(*type)++;
		(*length)--;
	}

	align = alignofEncoding(type, length, true);

	if (*length == 0 || **type != ']')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	return align;
}

static size_t
alignofStruct(const char **type, size_t *length)
{
	size_t align = 0;
#if defined(OF_POWERPC) && defined(OF_MACOS)
	bool first = true;
#endif

	assert(*length > 0);

	(*type)++;







|
>
|


|

|






|




|







|



|

|







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

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

#import "macros.h"

static size_t alignmentOfEncoding(const char **type, size_t *length,
    bool inStruct);
static size_t sizeOfEncoding(const char **type, size_t *length);

static size_t
alignmentOfArray(const char **type, size_t *length)
{
	size_t alignment;

	assert(*length > 0);

	(*type)++;
	(*length)--;

	while (*length > 0 && OFASCIIIsDigit(**type)) {
		(*type)++;
		(*length)--;
	}

	alignment = alignmentOfEncoding(type, length, true);

	if (*length == 0 || **type != ']')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	return alignment;
}

static size_t
alignmentOfStruct(const char **type, size_t *length)
{
	size_t alignment = 0;
#if defined(OF_POWERPC) && defined(OF_MACOS)
	bool first = true;
#endif

	assert(*length > 0);

	(*type)++;
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
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
		@throw [OFInvalidFormatException exception];

	/* Skip '=' */
	(*type)++;
	(*length)--;

	while (*length > 0 && **type != '}') {
		size_t fieldAlign = alignofEncoding(type, length, true);

#if defined(OF_POWERPC) && defined(OF_MACOS)
		if (!first && fieldAlign > 4)
			fieldAlign = 4;

		first = false;
#endif

		if (fieldAlign > align)
			align = fieldAlign;
	}

	if (*length == 0 || **type != '}')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	return align;
}

static size_t
alignofUnion(const char **type, size_t *length)
{
	size_t align = 0;

	assert(*length > 0);

	(*type)++;
	(*length)--;

	/* Skip name */
	while (*length > 0 && **type != '=') {
		(*type)++;
		(*length)--;
	}

	if (*length == 0)
		@throw [OFInvalidFormatException exception];

	/* Skip '=' */
	(*type)++;
	(*length)--;

	while (*length > 0 && **type != ')') {
		size_t fieldAlign = alignofEncoding(type, length, true);

		if (fieldAlign > align)
			align = fieldAlign;
	}

	if (*length == 0 || **type != ')')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	return align;
}

static size_t
alignofEncoding(const char **type, size_t *length, bool inStruct)
{
	size_t align;

	if (*length == 0)
		@throw [OFInvalidFormatException exception];

	if (**type == 'r') {
		(*type)++;
		(*length)--;

		if (*length == 0)
			@throw [OFInvalidFormatException exception];
	}

	switch (**type) {
	case 'c':
	case 'C':
		align = OF_ALIGNOF(char);
		break;
	case 'i':
	case 'I':
		align = OF_ALIGNOF(int);
		break;
	case 's':
	case 'S':
		align = OF_ALIGNOF(short);
		break;
	case 'l':
	case 'L':
		align = OF_ALIGNOF(long);
		break;
	case 'q':
	case 'Q':
#if defined(OF_X86) && !defined(OF_WINDOWS)
		if (inStruct)
			align = 4;
		else
#endif
			align = OF_ALIGNOF(long long);
		break;
#ifdef __SIZEOF_INT128__
	case 't':
	case 'T':
		align = __extension__ OF_ALIGNOF(__int128);
		break;
#endif
	case 'f':
		align = OF_ALIGNOF(float);
		break;
	case 'd':
#if defined(OF_X86) && !defined(OF_WINDOWS)
		if (inStruct)
			align = 4;
		else
#endif
			align = OF_ALIGNOF(double);
		break;
	case 'D':
#if defined(OF_X86) && !defined(OF_WINDOWS)
		if (inStruct)
			align = 4;
		else
#endif
			align = OF_ALIGNOF(long double);
		break;
	case 'B':
		align = OF_ALIGNOF(_Bool);
		break;
	case 'v':
		align = 0;
		break;
	case '*':
		align = OF_ALIGNOF(char *);
		break;
	case '@':
		align = OF_ALIGNOF(id);
		break;
	case '#':
		align = OF_ALIGNOF(Class);
		break;
	case ':':
		align = OF_ALIGNOF(SEL);
		break;
	case '[':
		return alignofArray(type, length);
	case '{':
		return alignofStruct(type, length);
	case '(':
		return alignofUnion(type, length);
	case '^':
		/* Just to skip over the rest */
		(*type)++;
		(*length)--;
		alignofEncoding(type, length, false);

		return OF_ALIGNOF(void *);
#ifndef __STDC_NO_COMPLEX__
	case 'j':
		(*type)++;
		(*length)--;

		if (*length == 0)
			@throw [OFInvalidFormatException exception];

		switch (**type) {
		case 'f':
			align = OF_ALIGNOF(float _Complex);
			break;
		case 'd':
# if defined(OF_X86) && !defined(OF_WINDOWS)
			if (inStruct)
				align = 4;
			else
# endif
				align = OF_ALIGNOF(double _Complex);
			break;
		case 'D':
			align = OF_ALIGNOF(long double _Complex);
			break;
		default:
			@throw [OFInvalidFormatException exception];
		}

		break;
#endif
	default:
		@throw [OFInvalidFormatException exception];
	}

	(*type)++;
	(*length)--;

	return align;
}

static size_t
sizeofArray(const char **type, size_t *length)
{
	size_t count = 0;
	size_t size;

	assert(*length > 0);

	(*type)++;
	(*length)--;

	while (*length > 0 && of_ascii_isdigit(**type)) {
		count = count * 10 + **type - '0';

		(*type)++;
		(*length)--;
	}

	if (count == 0)
		@throw [OFInvalidFormatException exception];

	size = sizeofEncoding(type, length);

	if (*length == 0 || **type != ']')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	if (SIZE_MAX / count < size)
		@throw [OFOutOfRangeException exception];

	return count * size;
}

static size_t
sizeofStruct(const char **type, size_t *length)
{
	size_t size = 0;
	const char *typeCopy = *type;
	size_t lengthCopy = *length;
	size_t alignment = alignofStruct(&typeCopy, &lengthCopy);
#if defined(OF_POWERPC) && defined(OF_MACOS)
	bool first = true;
#endif

	assert(*length > 0);

	(*type)++;







|


|
|




|
|








|



|

|




















|

|
|








|



|

|















|



|



|



|





|


|




|



|




|


|




|


|


|


|


|


|


|


|


|

|

|




|












|




|


|


|














|



|









|









|














|




|







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
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
		@throw [OFInvalidFormatException exception];

	/* Skip '=' */
	(*type)++;
	(*length)--;

	while (*length > 0 && **type != '}') {
		size_t fieldAlignment = alignmentOfEncoding(type, length, true);

#if defined(OF_POWERPC) && defined(OF_MACOS)
		if (!first && fieldAlignment > 4)
			fieldAlignment = 4;

		first = false;
#endif

		if (fieldAlignment > alignment)
			alignment = fieldAlignment;
	}

	if (*length == 0 || **type != '}')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	return alignment;
}

static size_t
alignmentOfUnion(const char **type, size_t *length)
{
	size_t alignment = 0;

	assert(*length > 0);

	(*type)++;
	(*length)--;

	/* Skip name */
	while (*length > 0 && **type != '=') {
		(*type)++;
		(*length)--;
	}

	if (*length == 0)
		@throw [OFInvalidFormatException exception];

	/* Skip '=' */
	(*type)++;
	(*length)--;

	while (*length > 0 && **type != ')') {
		size_t fieldAlignment = alignmentOfEncoding(type, length, true);

		if (fieldAlignment > alignment)
			alignment = fieldAlignment;
	}

	if (*length == 0 || **type != ')')
		@throw [OFInvalidFormatException exception];

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

	if (**type == 'r') {
		(*type)++;
		(*length)--;

		if (*length == 0)
			@throw [OFInvalidFormatException exception];
	}

	switch (**type) {
	case 'c':
	case 'C':
		alignment = OF_ALIGNOF(char);
		break;
	case 'i':
	case 'I':
		alignment = OF_ALIGNOF(int);
		break;
	case 's':
	case 'S':
		alignment = OF_ALIGNOF(short);
		break;
	case 'l':
	case 'L':
		alignment = OF_ALIGNOF(long);
		break;
	case 'q':
	case 'Q':
#if defined(OF_X86) && !defined(OF_WINDOWS)
		if (inStruct)
			alignment = 4;
		else
#endif
			alignment = OF_ALIGNOF(long long);
		break;
#ifdef __SIZEOF_INT128__
	case 't':
	case 'T':
		alignment = __extension__ OF_ALIGNOF(__int128);
		break;
#endif
	case 'f':
		alignment = OF_ALIGNOF(float);
		break;
	case 'd':
#if defined(OF_X86) && !defined(OF_WINDOWS)
		if (inStruct)
			alignment = 4;
		else
#endif
			alignment = OF_ALIGNOF(double);
		break;
	case 'D':
#if defined(OF_X86) && !defined(OF_WINDOWS)
		if (inStruct)
			alignment = 4;
		else
#endif
			alignment = OF_ALIGNOF(long double);
		break;
	case 'B':
		alignment = OF_ALIGNOF(_Bool);
		break;
	case 'v':
		alignment = 0;
		break;
	case '*':
		alignment = OF_ALIGNOF(char *);
		break;
	case '@':
		alignment = OF_ALIGNOF(id);
		break;
	case '#':
		alignment = OF_ALIGNOF(Class);
		break;
	case ':':
		alignment = OF_ALIGNOF(SEL);
		break;
	case '[':
		return alignmentOfArray(type, length);
	case '{':
		return alignmentOfStruct(type, length);
	case '(':
		return alignmentOfUnion(type, length);
	case '^':
		/* Just to skip over the rest */
		(*type)++;
		(*length)--;
		alignmentOfEncoding(type, length, false);

		return OF_ALIGNOF(void *);
#ifndef __STDC_NO_COMPLEX__
	case 'j':
		(*type)++;
		(*length)--;

		if (*length == 0)
			@throw [OFInvalidFormatException exception];

		switch (**type) {
		case 'f':
			alignment = OF_ALIGNOF(float _Complex);
			break;
		case 'd':
# if defined(OF_X86) && !defined(OF_WINDOWS)
			if (inStruct)
				alignment = 4;
			else
# endif
				alignment = OF_ALIGNOF(double _Complex);
			break;
		case 'D':
			alignment = OF_ALIGNOF(long double _Complex);
			break;
		default:
			@throw [OFInvalidFormatException exception];
		}

		break;
#endif
	default:
		@throw [OFInvalidFormatException exception];
	}

	(*type)++;
	(*length)--;

	return alignment;
}

static size_t
sizeOfArray(const char **type, size_t *length)
{
	size_t count = 0;
	size_t size;

	assert(*length > 0);

	(*type)++;
	(*length)--;

	while (*length > 0 && OFASCIIIsDigit(**type)) {
		count = count * 10 + **type - '0';

		(*type)++;
		(*length)--;
	}

	if (count == 0)
		@throw [OFInvalidFormatException exception];

	size = sizeOfEncoding(type, length);

	if (*length == 0 || **type != ']')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	if (SIZE_MAX / count < size)
		@throw [OFOutOfRangeException exception];

	return count * size;
}

static size_t
sizeOfStruct(const char **type, size_t *length)
{
	size_t size = 0;
	const char *typeCopy = *type;
	size_t lengthCopy = *length;
	size_t alignment = alignmentOfStruct(&typeCopy, &lengthCopy);
#if defined(OF_POWERPC) && defined(OF_MACOS)
	bool first = true;
#endif

	assert(*length > 0);

	(*type)++;
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
		@throw [OFInvalidFormatException exception];

	/* Skip '=' */
	(*type)++;
	(*length)--;

	while (*length > 0 && **type != '}') {
		size_t fieldSize, fieldAlign;

		typeCopy = *type;
		lengthCopy = *length;
		fieldSize = sizeofEncoding(type, length);
		fieldAlign = alignofEncoding(&typeCopy, &lengthCopy, true);


#if defined(OF_POWERPC) && defined(OF_MACOS)
		if (!first && fieldAlign > 4)
			fieldAlign = 4;

		first = false;
#endif

		if (size % fieldAlign != 0) {
			size_t padding = fieldAlign - (size % fieldAlign);


			if (SIZE_MAX - size < padding)
				@throw [OFOutOfRangeException exception];

			size += padding;
		}








|



|
|
>


|
|




|
|
>







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
		@throw [OFInvalidFormatException exception];

	/* Skip '=' */
	(*type)++;
	(*length)--;

	while (*length > 0 && **type != '}') {
		size_t fieldSize, fieldAlignment;

		typeCopy = *type;
		lengthCopy = *length;
		fieldSize = sizeOfEncoding(type, length);
		fieldAlignment = alignmentOfEncoding(&typeCopy, &lengthCopy,
		    true);

#if defined(OF_POWERPC) && defined(OF_MACOS)
		if (!first && fieldAlignment > 4)
			fieldAlignment = 4;

		first = false;
#endif

		if (size % fieldAlignment != 0) {
			size_t padding =
			    fieldAlignment - (size % fieldAlignment);

			if (SIZE_MAX - size < padding)
				@throw [OFOutOfRangeException exception];

			size += padding;
		}

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
		size += padding;
	}

	return size;
}

static size_t
sizeofUnion(const char **type, size_t *length)
{
	size_t size = 0;

	assert(*length > 0);

	(*type)++;
	(*length)--;







|







393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
		size += padding;
	}

	return size;
}

static size_t
sizeOfUnion(const char **type, size_t *length)
{
	size_t size = 0;

	assert(*length > 0);

	(*type)++;
	(*length)--;
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
		@throw [OFInvalidFormatException exception];

	/* Skip '=' */
	(*type)++;
	(*length)--;

	while (*length > 0 && **type != ')') {
		size_t fieldSize = sizeofEncoding(type, length);

		if (fieldSize > size)
			size = fieldSize;
	}

	if (*length == 0 || **type != ')')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	return size;
}

static size_t
sizeofEncoding(const char **type, size_t *length)
{
	size_t size;

	if (*length == 0)
		@throw [OFInvalidFormatException exception];

	if (**type == 'r') {







|















|







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
		@throw [OFInvalidFormatException exception];

	/* Skip '=' */
	(*type)++;
	(*length)--;

	while (*length > 0 && **type != ')') {
		size_t fieldSize = sizeOfEncoding(type, length);

		if (fieldSize > size)
			size = fieldSize;
	}

	if (*length == 0 || **type != ')')
		@throw [OFInvalidFormatException exception];

	(*type)++;
	(*length)--;

	return size;
}

static size_t
sizeOfEncoding(const char **type, size_t *length)
{
	size_t size;

	if (*length == 0)
		@throw [OFInvalidFormatException exception];

	if (**type == 'r') {
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
	case '#':
		size = sizeof(Class);
		break;
	case ':':
		size = sizeof(SEL);
		break;
	case '[':
		return sizeofArray(type, length);
	case '{':
		return sizeofStruct(type, length);
	case '(':
		return sizeofUnion(type, length);
	case '^':
		/* Just to skip over the rest */
		(*type)++;
		(*length)--;
		sizeofEncoding(type, length);

		return sizeof(void *);
#ifndef __STDC_NO_COMPLEX__
	case 'j':
		(*type)++;
		(*length)--;








|

|

|




|







502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
	case '#':
		size = sizeof(Class);
		break;
	case ':':
		size = sizeof(SEL);
		break;
	case '[':
		return sizeOfArray(type, length);
	case '{':
		return sizeOfStruct(type, length);
	case '(':
		return sizeOfUnion(type, length);
	case '^':
		/* Just to skip over the rest */
		(*type)++;
		(*length)--;
		sizeOfEncoding(type, length);

		return sizeof(void *);
#ifndef __STDC_NO_COMPLEX__
	case 'j':
		(*type)++;
		(*length)--;

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
	(*type)++;
	(*length)--;

	return size;
}

size_t
of_sizeof_type_encoding(const char *type)
{
	size_t length = strlen(type);
	size_t ret = sizeofEncoding(&type, &length);

	if (length > 0)
		@throw [OFInvalidFormatException exception];

	return ret;
}

size_t
of_alignof_type_encoding(const char *type)
{
	size_t length = strlen(type);
	size_t ret = alignofEncoding(&type, &length, false);

	if (length > 0)
		@throw [OFInvalidFormatException exception];

	return ret;
}








|


|








|


|







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
	(*type)++;
	(*length)--;

	return size;
}

size_t
OFSizeOfTypeEncoding(const char *type)
{
	size_t length = strlen(type);
	size_t ret = sizeOfEncoding(&type, &length);

	if (length > 0)
		@throw [OFInvalidFormatException exception];

	return ret;
}

size_t
OFAlignmentOfTypeEncoding(const char *type)
{
	size_t length = strlen(type);
	size_t ret = alignmentOfEncoding(&type, &length, false);

	if (length > 0)
		@throw [OFInvalidFormatException exception];

	return ret;
}

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
			@throw [OFInvalidArgumentException exception];

		length = strlen(types);

		if (length == 0)
			@throw [OFInvalidFormatException exception];

		_types = [self allocMemoryWithSize: length + 1];
		memcpy(_types, types, length);

		_typesPointers = [[OFMutableData alloc]
		    initWithItemSize: sizeof(char *)];
		_offsets = [[OFMutableData alloc]
		    initWithItemSize: sizeof(size_t)];

		last = _types;
		for (size_t i = 0; i < length; i++) {
			if (of_ascii_isdigit(_types[i])) {
				size_t offset = _types[i] - '0';

				if (last == _types + i)
					@throw [OFInvalidFormatException
					    exception];

				_types[i] = '\0';
				[_typesPointers addItem: &last];

				i++;
				for (; i < length &&
				    of_ascii_isdigit(_types[i]); i++)
					offset = offset * 10 + _types[i] - '0';

				[_offsets addItem: &offset];

				last = _types + i;
				i--;
			} else if (_types[i] == '{') {







|









|











|







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
			@throw [OFInvalidArgumentException exception];

		length = strlen(types);

		if (length == 0)
			@throw [OFInvalidFormatException exception];

		_types = OFAllocMemory(length + 1, 1);
		memcpy(_types, types, length);

		_typesPointers = [[OFMutableData alloc]
		    initWithItemSize: sizeof(char *)];
		_offsets = [[OFMutableData alloc]
		    initWithItemSize: sizeof(size_t)];

		last = _types;
		for (size_t i = 0; i < length; i++) {
			if (OFASCIIIsDigit(_types[i])) {
				size_t offset = _types[i] - '0';

				if (last == _types + i)
					@throw [OFInvalidFormatException
					    exception];

				_types[i] = '\0';
				[_typesPointers addItem: &last];

				i++;
				for (; i < length &&
				    OFASCIIIsDigit(_types[i]); i++)
					offset = offset * 10 + _types[i] - '0';

				[_offsets addItem: &offset];

				last = _types + i;
				i--;
			} else if (_types[i] == '{') {
667
668
669
670
671
672
673

674
675
676
677
678
679
680
	}

	return self;
}

- (void)dealloc
{

	[_typesPointers release];
	[_offsets release];

	[super dealloc];
}

- (size_t)numberOfArguments







>







668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
	}

	return self;
}

- (void)dealloc
{
	OFFreeMemory(_types);
	[_typesPointers release];
	[_offsets release];

	[super dealloc];
}

- (size_t)numberOfArguments

Modified src/OFMutableAdjacentArray.h from [9d0ea61f45] to [def2b8877c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutableAdjacentArray.m from [1999b771ee] to [7ff7f8969c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

	[_array addItem: &object];
	[object retain];

	_mutations++;
}

- (void)insertObject: (id)object
	     atIndex: (size_t)idx
{
	if (object == nil)
		@throw [OFInvalidArgumentException exception];

	@try {
		[_array insertItem: &object
			   atIndex: idx];
	} @catch (OFOutOfRangeException *e) {
		@throw [OFOutOfRangeException exception];
	}
	[object retain];

	_mutations++;
}

- (void)insertObjectsFromArray: (OFArray *)array
		       atIndex: (size_t)idx
{
	id const *objects = array.objects;
	size_t count = array.count;

	@try {
		[_array insertItems: objects
			    atIndex: idx
			      count: count];
	} @catch (OFOutOfRangeException *e) {
		@throw [OFOutOfRangeException exception];
	}

	for (size_t i = 0; i < count; i++)
		[objects[i] retain];

	_mutations++;
}

- (void)replaceObject: (id)oldObject
	   withObject: (id)newObject
{
	id *objects;
	size_t count;

	if (oldObject == nil || newObject == nil)
		@throw [OFInvalidArgumentException exception];

	objects = _array.mutableItems;
	count = _array.count;

	for (size_t i = 0; i < count; i++) {
		if ([objects[i] isEqual: oldObject]) {
			[newObject retain];
			[objects[i] release];
			objects[i] = newObject;

			return;
		}
	}
}

- (void)replaceObjectAtIndex: (size_t)idx
		  withObject: (id)object
{
	id *objects;
	id oldObject;

	if (object == nil)
		@throw [OFInvalidArgumentException exception];

	objects = _array.mutableItems;

	if (idx >= _array.count)
		@throw [OFOutOfRangeException exception];

	oldObject = objects[idx];
	objects[idx] = [object retain];
	[oldObject release];
}

- (void)replaceObjectIdenticalTo: (id)oldObject
		      withObject: (id)newObject
{
	id *objects;
	size_t count;

	if (oldObject == nil || newObject == nil)
		@throw [OFInvalidArgumentException exception];








|
<





|
<








|
<





|
<
<










|
<















<
<




|
<

















|
<







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

	[_array addItem: &object];
	[object retain];

	_mutations++;
}

- (void)insertObject: (id)object atIndex: (size_t)idx

{
	if (object == nil)
		@throw [OFInvalidArgumentException exception];

	@try {
		[_array insertItem: &object atIndex: idx];

	} @catch (OFOutOfRangeException *e) {
		@throw [OFOutOfRangeException exception];
	}
	[object retain];

	_mutations++;
}

- (void)insertObjectsFromArray: (OFArray *)array atIndex: (size_t)idx

{
	id const *objects = array.objects;
	size_t count = array.count;

	@try {
		[_array insertItems: objects atIndex: idx count: count];


	} @catch (OFOutOfRangeException *e) {
		@throw [OFOutOfRangeException exception];
	}

	for (size_t i = 0; i < count; i++)
		[objects[i] retain];

	_mutations++;
}

- (void)replaceObject: (id)oldObject withObject: (id)newObject

{
	id *objects;
	size_t count;

	if (oldObject == nil || newObject == nil)
		@throw [OFInvalidArgumentException exception];

	objects = _array.mutableItems;
	count = _array.count;

	for (size_t i = 0; i < count; i++) {
		if ([objects[i] isEqual: oldObject]) {
			[newObject retain];
			[objects[i] release];
			objects[i] = newObject;


		}
	}
}

- (void)replaceObjectAtIndex: (size_t)idx withObject: (id)object

{
	id *objects;
	id oldObject;

	if (object == nil)
		@throw [OFInvalidArgumentException exception];

	objects = _array.mutableItems;

	if (idx >= _array.count)
		@throw [OFOutOfRangeException exception];

	oldObject = objects[idx];
	objects[idx] = [object retain];
	[oldObject release];
}

- (void)replaceObjectIdenticalTo: (id)oldObject withObject: (id)newObject

{
	id *objects;
	size_t count;

	if (oldObject == nil || newObject == nil)
		@throw [OFInvalidArgumentException exception];

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


186

187
188
189
190
191
192
193
		@throw [OFInvalidArgumentException exception];

	objects = _array.items;
	count = _array.count;

	for (size_t i = 0; i < count; i++) {
		if ([objects[i] isEqual: object]) {
			object = objects[i];

			[_array removeItemAtIndex: i];
			_mutations++;

			[object release];



			return;

		}
	}
}

- (void)removeObjectIdenticalTo: (id)object
{
	id const *objects;







|




|

>
>
|
>







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
		@throw [OFInvalidArgumentException exception];

	objects = _array.items;
	count = _array.count;

	for (size_t i = 0; i < count; i++) {
		if ([objects[i] isEqual: object]) {
			id tmp = objects[i];

			[_array removeItemAtIndex: i];
			_mutations++;

			[tmp release];

			objects = _array.items;
			i--;
			count--;
			continue;
		}
	}
}

- (void)removeObjectIdenticalTo: (id)object
{
	id const *objects;
202
203
204
205
206
207
208


209

210
211
212
213
214
215
216
	for (size_t i = 0; i < count; i++) {
		if (objects[i] == object) {
			[_array removeItemAtIndex: i];
			_mutations++;

			[object release];



			return;

		}
	}
}

- (void)removeObjectAtIndex: (size_t)idx
{
#ifndef __clang_analyzer__







>
>
|
>







193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
	for (size_t i = 0; i < count; i++) {
		if (objects[i] == object) {
			[_array removeItemAtIndex: i];
			_mutations++;

			[object release];

			objects = _array.items;
			i--;
			count--;
			continue;
		}
	}
}

- (void)removeObjectAtIndex: (size_t)idx
{
#ifndef __clang_analyzer__
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

	for (size_t i = 0; i < count; i++)
		[objects[i] release];

	[_array removeAllItems];
}

- (void)removeObjectsInRange: (of_range_t)range
{
	id const *objects = _array.items;
	size_t count = _array.count;
	id *copy;

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

	copy = [self allocMemoryWithSize: sizeof(*copy)
				   count: range.length];
	memcpy(copy, objects + range.location, range.length * sizeof(id));

	@try {
		[_array removeItemsInRange: range];
		_mutations++;

		for (size_t i = 0; i < range.length; i++)
			[copy[i] release];
	} @finally {
		[self freeMemory: copy];
	}
}

- (void)removeLastObject
{
#ifndef __clang_analyzer__
	size_t count = _array.count;
	id object;

	if (count == 0)
		return;

	object = [self objectAtIndex: count - 1];
	[_array removeLastItem];
	[object release];

	_mutations++;
#endif
}

- (void)exchangeObjectAtIndex: (size_t)idx1
	    withObjectAtIndex: (size_t)idx2
{
	id *objects = _array.mutableItems;
	size_t count = _array.count;
	id tmp;

	if (idx1 >= count || idx2 >= count)
		@throw [OFOutOfRangeException exception];







|









<
|









|




















|
<







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

	for (size_t i = 0; i < count; i++)
		[objects[i] release];

	[_array removeAllItems];
}

- (void)removeObjectsInRange: (OFRange)range
{
	id const *objects = _array.items;
	size_t count = _array.count;
	id *copy;

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


	copy = OFAllocMemory(range.length, sizeof(*copy));
	memcpy(copy, objects + range.location, range.length * sizeof(id));

	@try {
		[_array removeItemsInRange: range];
		_mutations++;

		for (size_t i = 0; i < range.length; i++)
			[copy[i] release];
	} @finally {
		OFFreeMemory(copy);
	}
}

- (void)removeLastObject
{
#ifndef __clang_analyzer__
	size_t count = _array.count;
	id object;

	if (count == 0)
		return;

	object = [self objectAtIndex: count - 1];
	[_array removeLastItem];
	[object release];

	_mutations++;
#endif
}

- (void)exchangeObjectAtIndex: (size_t)idx1 withObjectAtIndex: (size_t)idx2

{
	id *objects = _array.mutableItems;
	size_t count = _array.count;
	id tmp;

	if (idx1 >= count || idx2 >= count)
		@throw [OFOutOfRangeException exception];
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
	for (i = 0, j = count - 1; i < j; i++, j--) {
		id tmp = objects[i];
		objects[i] = objects[j];
		objects[j] = tmp;
	}
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count_
{
	size_t count = _array.count;

	if (count > INT_MAX) {
		/*







|







293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
	for (i = 0, j = count - 1; i < j; i++, j--) {
		id tmp = objects[i];
		objects[i] = objects[j];
		objects[j] = tmp;
	}
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count_
{
	size_t count = _array.count;

	if (count > INT_MAX) {
		/*
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
{
	return [[[OFArrayEnumerator alloc]
	    initWithArray: self
	     mutationsPtr: &_mutations] autorelease];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (of_array_enumeration_block_t)block
{
	id const *objects = _array.items;
	size_t count = _array.count;
	bool stop = false;
	unsigned long mutations = _mutations;

	for (size_t i = 0; i < count && !stop; i++) {
		if (_mutations != mutations)
			@throw [OFEnumerationMutationException
			    exceptionWithObject: self];

		block(objects[i], i, &stop);
	}
}

- (void)replaceObjectsUsingBlock: (of_array_replace_block_t)block
{
	id *objects = _array.mutableItems;
	size_t count = _array.count;
	unsigned long mutations = _mutations;

	for (size_t i = 0; i < count; i++) {
		id new;







|















|







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
{
	return [[[OFArrayEnumerator alloc]
	    initWithArray: self
	     mutationsPtr: &_mutations] autorelease];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (OFArrayEnumerationBlock)block
{
	id const *objects = _array.items;
	size_t count = _array.count;
	bool stop = false;
	unsigned long mutations = _mutations;

	for (size_t i = 0; i < count && !stop; i++) {
		if (_mutations != mutations)
			@throw [OFEnumerationMutationException
			    exceptionWithObject: self];

		block(objects[i], i, &stop);
	}
}

- (void)replaceObjectsUsingBlock: (OFArrayReplaceBlock)block
{
	id *objects = _array.mutableItems;
	size_t count = _array.count;
	unsigned long mutations = _mutations;

	for (size_t i = 0; i < count; i++) {
		id new;

Modified src/OFMutableArray.h from [77912a7def] to [43a8133fa8].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
 * @brief A block for replacing values in an OFMutableArray.
 *
 * @param object The object to replace
 * @param index The index of the object to replace
 * @return The object to replace the object with
 */
typedef id _Nonnull (^of_array_replace_block_t)(id object, size_t index);
#endif

/**
 * @class OFMutableArray OFArray.h ObjFW/OFArray.h
 *
 * @brief An abstract class for storing, adding and removing objects in an
 *	  array.







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
 * @brief A block for replacing values in an OFMutableArray.
 *
 * @param object The object to replace
 * @param index The index of the object to replace
 * @return The object to replace the object with
 */
typedef id _Nonnull (^OFArrayReplaceBlock)(id object, size_t index);
#endif

/**
 * @class OFMutableArray OFArray.h ObjFW/OFArray.h
 *
 * @brief An abstract class for storing, adding and removing objects in an
 *	  array.
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

/**
 * @brief Inserts an object to the OFArray at the specified index.
 *
 * @param object An object to add
 * @param index The index where the object should be inserted
 */
- (void)insertObject: (ObjectType)object
	     atIndex: (size_t)index;

/**
 * @brief Inserts the objects from the specified OFArray at the specified index.
 *
 * @param array An array of objects
 * @param index The index where the objects should be inserted
 */
- (void)insertObjectsFromArray: (OFArray OF_GENERIC(ObjectType) *)array
		       atIndex: (size_t)index;

/**
 * @brief Replaces the first object equivalent to the specified object with the
 *	  other specified object.
 *
 * @param oldObject The object to replace
 * @param newObject The replacement object
 */
- (void)replaceObject: (ObjectType)oldObject
	   withObject: (ObjectType)newObject;

/**
 * @brief Replaces the object at the specified index with the specified object.
 *
 * @param index The index of the object to replace
 * @param object The replacement object
 */
- (void)replaceObjectAtIndex: (size_t)index
		  withObject: (ObjectType)object;

/**
 * @brief Replaces the object at the specified index with the specified object.
 *
 * This method is the same as @ref replaceObjectAtIndex:withObject:.
 *
 * This method is also used by the subscripting syntax.
 *
 * @param index The index of the object to replace
 * @param object The replacement object
 */
-    (void)setObject: (ObjectType)object
  atIndexedSubscript: (size_t)index;

/**
 * @brief Replaces the first object that has the same address as the specified
 *	  object with the other specified object.
 *
 * @param oldObject The object to replace
 * @param newObject The replacement object
 */
- (void)replaceObjectIdenticalTo: (ObjectType)oldObject
		      withObject: (ObjectType)newObject;

/**
 * @brief Removes the first object equivalent to the specified object.
 *
 * @param object The object to remove
 */
- (void)removeObject: (ObjectType)object;

/**
 * @brief Removes the first object that has the same address as the specified
 *	  object.
 *
 * @param object The object to remove
 */
- (void)removeObjectIdenticalTo: (ObjectType)object;

/**
 * @brief Removes the object at the specified index.
 *
 * @param index The index of the object to remove
 */
- (void)removeObjectAtIndex: (size_t)index;

/**
 * @brief Removes the object in the specified range.
 *
 * @param range The range of the objects to remove
 */
- (void)removeObjectsInRange: (of_range_t)range;

/**
 * @brief Removes the last object.
 */
- (void)removeLastObject;

/**
 * @brief Removes all objects.
 */
- (void)removeAllObjects;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Replaces each object with the object returned by the block.
 *
 * @param block The block which returns a new object for each object
 */
- (void)replaceObjectsUsingBlock: (of_array_replace_block_t)block;
#endif

/**
 * @brief Exchange the objects at the specified indices.
 *
 * @param index1 The index of the first object to exchange
 * @param index2 The index of the second object to exchange
 */
- (void)exchangeObjectAtIndex: (size_t)index1
	    withObjectAtIndex: (size_t)index2;

/**
 * @brief Sorts the array in ascending order.
 */
- (void)sort;

/**
 * @brief Sorts the array using the specified selector and options.
 *
 * @param selector The selector to use to sort the array. It's signature
 *		   should be the same as that of -[compare:].
 * @param options The options to use when sorting the array.@n
 *		  Possible values are:
 *		  Value                      | Description
 *		  ---------------------------|-------------------------
 *		  `OF_ARRAY_SORT_DESCENDING` | Sort in descending order
 */
- (void)sortUsingSelector: (SEL)selector
		  options: (int)options;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Sorts the array using the specified comparator and options.
 *
 * @param comparator The comparator to use to sort the array
 * @param options The options to use when sorting the array.@n
 *		  Possible values are:
 *		  Value                      | Description
 *		  ---------------------------|-------------------------
 *		  `OF_ARRAY_SORT_DESCENDING` | Sort in descending order
 */
- (void)sortUsingComparator: (of_comparator_t)comparator
		    options: (int)options;
#endif

/**
 * @brief Reverts the order of the objects in the array.
 */
- (void)reverse;








|
<











|





|
<







|
<











|
<












|






|














|



|

















|








|
<











|
<
<
<
<

|
<






|
<
<
<
<

|
|







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

/**
 * @brief Inserts an object to the OFArray at the specified index.
 *
 * @param object An object to add
 * @param index The index where the object should be inserted
 */
- (void)insertObject: (ObjectType)object atIndex: (size_t)index;


/**
 * @brief Inserts the objects from the specified OFArray at the specified index.
 *
 * @param array An array of objects
 * @param index The index where the objects should be inserted
 */
- (void)insertObjectsFromArray: (OFArray OF_GENERIC(ObjectType) *)array
		       atIndex: (size_t)index;

/**
 * @brief Replaces all objects equivalent to the specified object with the
 *	  other specified object.
 *
 * @param oldObject The object to replace
 * @param newObject The replacement object
 */
- (void)replaceObject: (ObjectType)oldObject withObject: (ObjectType)newObject;


/**
 * @brief Replaces the object at the specified index with the specified object.
 *
 * @param index The index of the object to replace
 * @param object The replacement object
 */
- (void)replaceObjectAtIndex: (size_t)index withObject: (ObjectType)object;


/**
 * @brief Replaces the object at the specified index with the specified object.
 *
 * This method is the same as @ref replaceObjectAtIndex:withObject:.
 *
 * This method is also used by the subscripting syntax.
 *
 * @param index The index of the object to replace
 * @param object The replacement object
 */
- (void)setObject: (ObjectType)object atIndexedSubscript: (size_t)index;


/**
 * @brief Replaces the first object that has the same address as the specified
 *	  object with the other specified object.
 *
 * @param oldObject The object to replace
 * @param newObject The replacement object
 */
- (void)replaceObjectIdenticalTo: (ObjectType)oldObject
		      withObject: (ObjectType)newObject;

/**
 * @brief Removes all objects equivalent to the specified object.
 *
 * @param object The object to remove
 */
- (void)removeObject: (ObjectType)object;

/**
 * @brief Removes all objects that have the same address as the specified
 *	  object.
 *
 * @param object The object to remove
 */
- (void)removeObjectIdenticalTo: (ObjectType)object;

/**
 * @brief Removes the object at the specified index.
 *
 * @param index The index of the object to remove
 */
- (void)removeObjectAtIndex: (size_t)index;

/**
 * @brief Removes the objects in the specified range.
 *
 * @param range The range of the objects to remove
 */
- (void)removeObjectsInRange: (OFRange)range;

/**
 * @brief Removes the last object.
 */
- (void)removeLastObject;

/**
 * @brief Removes all objects.
 */
- (void)removeAllObjects;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Replaces each object with the object returned by the block.
 *
 * @param block The block which returns a new object for each object
 */
- (void)replaceObjectsUsingBlock: (OFArrayReplaceBlock)block;
#endif

/**
 * @brief Exchange the objects at the specified indices.
 *
 * @param index1 The index of the first object to exchange
 * @param index2 The index of the second object to exchange
 */
- (void)exchangeObjectAtIndex: (size_t)index1 withObjectAtIndex: (size_t)index2;


/**
 * @brief Sorts the array in ascending order.
 */
- (void)sort;

/**
 * @brief Sorts the array using the specified selector and options.
 *
 * @param selector The selector to use to sort the array. It's signature
 *		   should be the same as that of -[compare:].
 * @param options The options to use when sorting the array




 */
- (void)sortUsingSelector: (SEL)selector options: (OFArraySortOptions)options;


#ifdef OF_HAVE_BLOCKS
/**
 * @brief Sorts the array using the specified comparator and options.
 *
 * @param comparator The comparator to use to sort the array
 * @param options The options to use when sorting the array




 */
- (void)sortUsingComparator: (OFComparator)comparator
		    options: (OFArraySortOptions)options;
#endif

/**
 * @brief Reverts the order of the objects in the array.
 */
- (void)reverse;

Modified src/OFMutableArray.m from [ec4d8f9c48] to [8e711f8f4a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
static struct {
	Class isa;
} placeholder;

@interface OFMutableArrayPlaceholder: OFMutableArray
@end

static of_comparison_result_t
compare(id left, id right, SEL selector)
{
	of_comparison_result_t (*comparator)(id, SEL, id) =
	    (of_comparison_result_t (*)(id, SEL, id))
	    [left methodForSelector: selector];

	return comparator(left, selector, right);
}

static void
quicksort(OFMutableArray *array, size_t left, size_t right, SEL selector,
    int options)
{
	of_comparison_result_t ascending, descending;

	if (options & OF_ARRAY_SORT_DESCENDING) {
		ascending = OF_ORDERED_DESCENDING;
		descending = OF_ORDERED_ASCENDING;
	} else {
		ascending = OF_ORDERED_ASCENDING;
		descending = OF_ORDERED_DESCENDING;
	}

	while (left < right) {
		size_t i = left;
		size_t j = right - 1;
		id pivot = [array objectAtIndex: right];








|


|
|







|

|

|
|
|

|
|







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
static struct {
	Class isa;
} placeholder;

@interface OFMutableArrayPlaceholder: OFMutableArray
@end

static OFComparisonResult
compare(id left, id right, SEL selector)
{
	OFComparisonResult (*comparator)(id, SEL, id) =
	    (OFComparisonResult (*)(id, SEL, id))
	    [left methodForSelector: selector];

	return comparator(left, selector, right);
}

static void
quicksort(OFMutableArray *array, size_t left, size_t right, SEL selector,
    OFArraySortOptions options)
{
	OFComparisonResult ascending, descending;

	if (options & OFArraySortDescending) {
		ascending = OFOrderedDescending;
		descending = OFOrderedAscending;
	} else {
		ascending = OFOrderedAscending;
		descending = OFOrderedDescending;
	}

	while (left < right) {
		size_t i = left;
		size_t j = right - 1;
		id pivot = [array objectAtIndex: right];

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
		left = i + 1;
	}
}

#ifdef OF_HAVE_BLOCKS
static void
quicksortWithBlock(OFMutableArray *array, size_t left, size_t right,
    of_comparator_t comparator, int options)
{
	of_comparison_result_t ascending, descending;

	if (options & OF_ARRAY_SORT_DESCENDING) {
		ascending = OF_ORDERED_DESCENDING;
		descending = OF_ORDERED_ASCENDING;
	} else {
		ascending = OF_ORDERED_ASCENDING;
		descending = OF_ORDERED_DESCENDING;
	}

	while (left < right) {
		size_t i = left;
		size_t j = right - 1;
		id pivot = [array objectAtIndex: right];








|

|

|
|
|

|
|







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
		left = i + 1;
	}
}

#ifdef OF_HAVE_BLOCKS
static void
quicksortWithBlock(OFMutableArray *array, size_t left, size_t right,
    OFComparator comparator, OFArraySortOptions options)
{
	OFComparisonResult ascending, descending;

	if (options & OFArraySortDescending) {
		ascending = OFOrderedDescending;
		descending = OFOrderedAscending;
	} else {
		ascending = OFOrderedAscending;
		descending = OFOrderedDescending;
	}

	while (left < right) {
		size_t i = left;
		size_t j = right - 1;
		id pivot = [array objectAtIndex: right];

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
	ret = [[OFMutableAdjacentArray alloc] initWithObject: firstObject
						   arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	return (id)[[OFMutableAdjacentArray alloc] initWithObject: firstObject
							arguments: arguments];
}

- (instancetype)initWithArray: (OFArray *)array
{
	return (id)[[OFMutableAdjacentArray alloc] initWithArray: array];
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	return (id)[[OFMutableAdjacentArray alloc] initWithObjects: objects
							     count: count];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{







|
<










|
<







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
	ret = [[OFMutableAdjacentArray alloc] initWithObject: firstObject
						   arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	return (id)[[OFMutableAdjacentArray alloc] initWithObject: firstObject
							arguments: arguments];
}

- (instancetype)initWithArray: (OFArray *)array
{
	return (id)[[OFMutableAdjacentArray alloc] initWithArray: array];
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	return (id)[[OFMutableAdjacentArray alloc] initWithObjects: objects
							     count: count];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
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
- (id)copy
{
	return [[OFArray alloc] initWithArray: self];
}

- (void)addObject: (id)object
{
	[self insertObject: object
		   atIndex: self.count];
}

- (void)addObjectsFromArray: (OFArray *)array
{
	[self insertObjectsFromArray: array
			     atIndex: self.count];
}

- (void)insertObject: (id)object
	     atIndex: (size_t)idx
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)insertObjectsFromArray: (OFArray *)array
		       atIndex: (size_t)idx
{
	size_t i = 0;

	for (id object in array)
		[self insertObject: object
			   atIndex: idx + i++];
}

- (void)replaceObjectAtIndex: (size_t)idx
		  withObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

-    (void)setObject: (id)object
  atIndexedSubscript: (size_t)idx
{
	[self replaceObjectAtIndex: idx
			withObject: object];
}

- (void)replaceObject: (id)oldObject
	   withObject: (id)newObject
{
	size_t count;

	if (oldObject == nil || newObject == nil)
		@throw [OFInvalidArgumentException exception];

	count = self.count;

	for (size_t i = 0; i < count; i++) {
		if ([[self objectAtIndex: i] isEqual: oldObject]) {
			[self replaceObjectAtIndex: i
					withObject: newObject];
			return;
		}
	}
}

- (void)replaceObjectIdenticalTo: (id)oldObject
		      withObject: (id)newObject
{
	size_t count;

	if (oldObject == nil || newObject == nil)
		@throw [OFInvalidArgumentException exception];

	count = self.count;

	for (size_t i = 0; i < count; i++) {
		if ([self objectAtIndex: i] == oldObject) {
			[self replaceObjectAtIndex: i
					withObject: newObject];

			return;
		}
	}
}

- (void)removeObjectAtIndex: (size_t)idx







|
<




|
<


|
<




|
<




|
<


|
<




<
|

|
<


|
<








|
|
|
<
<
|
|
<
<
|
<










|
<







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
- (id)copy
{
	return [[OFArray alloc] initWithArray: self];
}

- (void)addObject: (id)object
{
	[self insertObject: object atIndex: self.count];

}

- (void)addObjectsFromArray: (OFArray *)array
{
	[self insertObjectsFromArray: array atIndex: self.count];

}

- (void)insertObject: (id)object atIndex: (size_t)idx

{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)insertObjectsFromArray: (OFArray *)array atIndex: (size_t)idx

{
	size_t i = 0;

	for (id object in array)
		[self insertObject: object atIndex: idx + i++];

}

- (void)replaceObjectAtIndex: (size_t)idx withObject: (id)object

{
	OF_UNRECOGNIZED_SELECTOR
}


- (void)setObject: (id)object atIndexedSubscript: (size_t)idx
{
	[self replaceObjectAtIndex: idx withObject: object];

}

- (void)replaceObject: (id)oldObject withObject: (id)newObject

{
	size_t count;

	if (oldObject == nil || newObject == nil)
		@throw [OFInvalidArgumentException exception];

	count = self.count;

	for (size_t i = 0; i < count; i++)
		if ([[self objectAtIndex: i] isEqual: oldObject])
			[self replaceObjectAtIndex: i withObject: newObject];


}



- (void)replaceObjectIdenticalTo: (id)oldObject withObject: (id)newObject

{
	size_t count;

	if (oldObject == nil || newObject == nil)
		@throw [OFInvalidArgumentException exception];

	count = self.count;

	for (size_t i = 0; i < count; i++) {
		if ([self objectAtIndex: i] == oldObject) {
			[self replaceObjectAtIndex: i withObject: newObject];


			return;
		}
	}
}

- (void)removeObjectAtIndex: (size_t)idx
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

	count = self.count;

	for (size_t i = 0; i < count; i++) {
		if ([[self objectAtIndex: i] isEqual: object]) {
			[self removeObjectAtIndex: i];


			return;

		}
	}
}

- (void)removeObjectIdenticalTo: (id)object
{
	size_t count;

	if (object == nil)
		@throw [OFInvalidArgumentException exception];

	count = self.count;

	for (size_t i = 0; i < count; i++) {
		if ([self objectAtIndex: i] == object) {
			[self removeObjectAtIndex: i];


			return;

		}
	}
}

- (void)removeObjectsInRange: (of_range_t)range
{
	for (size_t i = 0; i < range.length; i++)
		[self removeObjectAtIndex: range.location];
}

- (void)removeLastObject
{
	size_t count = self.count;

	if (count == 0)
		return;

	[self removeObjectAtIndex: count - 1];
}

- (void)removeAllObjects
{
	[self removeObjectsInRange: of_range(0, self.count)];
}

#ifdef OF_HAVE_BLOCKS
- (void)replaceObjectsUsingBlock: (of_array_replace_block_t)block
{
	[self enumerateObjectsUsingBlock: ^ (id object, size_t idx,
	    bool *stop) {
		id new = block(object, idx);

		if (new != object)
			[self replaceObjectAtIndex: idx
					withObject: new];
	}];
}
#endif

- (void)exchangeObjectAtIndex: (size_t)idx1
	    withObjectAtIndex: (size_t)idx2
{
	id object1 = [self objectAtIndex: idx1];
	id object2 = [self objectAtIndex: idx2];

	[object1 retain];
	@try {
		[self replaceObjectAtIndex: idx1
				withObject: object2];
		[self replaceObjectAtIndex: idx2
				withObject: object1];
	} @finally {
		[object1 release];
	}
}

- (void)sort
{
	[self sortUsingSelector: @selector(compare:)
			options: 0];
}

- (void)sortUsingSelector: (SEL)selector
		  options: (int)options
{
	size_t count = self.count;

	if (count == 0 || count == 1)
		return;

	quicksort(self, 0, count - 1, selector, options);
}

#ifdef OF_HAVE_BLOCKS
- (void)sortUsingComparator: (of_comparator_t)comparator
		    options: (int)options
{
	size_t count = self.count;

	if (count == 0 || count == 1)
		return;

	quicksortWithBlock(self, 0, count - 1, comparator, options);
}
#endif

- (void)reverse
{
	size_t i, j, count = self.count;

	if (count == 0 || count == 1)
		return;

	for (i = 0, j = count - 1; i < j; i++, j--)
		[self exchangeObjectAtIndex: i
			  withObjectAtIndex: j];
}

- (void)makeImmutable
{
}
@end







>
|
>

















>
|
>




|

















|



|






|
<




|
<






|
<
|
<







|
<



|










|
|


















|
<






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

	count = self.count;

	for (size_t i = 0; i < count; i++) {
		if ([[self objectAtIndex: i] isEqual: object]) {
			[self removeObjectAtIndex: i];

			i--;
			count--;
			continue;
		}
	}
}

- (void)removeObjectIdenticalTo: (id)object
{
	size_t count;

	if (object == nil)
		@throw [OFInvalidArgumentException exception];

	count = self.count;

	for (size_t i = 0; i < count; i++) {
		if ([self objectAtIndex: i] == object) {
			[self removeObjectAtIndex: i];

			i--;
			count--;
			continue;
		}
	}
}

- (void)removeObjectsInRange: (OFRange)range
{
	for (size_t i = 0; i < range.length; i++)
		[self removeObjectAtIndex: range.location];
}

- (void)removeLastObject
{
	size_t count = self.count;

	if (count == 0)
		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) {
		id new = block(object, idx);

		if (new != object)
			[self replaceObjectAtIndex: idx withObject: new];

	}];
}
#endif

- (void)exchangeObjectAtIndex: (size_t)idx1 withObjectAtIndex: (size_t)idx2

{
	id object1 = [self objectAtIndex: idx1];
	id object2 = [self objectAtIndex: idx2];

	[object1 retain];
	@try {
		[self replaceObjectAtIndex: idx1 withObject: object2];

		[self replaceObjectAtIndex: idx2 withObject: object1];

	} @finally {
		[object1 release];
	}
}

- (void)sort
{
	[self sortUsingSelector: @selector(compare:) options: 0];

}

- (void)sortUsingSelector: (SEL)selector
		  options: (OFArraySortOptions)options
{
	size_t count = self.count;

	if (count == 0 || count == 1)
		return;

	quicksort(self, 0, count - 1, selector, options);
}

#ifdef OF_HAVE_BLOCKS
- (void)sortUsingComparator: (OFComparator)comparator
		    options: (OFArraySortOptions)options
{
	size_t count = self.count;

	if (count == 0 || count == 1)
		return;

	quicksortWithBlock(self, 0, count - 1, comparator, options);
}
#endif

- (void)reverse
{
	size_t i, j, count = self.count;

	if (count == 0 || count == 1)
		return;

	for (i = 0, j = count - 1; i < j; i++, j--)
		[self exchangeObjectAtIndex: i withObjectAtIndex: j];

}

- (void)makeImmutable
{
}
@end

Modified src/OFMutableData.h from [1a536a9853] to [5eba71457e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
 * @brief All items of the OFMutableData as a C array.
 *
 * @warning The pointer is only valid until the OFMutableData is changed!
 *
 * Modifying the returned array directly is allowed and will change the contents
 * of the data.
 */
@property (readonly, nonatomic) void *mutableItems OF_RETURNS_INNER_POINTER;


/**
 * @brief The first item of the OFMutableData or `NULL`.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *mutableFirstItem
    OF_RETURNS_INNER_POINTER;








|
>







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 * @brief All items of the OFMutableData as a C array.
 *
 * @warning The pointer is only valid until the OFMutableData is changed!
 *
 * Modifying the returned array directly is allowed and will change the contents
 * of the data.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *mutableItems
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The first item of the OFMutableData or `NULL`.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *mutableFirstItem
    OF_RETURNS_INNER_POINTER;

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
 * @brief Creates a new OFMutableData with enough memory to hold the specified
 *	  number of items which all have the same specified size.
 *
 * @param itemSize The size of a single element in the OFMutableData
 * @param capacity The initial capacity for the OFMutableData
 * @return A new autoreleased OFMutableData
 */
+ (instancetype)dataWithItemSize: (size_t)itemSize
			capacity: (size_t)capacity;

/**
 * @brief Initializes an already allocated OFMutableData with an item size of 1.
 *
 * @return An initialized OFMutableData
 */
- (instancetype)init;







|
<







83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
 * @brief Creates a new OFMutableData with enough memory to hold the specified
 *	  number of items which all have the same specified size.
 *
 * @param itemSize The size of a single element in the OFMutableData
 * @param capacity The initial capacity for the OFMutableData
 * @return A new autoreleased OFMutableData
 */
+ (instancetype)dataWithItemSize: (size_t)itemSize capacity: (size_t)capacity;


/**
 * @brief Initializes an already allocated OFMutableData with an item size of 1.
 *
 * @return An initialized OFMutableData
 */
- (instancetype)init;
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
 *	  hold the the specified number of items which all have the same
 *	  specified size.
 *
 * @param itemSize The size of a single element in the OFMutableData
 * @param capacity The initial capacity for the OFMutableData
 * @return An initialized OFMutableData
 */
- (instancetype)initWithItemSize: (size_t)itemSize
			capacity: (size_t)capacity;

/**
 * @brief Returns a specific item of the OFMutableData.
 *
 * Modifying the returned item directly is allowed and will change the contents
 * of the data.
 *







|
<







120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
 *	  hold the the specified number of items which all have the same
 *	  specified size.
 *
 * @param itemSize The size of a single element in the OFMutableData
 * @param capacity The initial capacity for the OFMutableData
 * @return An initialized OFMutableData
 */
- (instancetype)initWithItemSize: (size_t)itemSize capacity: (size_t)capacity;


/**
 * @brief Returns a specific item of the OFMutableData.
 *
 * Modifying the returned item directly is allowed and will change the contents
 * of the data.
 *
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

/**
 * @brief Adds an item to the OFMutableData at the specified index.
 *
 * @param item A pointer to an arbitrary item
 * @param index The index where the item should be added
 */
- (void)insertItem: (const void *)item
	   atIndex: (size_t)index;

/**
 * @brief Adds items from a C array to the OFMutableData.
 *
 * @param items A C array containing the items to add
 * @param count The number of items to add
 */
- (void)addItems: (const void *)items
	   count: (size_t)count;

/**
 * @brief Adds items from a C array to the OFMutableData at the specified index.
 *
 * @param items A C array containing the items to add
 * @param index The index where the items should be added
 * @param count The number of items to add







|
<







|
<







146
147
148
149
150
151
152
153

154
155
156
157
158
159
160
161

162
163
164
165
166
167
168

/**
 * @brief Adds an item to the OFMutableData at the specified index.
 *
 * @param item A pointer to an arbitrary item
 * @param index The index where the item should be added
 */
- (void)insertItem: (const void *)item atIndex: (size_t)index;


/**
 * @brief Adds items from a C array to the OFMutableData.
 *
 * @param items A C array containing the items to add
 * @param count The number of items to add
 */
- (void)addItems: (const void *)items count: (size_t)count;


/**
 * @brief Adds items from a C array to the OFMutableData at the specified index.
 *
 * @param items A C array containing the items to add
 * @param index The index where the items should be added
 * @param count The number of items to add
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
- (void)removeItemAtIndex: (size_t)index;

/**
 * @brief Removes the specified amount of items at the specified index.
 *
 * @param range The range of items to remove
 */
- (void)removeItemsInRange: (of_range_t)range;

/**
 * @brief Removes the last item.
 */
- (void)removeLastItem;

/**







|







187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
- (void)removeItemAtIndex: (size_t)index;

/**
 * @brief Removes the specified amount of items at the specified index.
 *
 * @param range The range of items to remove
 */
- (void)removeItemsInRange: (OFRange)range;

/**
 * @brief Removes the last item.
 */
- (void)removeLastItem;

/**

Modified src/OFMutableData.m from [6ca92c6b7f] to [2aa51e2819].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
}

+ (instancetype)dataWithCapacity: (size_t)capacity
{
	return [[[self alloc] initWithCapacity: capacity] autorelease];
}

+ (instancetype)dataWithItemSize: (size_t)itemSize
			capacity: (size_t)capacity
{
	return [[[self alloc] initWithItemSize: itemSize
				      capacity: capacity] autorelease];
}

- (instancetype)init
{
	self = [super init];

	_itemSize = 1;


	return self;
}

- (instancetype)initWithItemSize: (size_t)itemSize
{
	self = [super init];

	@try {
		if (itemSize == 0)
			@throw [OFInvalidArgumentException exception];

		_itemSize = itemSize;

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

	return self;
}

- (instancetype)initWithCapacity: (size_t)capacity
{
	return [self initWithItemSize: 1
			     capacity: capacity];
}

- (instancetype)initWithItemSize: (size_t)itemSize
			capacity: (size_t)capacity
{
	self = [super init];

	@try {
		if (itemSize == 0)
			@throw [OFInvalidArgumentException exception];

		_items = [self allocMemoryWithSize: itemSize
					     count: capacity];

		_itemSize = itemSize;
		_capacity = capacity;

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

	return self;
}

- (instancetype)initWithItems: (const void *)items
		     itemSize: (size_t)itemSize
			count: (size_t)count

{
	self = [super initWithItems: items
			   itemSize: itemSize
			      count: count];

	_capacity = _count;

	return self;
}

- (instancetype)initWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone
{
	self = [self initWithItems: items
			  itemSize: itemSize
			     count: count];

	if (freeWhenDone)
		free(items);

	return self;
}

- (instancetype)initWithStringRepresentation: (OFString *)string
{
	self = [super initWithStringRepresentation: string];







|
<










>













>














|
<







<
<
|


>









<

>

|
<
<







<

>


|
<
<


|







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
}

+ (instancetype)dataWithCapacity: (size_t)capacity
{
	return [[[self alloc] initWithCapacity: capacity] autorelease];
}

+ (instancetype)dataWithItemSize: (size_t)itemSize capacity: (size_t)capacity

{
	return [[[self alloc] initWithItemSize: itemSize
				      capacity: capacity] autorelease];
}

- (instancetype)init
{
	self = [super init];

	_itemSize = 1;
	_freeWhenDone = true;

	return self;
}

- (instancetype)initWithItemSize: (size_t)itemSize
{
	self = [super init];

	@try {
		if (itemSize == 0)
			@throw [OFInvalidArgumentException exception];

		_itemSize = itemSize;
		_freeWhenDone = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithCapacity: (size_t)capacity
{
	return [self initWithItemSize: 1
			     capacity: capacity];
}

- (instancetype)initWithItemSize: (size_t)itemSize capacity: (size_t)capacity

{
	self = [super init];

	@try {
		if (itemSize == 0)
			@throw [OFInvalidArgumentException exception];



		_items = OFAllocMemory(capacity, itemSize);
		_itemSize = itemSize;
		_capacity = capacity;
		_freeWhenDone = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithItems: (const void *)items

			count: (size_t)count
		     itemSize: (size_t)itemSize
{
	self = [super initWithItems: items count: count itemSize: itemSize];



	_capacity = _count;

	return self;
}

- (instancetype)initWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone
{
	self = [self initWithItems: items count: count itemSize: itemSize];



	if (freeWhenDone)
		OFFreeMemory(items);

	return self;
}

- (instancetype)initWithStringRepresentation: (OFString *)string
{
	self = [super initWithStringRepresentation: string];
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
{
	if (_items == NULL || _count == 0)
		return NULL;

	return _items + (_count - 1) * _itemSize;
}

- (OFData *)subdataWithRange: (of_range_t)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _count)
		@throw [OFOutOfRangeException exception];

	return [OFData dataWithItems: _items + (range.location * _itemSize)

			    itemSize: _itemSize
			       count: range.length];
}

- (void)addItem: (const void *)item
{
	if (SIZE_MAX - _count < 1)
		@throw [OFOutOfRangeException exception];

	if (_count + 1 > _capacity) {
		_items = [self resizeMemory: _items
				       size: _itemSize
				      count: _count + 1];
		_capacity = _count + 1;
	}

	memcpy(_items + _count * _itemSize, item, _itemSize);

	_count++;
}

- (void)insertItem: (const void *)item
	   atIndex: (size_t)idx
{
	[self insertItems: item
		  atIndex: idx
		    count: 1];
}

- (void)addItems: (const void *)items
	   count: (size_t)count
{
	if (count > SIZE_MAX - _count)
		@throw [OFOutOfRangeException exception];

	if (_count + count > _capacity) {
		_items = [self resizeMemory: _items
				       size: _itemSize
				      count: _count + count];
		_capacity = _count + count;
	}

	memcpy(_items + _count * _itemSize, items, count * _itemSize);
	_count += count;
}

- (void)insertItems: (const void *)items
	    atIndex: (size_t)idx
	      count: (size_t)count
{
	if (count > SIZE_MAX - _count || idx > _count)
		@throw [OFOutOfRangeException exception];

	if (_count + count > _capacity) {
		_items = [self resizeMemory: _items
				       size: _itemSize
				      count: _count + count];
		_capacity = _count + count;
	}

	memmove(_items + (idx + count) * _itemSize, _items + idx * _itemSize,
	    (_count - idx) * _itemSize);
	memcpy(_items + idx * _itemSize, items, count * _itemSize);

	_count += count;
}

- (void)increaseCountBy: (size_t)count
{
	if (count > SIZE_MAX - _count)
		@throw [OFOutOfRangeException exception];

	if (_count + count > _capacity) {
		_items = [self resizeMemory: _items
				       size: _itemSize
				      count: _count + count];
		_capacity = _count + count;
	}

	memset(_items + _count * _itemSize, '\0', count * _itemSize);
	_count += count;
}

- (void)removeItemAtIndex: (size_t)idx
{
	[self removeItemsInRange: of_range(idx, 1)];
}

- (void)removeItemsInRange: (of_range_t)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _count)
		@throw [OFOutOfRangeException exception];

	memmove(_items + range.location * _itemSize,
	    _items + (range.location + range.length) * _itemSize,
	    (_count - range.location - range.length) * _itemSize);

	_count -= range.length;
	@try {
		_items = [self resizeMemory: _items
				       size: _itemSize
				      count: _count];
		_capacity = _count;
	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)removeLastItem
{
	if (_count == 0)
		return;

	_count--;
	@try {
		_items = [self resizeMemory: _items
				       size: _itemSize
				      count: _count];
		_capacity = _count;
	} @catch (OFOutOfMemoryException *e) {
		/* We don't care, as we only made it smaller */
	}
}

- (void)removeAllItems
{
	[self freeMemory: _items];

	_items = NULL;
	_count = 0;
	_capacity = 0;
}

- (id)copy
{
	return [[OFData alloc] initWithItems: _items

				    itemSize: _itemSize
				       count: _count];
}

- (void)makeImmutable
{









	object_setClass(self, [OFData class]);
}
@end







|






>
|
<








<
<
|








|
<

|
<
<


|
<





<
<
|















<
<
|
















<
<
|









|


|











|
<
<













|
<
<








|
<








>
|
<




>
>
>
>
>
>
>
>
>



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
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
{
	if (_items == NULL || _count == 0)
		return NULL;

	return _items + (_count - 1) * _itemSize;
}

- (OFData *)subdataWithRange: (OFRange)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _count)
		@throw [OFOutOfRangeException exception];

	return [OFData dataWithItems: _items + (range.location * _itemSize)
			       count: range.length
			    itemSize: _itemSize];

}

- (void)addItem: (const void *)item
{
	if (SIZE_MAX - _count < 1)
		@throw [OFOutOfRangeException exception];

	if (_count + 1 > _capacity) {


		_items = OFResizeMemory(_items, _count + 1, _itemSize);
		_capacity = _count + 1;
	}

	memcpy(_items + _count * _itemSize, item, _itemSize);

	_count++;
}

- (void)insertItem: (const void *)item atIndex: (size_t)idx

{
	[self insertItems: item atIndex: idx count: 1];


}

- (void)addItems: (const void *)items count: (size_t)count

{
	if (count > SIZE_MAX - _count)
		@throw [OFOutOfRangeException exception];

	if (_count + count > _capacity) {


		_items = OFResizeMemory(_items, _count + count, _itemSize);
		_capacity = _count + count;
	}

	memcpy(_items + _count * _itemSize, items, count * _itemSize);
	_count += count;
}

- (void)insertItems: (const void *)items
	    atIndex: (size_t)idx
	      count: (size_t)count
{
	if (count > SIZE_MAX - _count || idx > _count)
		@throw [OFOutOfRangeException exception];

	if (_count + count > _capacity) {


		_items = OFResizeMemory(_items, _count + count, _itemSize);
		_capacity = _count + count;
	}

	memmove(_items + (idx + count) * _itemSize, _items + idx * _itemSize,
	    (_count - idx) * _itemSize);
	memcpy(_items + idx * _itemSize, items, count * _itemSize);

	_count += count;
}

- (void)increaseCountBy: (size_t)count
{
	if (count > SIZE_MAX - _count)
		@throw [OFOutOfRangeException exception];

	if (_count + count > _capacity) {


		_items = OFResizeMemory(_items, _count + count, _itemSize);
		_capacity = _count + count;
	}

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

	memmove(_items + range.location * _itemSize,
	    _items + (range.location + range.length) * _itemSize,
	    (_count - range.location - range.length) * _itemSize);

	_count -= range.length;
	@try {
		_items = OFResizeMemory(_items, _count, _itemSize);


		_capacity = _count;
	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)removeLastItem
{
	if (_count == 0)
		return;

	_count--;
	@try {
		_items = OFResizeMemory(_items, _count, _itemSize);


		_capacity = _count;
	} @catch (OFOutOfMemoryException *e) {
		/* We don't care, as we only made it smaller */
	}
}

- (void)removeAllItems
{
	OFFreeMemory(_items);

	_items = NULL;
	_count = 0;
	_capacity = 0;
}

- (id)copy
{
	return [[OFData alloc] initWithItems: _items
				       count: _count
				    itemSize: _itemSize];

}

- (void)makeImmutable
{
	if (_capacity != _count) {
		@try {
			_items = OFResizeMemory(_items, _count, _itemSize);
			_capacity = _count;
		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only made it smaller */
		}
	}

	object_setClass(self, [OFData class]);
}
@end

Modified src/OFMutableDictionary.h from [456cb78a11] to [851a8f19f7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
 * @brief A block for replacing objects in an OFMutableDictionary.
 *
 * @param key The key of the object to replace
 * @param object The object to replace
 * @return The object to replace the object with
 */
typedef id _Nonnull (^of_dictionary_replace_block_t)(id key, id object);
#endif

/**
 * @class OFMutableDictionary OFDictionary.h ObjFW/OFDictionary.h
 *
 * @brief An abstract class for storing and changing objects in a dictionary.
 *







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
 * @brief A block for replacing objects in an OFMutableDictionary.
 *
 * @param key The key of the object to replace
 * @param object The object to replace
 * @return The object to replace the object with
 */
typedef id _Nonnull (^OFDictionaryReplaceBlock)(id key, id object);
#endif

/**
 * @class OFMutableDictionary OFDictionary.h ObjFW/OFDictionary.h
 *
 * @brief An abstract class for storing and changing objects in a dictionary.
 *
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
 * @brief Sets an object for a key.
 *
 * A key can be any object that conforms to the OFCopying protocol.
 *
 * @param key The key to set
 * @param object The object to set the key to
 */
- (void)setObject: (ObjectType)object
	   forKey: (KeyType)key;

/**
 * @brief Sets an object for a key.
 *
 * A key can be any object that conforms to the OFCopying protocol.
 *
 * This method is also used by the subscripting syntax.
 *
 * @param key The key to set
 * @param object The object to set the key to. If it is nil, this is equal to
 *		 calling @ref removeObjectForKey:.
 */
-   (void)setObject: (nullable ObjectType)object
  forKeyedSubscript: (KeyType)key;

/**
 * @brief Removes the object for the specified key from the dictionary.
 *
 * @param key The key whose object should be removed
 */
- (void)removeObjectForKey: (KeyType)key;







|
<












|
<







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
 * @brief Sets an object for a key.
 *
 * A key can be any object that conforms to the OFCopying protocol.
 *
 * @param key The key to set
 * @param object The object to set the key to
 */
- (void)setObject: (ObjectType)object forKey: (KeyType)key;


/**
 * @brief Sets an object for a key.
 *
 * A key can be any object that conforms to the OFCopying protocol.
 *
 * This method is also used by the subscripting syntax.
 *
 * @param key The key to set
 * @param object The object to set the key to. If it is nil, this is equal to
 *		 calling @ref removeObjectForKey:.
 */
- (void)setObject: (nullable ObjectType)object forKeyedSubscript: (KeyType)key;


/**
 * @brief Removes the object for the specified key from the dictionary.
 *
 * @param key The key whose object should be removed
 */
- (void)removeObjectForKey: (KeyType)key;
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Replaces each object with the object returned by the block.
 *
 * @param block The block which returns a new object for each object
 */
- (void)replaceObjectsUsingBlock: (of_dictionary_replace_block_t)block;
#endif

/**
 * @brief Converts the mutable dictionary to an immutable dictionary.
 */
- (void)makeImmutable;
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef KeyType
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END







|













108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Replaces each object with the object returned by the block.
 *
 * @param block The block which returns a new object for each object
 */
- (void)replaceObjectsUsingBlock: (OFDictionaryReplaceBlock)block;
#endif

/**
 * @brief Converts the mutable dictionary to an immutable dictionary.
 */
- (void)makeImmutable;
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef KeyType
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END

Modified src/OFMutableDictionary.m from [178e351b06] to [459ca84c71].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

- (instancetype)initWithDictionary: (OFDictionary *)dictionary
{
	return (id)[[OFMutableMapTableDictionary alloc]
	    initWithDictionary: dictionary];
}

- (instancetype)initWithObject: (id)object
			forKey: (id)key
{
	return (id)[[OFMutableMapTableDictionary alloc] initWithObject: object
								forKey: key];
}

- (instancetype)initWithObjects: (OFArray *)objects
			forKeys: (OFArray *)keys
{
	return (id)[[OFMutableMapTableDictionary alloc] initWithObjects: objects
								forKeys: keys];
}

- (instancetype)initWithObjects: (id const *)objects
			forKeys: (id const *)keys







|
<





|
<







36
37
38
39
40
41
42
43

44
45
46
47
48
49

50
51
52
53
54
55
56

- (instancetype)initWithDictionary: (OFDictionary *)dictionary
{
	return (id)[[OFMutableMapTableDictionary alloc]
	    initWithDictionary: dictionary];
}

- (instancetype)initWithObject: (id)object forKey: (id)key

{
	return (id)[[OFMutableMapTableDictionary alloc] initWithObject: object
								forKey: key];
}

- (instancetype)initWithObjects: (OFArray *)objects forKeys: (OFArray *)keys

{
	return (id)[[OFMutableMapTableDictionary alloc] initWithObjects: objects
								forKeys: keys];
}

- (instancetype)initWithObjects: (id const *)objects
			forKeys: (id const *)keys
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
	ret = (id)[[OFMutableMapTableDictionary alloc] initWithKey: firstKey
							 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithKey: (id)firstKey
		  arguments: (va_list)arguments
{
	return (id)[[OFMutableMapTableDictionary alloc] initWithKey: firstKey
							  arguments: arguments];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{







|
<







70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
	ret = (id)[[OFMutableMapTableDictionary alloc] initWithKey: firstKey
							 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithKey: (id)firstKey arguments: (va_list)arguments

{
	return (id)[[OFMutableMapTableDictionary alloc] initWithKey: firstKey
							  arguments: arguments];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
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
}

- (instancetype)initWithCapacity: (size_t)capacity
{
	OF_INVALID_INIT_METHOD
}

- (void)setObject: (id)object
	   forKey: (id)key
{
	OF_UNRECOGNIZED_SELECTOR
}

-   (void)setObject: (id)object
  forKeyedSubscript: (id)key
{
	if (object != nil)
		[self setObject: object
			 forKey: key];
	else
		[self removeObjectForKey: key];
}

- (void)removeObjectForKey: (id)key
{
	OF_UNRECOGNIZED_SELECTOR







|
<




|
<


|
<







149
150
151
152
153
154
155
156

157
158
159
160
161

162
163
164

165
166
167
168
169
170
171
}

- (instancetype)initWithCapacity: (size_t)capacity
{
	OF_INVALID_INIT_METHOD
}

- (void)setObject: (id)object forKey: (id)key

{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setObject: (id)object forKeyedSubscript: (id)key

{
	if (object != nil)
		[self setObject: object forKey: key];

	else
		[self removeObjectForKey: key];
}

- (void)removeObjectForKey: (id)key
{
	OF_UNRECOGNIZED_SELECTOR
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
	void *pool = objc_autoreleasePoolPush();
	OFEnumerator *keyEnumerator = [dictionary keyEnumerator];
	OFEnumerator *objectEnumerator = [dictionary objectEnumerator];
	id key, object;

	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil)
		[self setObject: object
			 forKey: key];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)replaceObjectsUsingBlock: (of_dictionary_replace_block_t)block
{
	[self enumerateKeysAndObjectsUsingBlock: ^ (id key, id object,
	    bool *stop) {
		id new = block(key, object);

		if (new != object) {
			[self setObject: block(key, object)
				 forKey: key];
		}
	}];
}
#endif

- (void)makeImmutable
{
}
@end







|
<





|






|
<









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
	void *pool = objc_autoreleasePoolPush();
	OFEnumerator *keyEnumerator = [dictionary keyEnumerator];
	OFEnumerator *objectEnumerator = [dictionary objectEnumerator];
	id key, object;

	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil)
		[self setObject: object forKey: key];


	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)replaceObjectsUsingBlock: (OFDictionaryReplaceBlock)block
{
	[self enumerateKeysAndObjectsUsingBlock: ^ (id key, id object,
	    bool *stop) {
		id new = block(key, object);

		if (new != object) {
			[self setObject: block(key, object) forKey: key];

		}
	}];
}
#endif

- (void)makeImmutable
{
}
@end

Modified src/OFMutableLHAArchiveEntry.h from [ec3dafe13e] to [4253f691f5].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutableLHAArchiveEntry.m from [164ba61cd2] to [8d9a6a0cb1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutableMapTableDictionary.h from [64ba6e9547] to [e3ea116447].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutableMapTableDictionary.m from [9a2bd23662] to [eda929d7fd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@implementation OFMutableMapTableDictionary
+ (void)initialize
{
	if (self == [OFMutableMapTableDictionary class])
		[self inheritMethodsFromClass: [OFMapTableDictionary class]];
}

- (void)setObject: (id)object
	   forKey: (id)key
{
	[_mapTable setObject: object
		      forKey: key];
}

- (void)removeObjectForKey: (id)key
{
	[_mapTable removeObjectForKey: key];
}

- (void)removeAllObjects
{
	[_mapTable removeAllObjects];
}

#ifdef OF_HAVE_BLOCKS
- (void)replaceObjectsUsingBlock: (of_dictionary_replace_block_t)block
{
	@try {
		[_mapTable replaceObjectsUsingBlock:
		    ^ void *(void *key, void *object) {
			return block(key, object);
		}];
	} @catch (OFEnumerationMutationException *e) {







|
<

|
<













|







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
@implementation OFMutableMapTableDictionary
+ (void)initialize
{
	if (self == [OFMutableMapTableDictionary class])
		[self inheritMethodsFromClass: [OFMapTableDictionary class]];
}

- (void)setObject: (id)object forKey: (id)key

{
	[_mapTable setObject: object forKey: key];

}

- (void)removeObjectForKey: (id)key
{
	[_mapTable removeObjectForKey: key];
}

- (void)removeAllObjects
{
	[_mapTable removeAllObjects];
}

#ifdef OF_HAVE_BLOCKS
- (void)replaceObjectsUsingBlock: (OFDictionaryReplaceBlock)block
{
	@try {
		[_mapTable replaceObjectsUsingBlock:
		    ^ void *(void *key, void *object) {
			return block(key, object);
		}];
	} @catch (OFEnumerationMutationException *e) {

Modified src/OFMutableMapTableSet.h from [b78ac106e9] to [ee94211a1c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutableMapTableSet.m from [d444b25d5d] to [134371a3b6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
	if (self == [OFMutableMapTableSet class])
		[self inheritMethodsFromClass: [OFMapTableSet class]];
}

- (void)addObject: (id)object
{
	[_mapTable setObject: (void *)1
		      forKey: object];
}

- (void)removeObject: (id)object
{
	[_mapTable removeObjectForKey: object];
}








|
<







24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
{
	if (self == [OFMutableMapTableSet class])
		[self inheritMethodsFromClass: [OFMapTableSet class]];
}

- (void)addObject: (id)object
{
	[_mapTable setObject: (void *)1 forKey: object];

}

- (void)removeObject: (id)object
{
	[_mapTable removeObjectForKey: object];
}

Modified src/OFMutablePair.h from [d92de22cb9] to [fd02ba0f90].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutablePair.m from [d45cf41ccb] to [6901454ea9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
	_secondObject = [secondObject retain];
	[old release];
}

- (id)copy
{
	OFMutablePair *copy = [self mutableCopy];

	[copy makeImmutable];

	return copy;
}

- (void)makeImmutable
{
	object_setClass(self, [OFPair class]);
}
@end







<

<








33
34
35
36
37
38
39

40

41
42
43
44
45
46
47
48
	_secondObject = [secondObject retain];
	[old release];
}

- (id)copy
{
	OFMutablePair *copy = [self mutableCopy];

	[copy makeImmutable];

	return copy;
}

- (void)makeImmutable
{
	object_setClass(self, [OFPair class]);
}
@end

Modified src/OFMutableSet.h from [596b811b75] to [c05177c6a6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutableSet.m from [460faa4705] to [b126234b89].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
	ret = [[OFMutableMapTableSet alloc] initWithObject: firstObject
						 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	return (id)[[OFMutableMapTableSet alloc] initWithObjects: objects
							   count: count];
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	return (id)[[OFMutableMapTableSet alloc] initWithObject: firstObject
						      arguments: arguments];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{







|
<





|
<







54
55
56
57
58
59
60
61

62
63
64
65
66
67

68
69
70
71
72
73
74
	ret = [[OFMutableMapTableSet alloc] initWithObject: firstObject
						 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	return (id)[[OFMutableMapTableSet alloc] initWithObjects: objects
							   count: count];
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	return (id)[[OFMutableMapTableSet alloc] initWithObject: firstObject
						      arguments: arguments];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
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

- (void)intersectSet: (OFSet *)set
{
	void *pool = objc_autoreleasePoolPush();
	size_t count = self.count;
	id *cArray;

	cArray = [self allocMemoryWithSize: sizeof(id)
				     count: count];

	@try {
		size_t i;

		i = 0;
		for (id object in self) {
			assert(i < count);
			cArray[i++] = object;
		}

		for (i = 0; i < count; i++)
			if (![set containsObject: cArray[i]])
			      [self removeObject: cArray[i]];
	} @finally {
		[self freeMemory: cArray];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)unionSet: (OFSet *)set
{







<
<
|











|

|







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

- (void)intersectSet: (OFSet *)set
{
	void *pool = objc_autoreleasePoolPush();
	size_t count = self.count;
	id *cArray;



	cArray = OFAllocMemory(count, sizeof(id));
	@try {
		size_t i;

		i = 0;
		for (id object in self) {
			assert(i < count);
			cArray[i++] = object;
		}

		for (i = 0; i < count; i++)
			if (![set containsObject: cArray[i]])
				[self removeObject: cArray[i]];
	} @finally {
		OFFreeMemory(cArray);
	}

	objc_autoreleasePoolPop(pool);
}

- (void)unionSet: (OFSet *)set
{

Modified src/OFMutableString.h from [4d268ac292] to [9300a1f2cf].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@interface OFMutableString: OFString
/**
 * @brief Sets the character at the specified index.
 *
 * @param character The character to set
 * @param index The index where to set the character
 */
- (void)setCharacter: (of_unichar_t)character
	     atIndex: (size_t)index;

/**
 * @brief Appends another OFString to the OFMutableString.
 *
 * @param string An OFString to append
 */
- (void)appendString: (OFString *)string;

/**
 * @brief Appends the specified characters to the OFMutableString.
 *
 * @param characters An array of characters to append
 * @param length The length of the array of characters
 */
- (void)appendCharacters: (const of_unichar_t *)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;







|
<














|
<







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
@interface OFMutableString: OFString
/**
 * @brief Sets the character at the specified index.
 *
 * @param character The character to set
 * @param index The index where to set the character
 */
- (void)setCharacter: (OFUnichar)character atIndex: (size_t)index;


/**
 * @brief Appends another OFString to the OFMutableString.
 *
 * @param string An OFString to append
 */
- (void)appendString: (OFString *)string;

/**
 * @brief Appends the specified characters to the OFMutableString.
 *
 * @param characters An array of characters to append
 * @param length The length of the array of characters
 */
- (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;
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
/**
 * @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: (of_string_encoding_t)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: (of_string_encoding_t)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 `of_unichar_t` and `%S` for
 * `const of_unichar_t *`.
 *
 * @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 `of_unichar_t` and `%S` for
 * `const of_unichar_t *`.
 *
 * @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;







|










|






|
|









|
|




|
<







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

/**
 * @brief Inserts a string at the specified index.
 *
 * @param string The string to insert
 * @param index The index
 */
- (void)insertString: (OFString *)string
	     atIndex: (size_t)index;

/**
 * @brief Deletes the characters at the specified range.
 *
 * @param range The range of the characters which should be removed
 */
- (void)deleteCharactersInRange: (of_range_t)range;

/**
 * @brief Replaces the characters at the specified range.
 *
 * @param range The range of the characters which should be replaced
 * @param replacement The string to the replace the characters with
 */
- (void)replaceCharactersInRange: (of_range_t)range
		      withString: (OFString *)replacement;

/**
 * @brief Replaces all occurrences of a string with another string.
 *
 * @param string The string to replace
 * @param replacement The string with which it should be replaced







|
<






|







|







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

/**
 * @brief Inserts a string at the specified index.
 *
 * @param string The string to insert
 * @param index The index
 */
- (void)insertString: (OFString *)string atIndex: (size_t)index;


/**
 * @brief Deletes the characters at the specified range.
 *
 * @param range The range of the characters which should be removed
 */
- (void)deleteCharactersInRange: (OFRange)range;

/**
 * @brief Replaces the characters at the specified range.
 *
 * @param range The range of the characters which should be replaced
 * @param replacement The string to the replace the characters with
 */
- (void)replaceCharactersInRange: (OFRange)range
		      withString: (OFString *)replacement;

/**
 * @brief Replaces all occurrences of a string with another string.
 *
 * @param string The string to replace
 * @param replacement The string with which it should be replaced
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
 * @param options Options modifying search behaviour
 *		  Possible values: None yet
 * @param range The range in which the string should be replaced
 */
- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
			   options: (int)options
			     range: (of_range_t)range;

/**
 * @brief Deletes all whitespaces at the beginning of the string.
 */
- (void)deleteLeadingWhitespaces;

/**







|







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
 * @param options Options modifying search behaviour
 *		  Possible values: None yet
 * @param range The range in which the string should be replaced
 */
- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
			   options: (int)options
			     range: (OFRange)range;

/**
 * @brief Deletes all whitespaces at the beginning of the string.
 */
- (void)deleteLeadingWhitespaces;

/**

Modified src/OFMutableString.m from [bb86d6b9da] to [a015aada74].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdarg.h>
#include <stdlib.h>
#include <string.h>

#import "OFString.h"

#import "OFMutableUTF8String.h"

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

#import "of_asprintf.h"
#import "unicode.h"

static struct {
	Class isa;
} placeholder;

@interface OFMutableStringPlaceholder: OFMutableString

<
<
|




















>






<







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


 * 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 <stdarg.h>
#include <stdlib.h>
#include <string.h>

#import "OFString.h"
#import "OFASPrintF.h"
#import "OFMutableUTF8String.h"

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


#import "unicode.h"

static struct {
	Class isa;
} placeholder;

@interface OFMutableStringPlaceholder: OFMutableString
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
{
	return (id)[[OFMutableUTF8String alloc]
	    initWithUTF8String: UTF8String
			length: UTF8StringLength];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
{
	return (id)[[OFMutableUTF8String alloc] initWithCString: cString
						       encoding: encoding];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
			 length: (size_t)cStringLength
{
	return (id)[[OFMutableUTF8String alloc] initWithCString: cString
						       encoding: encoding
							 length: cStringLength];
}

- (instancetype)initWithString: (OFString *)string
{
	return (id)[[OFMutableUTF8String alloc] initWithString: string];
}

- (instancetype)initWithCharacters: (const of_unichar_t *)characters
			    length: (size_t)length
{
	return (id)[[OFMutableUTF8String alloc] initWithCharacters: characters
							    length: length];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF16String: string];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF16String: string
							      length: length];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			  byteOrder: (of_byte_order_t)byteOrder
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF16String: string
							  byteOrder: byteOrder];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF16String: string
							     length: length
							  byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF32String: string];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			     length: (size_t)length
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF32String: string
							     length: length];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			  byteOrder: (of_byte_order_t)byteOrder
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF32String: string
							  byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF32String: string
							     length: length
							  byteOrder: byteOrder];
}

- (instancetype)initWithFormat: (OFConstantString *)format, ...







|






|












|






|




|






|
|





|

|






|




|






|
|





|

|







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
{
	return (id)[[OFMutableUTF8String alloc]
	    initWithUTF8String: UTF8String
			length: UTF8StringLength];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
{
	return (id)[[OFMutableUTF8String alloc] initWithCString: cString
						       encoding: encoding];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
			 length: (size_t)cStringLength
{
	return (id)[[OFMutableUTF8String alloc] initWithCString: cString
						       encoding: encoding
							 length: cStringLength];
}

- (instancetype)initWithString: (OFString *)string
{
	return (id)[[OFMutableUTF8String alloc] initWithString: string];
}

- (instancetype)initWithCharacters: (const OFUnichar *)characters
			    length: (size_t)length
{
	return (id)[[OFMutableUTF8String alloc] initWithCharacters: characters
							    length: length];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF16String: string];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF16String: string
							      length: length];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			  byteOrder: (OFByteOrder)byteOrder
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF16String: string
							  byteOrder: byteOrder];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF16String: string
							     length: length
							  byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF32String: string];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			     length: (size_t)length
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF32String: string
							     length: length];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			  byteOrder: (OFByteOrder)byteOrder
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF32String: string
							  byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	return (id)[[OFMutableUTF8String alloc] initWithUTF32String: string
							     length: length
							  byteOrder: byteOrder];
}

- (instancetype)initWithFormat: (OFConstantString *)format, ...
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
#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
	return (id)[[OFMutableUTF8String alloc] initWithContentsOfFile: path];
}

- (instancetype)initWithContentsOfFile: (OFString *)path
			      encoding: (of_string_encoding_t)encoding
{
	return (id)[[OFMutableUTF8String alloc]
	    initWithContentsOfFile: path
			  encoding: encoding];
}
#endif

#if defined(OF_HAVE_FILES) || defined(OF_HAVE_SOCKETS)
- (instancetype)initWithContentsOfURL: (OFURL *)URL
{
	return (id)[[OFMutableUTF8String alloc] initWithContentsOfURL: URL];
}

- (instancetype)initWithContentsOfURL: (OFURL *)URL
			     encoding: (of_string_encoding_t)encoding
{
	return (id)[[OFMutableUTF8String alloc]
	    initWithContentsOfURL: URL
			 encoding: encoding];
}
#endif

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	return (id)[[OFMutableUTF8String alloc] initWithSerialization: element];
}

- (instancetype)retain







|







<






|





<







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
#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
	return (id)[[OFMutableUTF8String alloc] initWithContentsOfFile: path];
}

- (instancetype)initWithContentsOfFile: (OFString *)path
			      encoding: (OFStringEncoding)encoding
{
	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];
}

- (instancetype)retain
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
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
	if (self == [OFMutableString class])
		return (id)&placeholder;

	return [super alloc];
}

#ifdef OF_HAVE_UNICODE_TABLES
- (void)of_convertWithWordStartTable: (const of_unichar_t *const [])startTable
		     wordMiddleTable: (const of_unichar_t *const [])middleTable
		  wordStartTableSize: (size_t)startTableSize
		 wordMiddleTableSize: (size_t)middleTableSize OF_DIRECT
{
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters = self.characters;
	size_t length = self.length;
	bool isStart = true;

	for (size_t i = 0; i < length; i++) {
		const of_unichar_t *const *table;
		size_t tableSize;
		of_unichar_t c = characters[i];

		if (isStart) {
			table = startTable;
			tableSize = middleTableSize;
		} else {
			table = middleTable;
			tableSize = middleTableSize;
		}

		if (c >> 8 < tableSize && table[c >> 8][c & 0xFF])
			[self setCharacter: table[c >> 8][c & 0xFF]
				   atIndex: i];

		isStart = of_ascii_isspace(c);
	}

	objc_autoreleasePoolPop(pool);
}
#else

- (void)of_convertWithWordStartFunction: (char (*)(char))startFunction
		     wordMiddleFunction: (char (*)(char))middleFunction
    OF_DIRECT
{
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters = self.characters;
	size_t length = self.length;
	bool isStart = true;

	for (size_t i = 0; i < length; i++) {
		char (*function)(char) =
		    (isStart ? startFunction : middleFunction);
		of_unichar_t c = characters[i];

		if (c <= 0x7F)
			[self setCharacter: (int)function(c)
				   atIndex: i];

		isStart = of_ascii_isspace(c);
	}

	objc_autoreleasePoolPop(pool);
}
#endif

- (void)setCharacter: (of_unichar_t)character
	     atIndex: (size_t)idx
{
	void *pool = objc_autoreleasePoolPush();
	OFString *string;

	string = [OFString stringWithCharacters: &character
					 length: 1];

	[self replaceCharactersInRange: of_range(idx, 1)
			    withString: string];

	objc_autoreleasePoolPop(pool);
}

- (void)appendString: (OFString *)string
{
	[self insertString: string
		   atIndex: self.length];
}

- (void)appendCharacters: (const of_unichar_t *)characters
		  length: (size_t)length
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithCharacters: characters
						    length: length]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendUTF8String: (const char *)UTF8String
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithUTF8String: UTF8String]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendUTF8String: (const char *)UTF8String
		  length: (size_t)UTF8StringLength
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithUTF8String: UTF8String
						    length: UTF8StringLength]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendCString: (const char *)cString
	     encoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithCString: cString
					       encoding: encoding]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendCString: (const char *)cString
	     encoding: (of_string_encoding_t)encoding
	       length: (size_t)cStringLength
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithCString: cString
					       encoding: encoding
						 length: cStringLength]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendFormat: (OFConstantString *)format, ...
{
	va_list arguments;

	va_start(arguments, format);
	[self appendFormat: format
		 arguments: arguments];
	va_end(arguments);
}

- (void)appendFormat: (OFConstantString *)format
	   arguments: (va_list)arguments
{
	char *UTF8String;
	int UTF8StringLength;

	if (format == nil)
		@throw [OFInvalidArgumentException exception];

	if ((UTF8StringLength = of_vasprintf(&UTF8String, format.UTF8String,
	    arguments)) == -1)
		@throw [OFInvalidFormatException exception];

	@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--) {
		of_unichar_t 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: of_unicode_uppercase_table
			   wordMiddleTable: of_unicode_uppercase_table
			wordStartTableSize: OF_UNICODE_UPPERCASE_TABLE_SIZE
		       wordMiddleTableSize: OF_UNICODE_UPPERCASE_TABLE_SIZE];
}

- (void)lowercase
{
	[self of_convertWithWordStartTable: of_unicode_lowercase_table
			   wordMiddleTable: of_unicode_lowercase_table
			wordStartTableSize: OF_UNICODE_LOWERCASE_TABLE_SIZE
		       wordMiddleTableSize: OF_UNICODE_LOWERCASE_TABLE_SIZE];
}

- (void)capitalize
{
	[self of_convertWithWordStartTable: of_unicode_titlecase_table
			   wordMiddleTable: of_unicode_lowercase_table
			wordStartTableSize: OF_UNICODE_TITLECASE_TABLE_SIZE
		       wordMiddleTableSize: OF_UNICODE_LOWERCASE_TABLE_SIZE];
}
#else
- (void)uppercase
{
	[self of_convertWithWordStartFunction: of_ascii_toupper
			   wordMiddleFunction: of_ascii_toupper];
}




- (void)lowercase
{
	[self of_convertWithWordStartFunction: of_ascii_tolower
			   wordMiddleFunction: of_ascii_tolower];
}

- (void)capitalize
{
	[self of_convertWithWordStartFunction: of_ascii_toupper
			   wordMiddleFunction: of_ascii_tolower];
}
#endif

- (void)insertString: (OFString *)string
	     atIndex: (size_t)idx
{
	[self replaceCharactersInRange: of_range(idx, 0)
			    withString: string];
}

- (void)deleteCharactersInRange: (of_range_t)range
{
	[self replaceCharactersInRange: range
			    withString: @""];
}

- (void)replaceCharactersInRange: (of_range_t)range
		      withString: (OFString *)replacement
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
{
	[self replaceOccurrencesOfString: string
			      withString: replacement
				 options: 0
				   range: of_range(0, self.length)];
}

- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
			   options: (int)options
			     range: (of_range_t)range
{
	void *pool = objc_autoreleasePoolPush(), *pool2;
	const of_unichar_t *characters;
	const of_unichar_t *searchCharacters = string.characters;
	size_t searchLength = string.length;
	size_t replacementLength = replacement.length;

	if (string == nil || replacement == nil)
		@throw [OFInvalidArgumentException exception];

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

	if (searchLength > range.length) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	pool2 = objc_autoreleasePoolPush();
	characters = self.characters;

	for (size_t i = range.location; i <= range.length - searchLength; i++) {
		if (memcmp(characters + i, searchCharacters,
		    searchLength * sizeof(of_unichar_t)) != 0)
			continue;

		[self replaceCharactersInRange: of_range(i, searchLength)
				    withString: replacement];

		range.length -= searchLength;
		range.length += replacementLength;

		i += replacementLength - 1;

		objc_autoreleasePoolPop(pool2);
		pool2 = objc_autoreleasePoolPush();

		characters = self.characters;
	}

	objc_autoreleasePoolPop(pool);
}

- (void)deleteLeadingWhitespaces
{
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters = self.characters;
	size_t i, length = self.length;

	for (i = 0; i < length; i++) {
		of_unichar_t c = characters[i];

		if (!of_ascii_isspace(c))
			break;
	}

	objc_autoreleasePoolPop(pool);

	[self deleteCharactersInRange: of_range(0, i)];
}

- (void)deleteTrailingWhitespaces
{
	void *pool;
	const of_unichar_t *characters, *p;
	size_t length, d;

	length = self.length;

	if (length == 0)
		return;

	pool = objc_autoreleasePoolPush();
	characters = self.characters;

	d = 0;
	for (p = characters + length - 1; p >= characters; p--) {
		if (!of_ascii_isspace(*p))
			break;

		d++;
	}

	objc_autoreleasePoolPop(pool);

	[self deleteCharactersInRange: of_range(length - d, d)];
}

- (void)deleteEnclosingWhitespaces
{
	[self deleteLeadingWhitespaces];
	[self deleteTrailingWhitespaces];
}







|
|

|


|




|

|










|
<

|





>
|
|
<


|






|


|
<

|






|
<


|
<
|
<
<
|
<
<





|
<


|



<


<






<

<







<


<




|


<


<




|



<



<













|
<







|




|
<







|
<







|
|
<
|
<






|
|
|
|




|
|
|
|




|
|
|
|



<
<
<
<
|
>
>
>


|
<




|
<



|
<

|
<


|

|
<


|











|





|


|
|




















|


|



















|



|

|





|





|












|







|







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
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
	if (self == [OFMutableString class])
		return (id)&placeholder;

	return [super alloc];
}

#ifdef OF_HAVE_UNICODE_TABLES
- (void)of_convertWithWordStartTable: (const OFUnichar *const [])startTable
		     wordMiddleTable: (const OFUnichar *const [])middleTable
		  wordStartTableSize: (size_t)startTableSize
		 wordMiddleTableSize: (size_t)middleTableSize
{
	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters = self.characters;
	size_t length = self.length;
	bool isStart = true;

	for (size_t i = 0; i < length; i++) {
		const OFUnichar *const *table;
		size_t tableSize;
		OFUnichar c = characters[i];

		if (isStart) {
			table = startTable;
			tableSize = middleTableSize;
		} else {
			table = middleTable;
			tableSize = middleTableSize;
		}

		if (c >> 8 < tableSize && table[c >> 8][c & 0xFF])
			[self setCharacter: table[c >> 8][c & 0xFF] atIndex: i];


		isStart = OFASCIIIsSpace(c);
	}

	objc_autoreleasePoolPop(pool);
}
#else
static void
convert(OFMutableString *self, char (*startFunction)(char),
    char (*middleFunction)(char))

{
	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters = self.characters;
	size_t length = self.length;
	bool isStart = true;

	for (size_t i = 0; i < length; i++) {
		char (*function)(char) =
		    (isStart ? startFunction : middleFunction);
		OFUnichar c = characters[i];

		if (c <= 0x7F)
			[self setCharacter: (int)function(c) atIndex: i];


		isStart = OFASCIIIsSpace(c);
	}

	objc_autoreleasePoolPop(pool);
}
#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];

}

- (void)appendCharacters: (const OFUnichar *)characters
		  length: (size_t)length
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithCharacters: characters
						    length: length]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendUTF8String: (const char *)UTF8String
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithUTF8String: UTF8String]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendUTF8String: (const char *)UTF8String
		  length: (size_t)UTF8StringLength
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithUTF8String: UTF8String
						    length: UTF8StringLength]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendCString: (const char *)cString
	     encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithCString: cString
					       encoding: encoding]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendCString: (const char *)cString
	     encoding: (OFStringEncoding)encoding
	       length: (size_t)cStringLength
{
	void *pool = objc_autoreleasePoolPush();

	[self appendString: [OFString stringWithCString: cString
					       encoding: encoding
						 length: cStringLength]];

	objc_autoreleasePoolPop(pool);
}

- (void)appendFormat: (OFConstantString *)format, ...
{
	va_list arguments;

	va_start(arguments, format);
	[self appendFormat: format
		 arguments: arguments];
	va_end(arguments);
}

- (void)appendFormat: (OFConstantString *)format arguments: (va_list)arguments

{
	char *UTF8String;
	int UTF8StringLength;

	if (format == nil)
		@throw [OFInvalidArgumentException exception];

	if ((UTF8StringLength = OFVASPrintF(&UTF8String, format.UTF8String,
	    arguments)) == -1)
		@throw [OFInvalidFormatException exception];

	@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];
}

- (void)lowercase
{
	[self of_convertWithWordStartTable: OFUnicodeLowercaseTable
			   wordMiddleTable: OFUnicodeLowercaseTable
			wordStartTableSize: OFUnicodeLowercaseTableSize
		       wordMiddleTableSize: OFUnicodeLowercaseTableSize];
}

- (void)capitalize
{
	[self of_convertWithWordStartTable: OFUnicodeTitlecaseTable
			   wordMiddleTable: OFUnicodeLowercaseTable
			wordStartTableSize: OFUnicodeTitlecaseTableSize
		       wordMiddleTableSize: OFUnicodeLowercaseTableSize];
}
#else
- (void)uppercase




{
	convert(self, OFASCIIToUpper, OFASCIIToUpper);
}

- (void)lowercase
{
	convert(self, OFASCIIToLower, OFASCIIToLower);

}

- (void)capitalize
{
	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
{
	void *pool = objc_autoreleasePoolPush(), *pool2;
	const OFUnichar *characters;
	const OFUnichar *searchCharacters = string.characters;
	size_t searchLength = string.length;
	size_t replacementLength = replacement.length;

	if (string == nil || replacement == nil)
		@throw [OFInvalidArgumentException exception];

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

	if (searchLength > range.length) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	pool2 = objc_autoreleasePoolPush();
	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;

		objc_autoreleasePoolPop(pool2);
		pool2 = objc_autoreleasePoolPush();

		characters = self.characters;
	}

	objc_autoreleasePoolPop(pool);
}

- (void)deleteLeadingWhitespaces
{
	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters = self.characters;
	size_t i, length = self.length;

	for (i = 0; i < length; i++) {
		OFUnichar c = characters[i];

		if (!OFASCIIIsSpace(c))
			break;
	}

	objc_autoreleasePoolPop(pool);

	[self deleteCharactersInRange: OFRangeMake(0, i)];
}

- (void)deleteTrailingWhitespaces
{
	void *pool;
	const OFUnichar *characters, *p;
	size_t length, d;

	length = self.length;

	if (length == 0)
		return;

	pool = objc_autoreleasePoolPush();
	characters = self.characters;

	d = 0;
	for (p = characters + length - 1; p >= characters; p--) {
		if (!OFASCIIIsSpace(*p))
			break;

		d++;
	}

	objc_autoreleasePoolPop(pool);

	[self deleteCharactersInRange: OFRangeMake(length - d, d)];
}

- (void)deleteEnclosingWhitespaces
{
	[self deleteLeadingWhitespaces];
	[self deleteTrailingWhitespaces];
}

Modified src/OFMutableTarArchiveEntry.h from [d27d9283d7] to [f3b4444a50].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 * @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 of_tar_archive_entry_type_t.
 */
@property (readwrite, nonatomic) of_tar_archive_entry_type_t type;

/**
 * @brief The file name of the target (for a hard link or symbolic link).
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *targetFileName;








|

|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
 * @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;

Modified src/OFMutableTarArchiveEntry.m from [1fdec748d1] to [bfb188d370].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
- (void)setModificationDate: (OFDate *)modificationDate
{
	OFDate *old = _modificationDate;
	_modificationDate = [modificationDate retain];
	[old release];
}

- (void)setType: (of_tar_archive_entry_type_t)type
{
	_type = type;
}

- (void)setTargetFileName: (OFString *)targetFileName
{
	OFString *old = _targetFileName;







|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
- (void)setModificationDate: (OFDate *)modificationDate
{
	OFDate *old = _modificationDate;
	_modificationDate = [modificationDate retain];
	[old release];
}

- (void)setType: (OFTarArchiveEntryType)type
{
	_type = type;
}

- (void)setTargetFileName: (OFString *)targetFileName
{
	OFString *old = _targetFileName;

Modified src/OFMutableTriple.h from [9542203867] to [9e67514e20].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutableTriple.m from [af3517301d] to [a54e7551cb].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFMutableURL.h from [781d0b7833] to [28e4adf533].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/**
 * @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.







|
|







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
 * @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.
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
 * @param isDirectory Whether the path is a directory, in which case a slash is
 *		      appened if there is no slash yet
 */
- (void)appendPathComponent: (OFString *)component
		isDirectory: (bool)isDirectory;

/**
 * @brief Resolves relative sub paths.
 */
- (void)standardizePath;

/**
 * @brief Converts the mutable URL to an immutable URL.
 */
- (void)makeImmutable;
@end

OF_ASSUME_NONNULL_END







|










173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
 * @param isDirectory Whether the path is a directory, in which case a slash is
 *		      appened if there is no slash yet
 */
- (void)appendPathComponent: (OFString *)component
		isDirectory: (bool)isDirectory;

/**
 * @brief Resolves relative subpaths.
 */
- (void)standardizePath;

/**
 * @brief Converts the mutable URL to an immutable URL.
 */
- (void)makeImmutable;
@end

OF_ASSUME_NONNULL_END

Modified src/OFMutableURL.m from [596cab8221] to [ce9685e8ce].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# import "OFFileManager.h"
#endif
#import "OFNumber.h"
#import "OFString.h"

#import "OFInvalidFormatException.h"

extern void of_url_verify_escaped(OFString *, OFCharacterSet *);

@implementation OFMutableURL
@dynamic scheme, URLEncodedScheme, host, URLEncodedHost, port, user;
@dynamic URLEncodedUser, password, URLEncodedPassword, path, URLEncodedPath;
@dynamic pathComponents, query, URLEncodedQuery, queryDictionary, fragment;
@dynamic URLEncodedFragment;

+ (instancetype)URL







<
<







22
23
24
25
26
27
28


29
30
31
32
33
34
35
# import "OFFileManager.h"
#endif
#import "OFNumber.h"
#import "OFString.h"

#import "OFInvalidFormatException.h"



@implementation OFMutableURL
@dynamic scheme, URLEncodedScheme, host, URLEncodedHost, port, user;
@dynamic URLEncodedUser, password, URLEncodedPassword, path, URLEncodedPath;
@dynamic pathComponents, query, URLEncodedQuery, queryDictionary, fragment;
@dynamic URLEncodedFragment;

+ (instancetype)URL
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
}

- (void)setURLEncodedScheme: (OFString *)URLEncodedScheme
{
	OFString *old;

	if (URLEncodedScheme != nil)
		of_url_verify_escaped(URLEncodedScheme,
		    [OFCharacterSet URLSchemeAllowedCharacterSet]);

	old = _URLEncodedScheme;
	_URLEncodedScheme = [URLEncodedScheme copy];
	[old release];
}

- (void)setHost: (OFString *)host
{
	void *pool = objc_autoreleasePoolPush();
	OFString *old = _URLEncodedHost;

	if (of_url_is_ipv6_host(host))
		_URLEncodedHost = [[OFString alloc]
		    initWithFormat: @"[%@]", host];
	else
		_URLEncodedHost = [[host
		    stringByURLEncodingWithAllowedCharacters:
		    [OFCharacterSet URLHostAllowedCharacterSet]] copy];

	[old release];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedHost: (OFString *)URLEncodedHost
{
	OFString *old;

	if ([URLEncodedHost hasPrefix: @"["] &&
	    [URLEncodedHost hasSuffix: @"]"]) {
		if (!of_url_is_ipv6_host([URLEncodedHost substringWithRange:
		    of_range(1, URLEncodedHost.length - 2)]))
			@throw [OFInvalidFormatException exception];
	} else if (URLEncodedHost != nil)
		of_url_verify_escaped(URLEncodedHost,
		    [OFCharacterSet URLHostAllowedCharacterSet]);

	old = _URLEncodedHost;
	_URLEncodedHost = [URLEncodedHost copy];
	[old release];
}








|












|


















|
|


|







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
}

- (void)setURLEncodedScheme: (OFString *)URLEncodedScheme
{
	OFString *old;

	if (URLEncodedScheme != nil)
		OFURLVerifyIsEscaped(URLEncodedScheme,
		    [OFCharacterSet URLSchemeAllowedCharacterSet]);

	old = _URLEncodedScheme;
	_URLEncodedScheme = [URLEncodedScheme copy];
	[old release];
}

- (void)setHost: (OFString *)host
{
	void *pool = objc_autoreleasePoolPush();
	OFString *old = _URLEncodedHost;

	if (OFURLIsIPv6Host(host))
		_URLEncodedHost = [[OFString alloc]
		    initWithFormat: @"[%@]", host];
	else
		_URLEncodedHost = [[host
		    stringByURLEncodingWithAllowedCharacters:
		    [OFCharacterSet URLHostAllowedCharacterSet]] copy];

	[old release];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedHost: (OFString *)URLEncodedHost
{
	OFString *old;

	if ([URLEncodedHost hasPrefix: @"["] &&
	    [URLEncodedHost hasSuffix: @"]"]) {
		if (!OFURLIsIPv6Host([URLEncodedHost substringWithRange:
		    OFRangeMake(1, URLEncodedHost.length - 2)]))
			@throw [OFInvalidFormatException exception];
	} else if (URLEncodedHost != nil)
		OFURLVerifyIsEscaped(URLEncodedHost,
		    [OFCharacterSet URLHostAllowedCharacterSet]);

	old = _URLEncodedHost;
	_URLEncodedHost = [URLEncodedHost copy];
	[old release];
}

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
}

- (void)setURLEncodedUser: (OFString *)URLEncodedUser
{
	OFString *old;

	if (URLEncodedUser != nil)
		of_url_verify_escaped(URLEncodedUser,
		    [OFCharacterSet URLUserAllowedCharacterSet]);

	old = _URLEncodedUser;
	_URLEncodedUser = [URLEncodedUser copy];
	[old release];
}








|







120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
}

- (void)setURLEncodedUser: (OFString *)URLEncodedUser
{
	OFString *old;

	if (URLEncodedUser != nil)
		OFURLVerifyIsEscaped(URLEncodedUser,
		    [OFCharacterSet URLUserAllowedCharacterSet]);

	old = _URLEncodedUser;
	_URLEncodedUser = [URLEncodedUser copy];
	[old release];
}

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
}

- (void)setURLEncodedPassword: (OFString *)URLEncodedPassword
{
	OFString *old;

	if (URLEncodedPassword != nil)
		of_url_verify_escaped(URLEncodedPassword,
		    [OFCharacterSet URLPasswordAllowedCharacterSet]);

	old = _URLEncodedPassword;
	_URLEncodedPassword = [URLEncodedPassword copy];
	[old release];
}








|







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
}

- (void)setURLEncodedPassword: (OFString *)URLEncodedPassword
{
	OFString *old;

	if (URLEncodedPassword != nil)
		OFURLVerifyIsEscaped(URLEncodedPassword,
		    [OFCharacterSet URLPasswordAllowedCharacterSet]);

	old = _URLEncodedPassword;
	_URLEncodedPassword = [URLEncodedPassword copy];
	[old release];
}

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

- (void)setURLEncodedPath: (OFString *)URLEncodedPath
{
	OFString *old;

	if (URLEncodedPath != nil)
		of_url_verify_escaped(URLEncodedPath,
		    [OFCharacterSet URLPathAllowedCharacterSet]);

	old = _URLEncodedPath;
	_URLEncodedPath = [URLEncodedPath copy];
	[old release];
}








|







173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
}

- (void)setURLEncodedPath: (OFString *)URLEncodedPath
{
	OFString *old;

	if (URLEncodedPath != nil)
		OFURLVerifyIsEscaped(URLEncodedPath,
		    [OFCharacterSet URLPathAllowedCharacterSet]);

	old = _URLEncodedPath;
	_URLEncodedPath = [URLEncodedPath copy];
	[old release];
}

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
}

- (void)setURLEncodedQuery: (OFString *)URLEncodedQuery
{
	OFString *old;

	if (URLEncodedQuery != nil)
		of_url_verify_escaped(URLEncodedQuery,
		    [OFCharacterSet URLQueryAllowedCharacterSet]);

	old = _URLEncodedQuery;
	_URLEncodedQuery = [URLEncodedQuery copy];
	[old release];
}








|







219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
}

- (void)setURLEncodedQuery: (OFString *)URLEncodedQuery
{
	OFString *old;

	if (URLEncodedQuery != nil)
		OFURLVerifyIsEscaped(URLEncodedQuery,
		    [OFCharacterSet URLQueryAllowedCharacterSet]);

	old = _URLEncodedQuery;
	_URLEncodedQuery = [URLEncodedQuery copy];
	[old release];
}

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
}

- (void)setURLEncodedFragment: (OFString *)URLEncodedFragment
{
	OFString *old;

	if (URLEncodedFragment != nil)
		of_url_verify_escaped(URLEncodedFragment,
		    [OFCharacterSet URLFragmentAllowedCharacterSet]);

	old = _URLEncodedFragment;
	_URLEncodedFragment = [URLEncodedFragment copy];
	[old release];
}

- (id)copy
{
	OFMutableURL *copy = [self mutableCopy];

	[copy makeImmutable];

	return copy;
}

- (void)appendPathComponent: (OFString *)component
{
	[self appendPathComponent: component
		      isDirectory: false];

#ifdef OF_HAVE_FILES
	if ([_URLEncodedScheme isEqual: @"file"] &&
	    ![_URLEncodedPath hasSuffix: @"/"] &&
	    [[OFFileManager defaultManager] directoryExistsAtURL: self]) {
		void *pool = objc_autoreleasePoolPush();
		OFString *path = [_URLEncodedPath







|


















|
<







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
}

- (void)setURLEncodedFragment: (OFString *)URLEncodedFragment
{
	OFString *old;

	if (URLEncodedFragment != nil)
		OFURLVerifyIsEscaped(URLEncodedFragment,
		    [OFCharacterSet URLFragmentAllowedCharacterSet]);

	old = _URLEncodedFragment;
	_URLEncodedFragment = [URLEncodedFragment copy];
	[old release];
}

- (id)copy
{
	OFMutableURL *copy = [self mutableCopy];

	[copy makeImmutable];

	return copy;
}

- (void)appendPathComponent: (OFString *)component
{
	[self appendPathComponent: component isDirectory: false];


#ifdef OF_HAVE_FILES
	if ([_URLEncodedScheme isEqual: @"file"] &&
	    ![_URLEncodedPath hasSuffix: @"/"] &&
	    [[OFFileManager defaultManager] directoryExistsAtURL: self]) {
		void *pool = objc_autoreleasePoolPush();
		OFString *path = [_URLEncodedPath
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
				done = false;
				break;
			}

			if ([current isEqual: @".."] && parent != nil &&
			    ![parent isEqual: @".."]) {
				[array removeObjectsInRange:
				    of_range(i - 1, 2)];

				done = false;
				break;
			}
		}
	}

	[array insertObject: @""
		    atIndex: 0];
	if (endsWithEmpty)
		[array addObject: @""];

	path = [array componentsJoinedByString: @"/"];
	if (path.length == 0)
		path = @"/";








|







|
<







398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413

414
415
416
417
418
419
420
				done = false;
				break;
			}

			if ([current isEqual: @".."] && parent != nil &&
			    ![parent isEqual: @".."]) {
				[array removeObjectsInRange:
				    OFRangeMake(i - 1, 2)];

				done = false;
				break;
			}
		}
	}

	[array insertObject: @"" atIndex: 0];

	if (endsWithEmpty)
		[array addObject: @""];

	path = [array componentsJoinedByString: @"/"];
	if (path.length == 0)
		path = @"/";

Modified src/OFMutableUTF8String.h from [b6241cfb40] to [681d1d0ba4].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFMutableString.h"
#import "OFUTF8String.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFMutableUTF8String: OFMutableString
{
	struct of_string_utf8_ivars *restrict _s;
	struct of_string_utf8_ivars _storage;
}
@end

OF_ASSUME_NONNULL_END

<
<
|




















|
|




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


 * 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 "OFMutableString.h"
#import "OFUTF8String.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFMutableUTF8String: OFMutableString
{
	struct OFUTF8StringIvars *restrict _s;
	struct OFUTF8StringIvars _storage;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFMutableUTF8String.m from [cb57991be9] to [e069c57766].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#import "OFMutableUTF8String.h"

#import "OFString.h"
#import "OFUTF8String.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#import "of_asprintf.h"
#import "unicode.h"

@implementation OFMutableUTF8String
+ (void)initialize
{
	if (self == [OFMutableUTF8String class])
		[self inheritMethodsFromClass: [OFUTF8String class]];
}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
			    freeWhenDone: (bool)freeWhenDone
{
	@try {
		self = [self initWithUTF8String: UTF8String];
	} @finally {
		if (freeWhenDone)
			free(UTF8String);
	}

	return self;
}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
				  length: (size_t)UTF8StringLength
			    freeWhenDone: (bool)freeWhenDone
{
	@try {
		self = [self initWithUTF8String: UTF8String
					 length: UTF8StringLength];
	} @finally {
		if (freeWhenDone)
			free(UTF8String);
	}

	return self;
}


- (void)of_convertWithWordStartTable: (const of_unichar_t *const[])startTable
		     wordMiddleTable: (const of_unichar_t *const[])middleTable
		  wordStartTableSize: (size_t)startTableSize
		 wordMiddleTableSize: (size_t)middleTableSize OF_DIRECT
{
	of_unichar_t *unicodeString;
	size_t unicodeLen, newCStringLength;
	size_t i, j;
	char *newCString;
	bool isStart = true;

	if (!_s->isUTF8) {
		uint8_t t;
		const of_unichar_t *const *table;

		assert(startTableSize >= 1 && middleTableSize >= 1);

		_s->hashed = false;

		for (i = 0; i < _s->cStringLength; i++) {
			if (isStart)
				table = startTable;
			else
				table = middleTable;

			isStart = of_ascii_isspace(_s->cString[i]);

			if ((t = table[0][(uint8_t)_s->cString[i]]) != 0)
				_s->cString[i] = t;
		}

		return;
	}

	unicodeLen = self.length;
	unicodeString = [self allocMemoryWithSize: sizeof(of_unichar_t)
					    count: unicodeLen];

	i = j = 0;
	newCStringLength = 0;

	while (i < _s->cStringLength) {
		const of_unichar_t *const *table;
		size_t tableSize;
		of_unichar_t c;
		ssize_t cLen;

		if (isStart) {
			table = startTable;
			tableSize = middleTableSize;
		} else {
			table = middleTable;
			tableSize = middleTableSize;
		}

		cLen = of_string_utf8_decode(_s->cString + i,
		    _s->cStringLength - i, &c);

		if (cLen <= 0 || c > 0x10FFFF) {
			[self freeMemory: unicodeString];
			@throw [OFInvalidEncodingException exception];
		}

		isStart = of_ascii_isspace(c);

		if (c >> 8 < tableSize) {
			of_unichar_t tc = table[c >> 8][c & 0xFF];

			if (tc)
				c = tc;
		}
		unicodeString[j++] = c;

		if (c < 0x80)
			newCStringLength++;
		else if (c < 0x800)
			newCStringLength += 2;
		else if (c < 0x10000)
			newCStringLength += 3;
		else if (c < 0x110000)
			newCStringLength += 4;
		else {
			[self freeMemory: unicodeString];
			@throw [OFInvalidEncodingException exception];
		}

		i += cLen;
	}

	@try {
		newCString = [self allocMemoryWithSize: newCStringLength + 1];
	} @catch (id e) {
		[self freeMemory: unicodeString];
		@throw e;
	}

	j = 0;

	for (i = 0; i < unicodeLen; i++) {
		size_t d;

		if ((d = of_string_utf8_encode(unicodeString[i],
		    newCString + j)) == 0) {
			[self freeMemory: unicodeString];
			[self freeMemory: newCString];
			@throw [OFInvalidEncodingException exception];
		}
		j += d;
	}

	assert(j == newCStringLength);
	newCString[j] = 0;
	[self freeMemory: unicodeString];

	[self freeMemory: _s->cString];
	_s->hashed = false;
	_s->cString = newCString;
	_s->cStringLength = newCStringLength;

	/*
	 * Even though cStringLength can change, length cannot, therefore no
	 * need to change it.
	 */
}


- (void)setCharacter: (of_unichar_t)character
	     atIndex: (size_t)idx
{
	char buffer[4];
	of_unichar_t c;
	size_t lenNew;
	ssize_t lenOld;

	if (_s->isUTF8)
		idx = of_string_utf8_get_position(_s->cString, idx,
		    _s->cStringLength);

	if (idx >= _s->cStringLength)
		@throw [OFOutOfRangeException exception];

	/* Shortcut if old and new character both are ASCII */
	if (character < 0x80 && !(_s->cString[idx] & 0x80)) {
		_s->hashed = false;
		_s->cString[idx] = character;
		return;
	}

	if ((lenNew = of_string_utf8_encode(character, buffer)) == 0)
		@throw [OFInvalidEncodingException exception];

	if ((lenOld = of_string_utf8_decode(_s->cString + idx,
	    _s->cStringLength - idx, &c)) <= 0)
		@throw [OFInvalidEncodingException exception];

	_s->hashed = false;

	if (lenNew == (size_t)lenOld)
		memcpy(_s->cString + idx, buffer, lenNew);
	else if (lenNew > (size_t)lenOld) {
		_s->cString = [self resizeMemory: _s->cString
					    size: _s->cStringLength -
						  lenOld + lenNew + 1];

		memmove(_s->cString + idx + lenNew, _s->cString + idx + lenOld,
		    _s->cStringLength - idx - lenOld);
		memcpy(_s->cString + idx, buffer, lenNew);

		_s->cStringLength -= lenOld;
		_s->cStringLength += lenNew;







>









<












<
|
|
|
|
<








<
|
|
<
|
|
<




>
|
|

|

|







|



|







|









|
<





|

|










|



|



|


|















|







|

|








|

|
|







|

|
|








>

|
<


|




|







|




|


|



|




<
|
|







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

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#import "OFMutableUTF8String.h"
#import "OFASPrintF.h"
#import "OFString.h"
#import "OFUTF8String.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"


#import "unicode.h"

@implementation OFMutableUTF8String
+ (void)initialize
{
	if (self == [OFMutableUTF8String class])
		[self inheritMethodsFromClass: [OFUTF8String class]];
}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
			    freeWhenDone: (bool)freeWhenDone
{

	self = [self initWithUTF8String: UTF8String];

	if (freeWhenDone)
		OFFreeMemory(UTF8String);


	return self;
}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
				  length: (size_t)UTF8StringLength
			    freeWhenDone: (bool)freeWhenDone
{

	self = [self initWithUTF8String: UTF8String length: UTF8StringLength];


	if (freeWhenDone)
		OFFreeMemory(UTF8String);


	return self;
}

#ifdef OF_HAVE_UNICODE_TABLES
- (void)of_convertWithWordStartTable: (const OFUnichar *const [])startTable
		     wordMiddleTable: (const OFUnichar *const [])middleTable
		  wordStartTableSize: (size_t)startTableSize
		 wordMiddleTableSize: (size_t)middleTableSize
{
	OFUnichar *unicodeString;
	size_t unicodeLen, newCStringLength;
	size_t i, j;
	char *newCString;
	bool isStart = true;

	if (!_s->isUTF8) {
		uint8_t t;
		const OFUnichar *const *table;

		assert(startTableSize >= 1 && middleTableSize >= 1);

		_s->hasHash = false;

		for (i = 0; i < _s->cStringLength; i++) {
			if (isStart)
				table = startTable;
			else
				table = middleTable;

			isStart = OFASCIIIsSpace(_s->cString[i]);

			if ((t = table[0][(uint8_t)_s->cString[i]]) != 0)
				_s->cString[i] = t;
		}

		return;
	}

	unicodeLen = self.length;
	unicodeString = OFAllocMemory(unicodeLen, sizeof(OFUnichar));


	i = j = 0;
	newCStringLength = 0;

	while (i < _s->cStringLength) {
		const OFUnichar *const *table;
		size_t tableSize;
		OFUnichar c;
		ssize_t cLen;

		if (isStart) {
			table = startTable;
			tableSize = middleTableSize;
		} else {
			table = middleTable;
			tableSize = middleTableSize;
		}

		cLen = OFUTF8StringDecode(_s->cString + i,
		    _s->cStringLength - i, &c);

		if (cLen <= 0 || c > 0x10FFFF) {
			OFFreeMemory(unicodeString);
			@throw [OFInvalidEncodingException exception];
		}

		isStart = OFASCIIIsSpace(c);

		if (c >> 8 < tableSize) {
			OFUnichar tc = table[c >> 8][c & 0xFF];

			if (tc)
				c = tc;
		}
		unicodeString[j++] = c;

		if (c < 0x80)
			newCStringLength++;
		else if (c < 0x800)
			newCStringLength += 2;
		else if (c < 0x10000)
			newCStringLength += 3;
		else if (c < 0x110000)
			newCStringLength += 4;
		else {
			OFFreeMemory(unicodeString);
			@throw [OFInvalidEncodingException exception];
		}

		i += cLen;
	}

	@try {
		newCString = OFAllocMemory(newCStringLength + 1, 1);
	} @catch (id e) {
		OFFreeMemory(unicodeString);
		@throw e;
	}

	j = 0;

	for (i = 0; i < unicodeLen; i++) {
		size_t d;

		if ((d = OFUTF8StringEncode(unicodeString[i],
		    newCString + j)) == 0) {
			OFFreeMemory(unicodeString);
			OFFreeMemory(newCString);
			@throw [OFInvalidEncodingException exception];
		}
		j += d;
	}

	assert(j == newCStringLength);
	newCString[j] = 0;
	OFFreeMemory(unicodeString);

	OFFreeMemory(_s->cString);
	_s->hasHash = false;
	_s->cString = newCString;
	_s->cStringLength = newCStringLength;

	/*
	 * Even though cStringLength can change, length cannot, therefore no
	 * need to change it.
	 */
}
#endif

- (void)setCharacter: (OFUnichar)character atIndex: (size_t)idx

{
	char buffer[4];
	OFUnichar c;
	size_t lenNew;
	ssize_t lenOld;

	if (_s->isUTF8)
		idx = OFUTF8StringIndexToPosition(_s->cString, idx,
		    _s->cStringLength);

	if (idx >= _s->cStringLength)
		@throw [OFOutOfRangeException exception];

	/* Shortcut if old and new character both are ASCII */
	if (character < 0x80 && !(_s->cString[idx] & 0x80)) {
		_s->hasHash = false;
		_s->cString[idx] = character;
		return;
	}

	if ((lenNew = OFUTF8StringEncode(character, buffer)) == 0)
		@throw [OFInvalidEncodingException exception];

	if ((lenOld = OFUTF8StringDecode(_s->cString + idx,
	    _s->cStringLength - idx, &c)) <= 0)
		@throw [OFInvalidEncodingException exception];

	_s->hasHash = false;

	if (lenNew == (size_t)lenOld)
		memcpy(_s->cString + idx, buffer, lenNew);
	else if (lenNew > (size_t)lenOld) {

		_s->cString = OFResizeMemory(_s->cString,
		    _s->cStringLength - lenOld + lenNew + 1, 1);

		memmove(_s->cString + idx + lenNew, _s->cString + idx + lenOld,
		    _s->cStringLength - idx - lenOld);
		memcpy(_s->cString + idx, buffer, lenNew);

		_s->cStringLength -= lenOld;
		_s->cStringLength += lenNew;
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
		_s->cStringLength += lenNew;
		_s->cString[_s->cStringLength] = '\0';

		if (character >= 0x80)
			_s->isUTF8 = true;

		@try {
			_s->cString = [self
			    resizeMemory: _s->cString
				    size: _s->cStringLength + 1];
		} @catch (OFOutOfMemoryException *e) {
			/* We don't really care, as we only made it smaller */
		}
	}
}

- (void)appendUTF8String: (const char *)UTF8String
{
	size_t UTF8StringLength = strlen(UTF8String);
	size_t length;

	if (UTF8StringLength >= 3 &&
	    memcmp(UTF8String, "\xEF\xBB\xBF", 3) == 0) {
		UTF8String += 3;
		UTF8StringLength -= 3;
	}

	switch (of_string_utf8_check(UTF8String, UTF8StringLength, &length)) {
	case 1:
		_s->isUTF8 = true;
		break;
	case -1:
		@throw [OFInvalidEncodingException exception];
	}

	_s->hashed = false;
	_s->cString = [self resizeMemory: _s->cString
				    size: _s->cStringLength +
					  UTF8StringLength + 1];
	memcpy(_s->cString + _s->cStringLength, UTF8String,
	    UTF8StringLength + 1);

	_s->cStringLength += UTF8StringLength;
	_s->length += length;
}

- (void)appendUTF8String: (const char *)UTF8String
		  length: (size_t)UTF8StringLength
{
	size_t length;

	if (UTF8StringLength >= 3 &&
	    memcmp(UTF8String, "\xEF\xBB\xBF", 3) == 0) {
		UTF8String += 3;
		UTF8StringLength -= 3;
	}

	switch (of_string_utf8_check(UTF8String, UTF8StringLength, &length)) {
	case 1:
		_s->isUTF8 = true;
		break;
	case -1:
		@throw [OFInvalidEncodingException exception];
	}

	_s->hashed = false;
	_s->cString = [self resizeMemory: _s->cString
				    size: _s->cStringLength +
					  UTF8StringLength + 1];
	memcpy(_s->cString + _s->cStringLength, UTF8String, UTF8StringLength);

	_s->cStringLength += UTF8StringLength;
	_s->length += length;

	_s->cString[_s->cStringLength] = 0;
}

- (void)appendCString: (const char *)cString
	     encoding: (of_string_encoding_t)encoding
{
	[self appendCString: cString
		   encoding: encoding
		     length: strlen(cString)];
}

- (void)appendCString: (const char *)cString
	     encoding: (of_string_encoding_t)encoding
	       length: (size_t)cStringLength
{
	if (encoding == OF_STRING_ENCODING_UTF_8)
		[self appendUTF8String: cString
				length: cStringLength];
	else {
		void *pool = objc_autoreleasePoolPush();

		[self appendString:
		    [OFString stringWithCString: cString
				       encoding: encoding
					 length: cStringLength]];







|
<
|

















|







|
<
|
|


















|







|
<
|
|









|







|


|
|
<







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
		_s->cStringLength += lenNew;
		_s->cString[_s->cStringLength] = '\0';

		if (character >= 0x80)
			_s->isUTF8 = true;

		@try {
			_s->cString = OFResizeMemory(_s->cString,

			    _s->cStringLength + 1, 1);
		} @catch (OFOutOfMemoryException *e) {
			/* We don't really care, as we only made it smaller */
		}
	}
}

- (void)appendUTF8String: (const char *)UTF8String
{
	size_t UTF8StringLength = strlen(UTF8String);
	size_t length;

	if (UTF8StringLength >= 3 &&
	    memcmp(UTF8String, "\xEF\xBB\xBF", 3) == 0) {
		UTF8String += 3;
		UTF8StringLength -= 3;
	}

	switch (OFUTF8StringCheck(UTF8String, UTF8StringLength, &length)) {
	case 1:
		_s->isUTF8 = true;
		break;
	case -1:
		@throw [OFInvalidEncodingException exception];
	}

	_s->hasHash = false;

	_s->cString = OFResizeMemory(_s->cString,
	    _s->cStringLength + UTF8StringLength + 1, 1);
	memcpy(_s->cString + _s->cStringLength, UTF8String,
	    UTF8StringLength + 1);

	_s->cStringLength += UTF8StringLength;
	_s->length += length;
}

- (void)appendUTF8String: (const char *)UTF8String
		  length: (size_t)UTF8StringLength
{
	size_t length;

	if (UTF8StringLength >= 3 &&
	    memcmp(UTF8String, "\xEF\xBB\xBF", 3) == 0) {
		UTF8String += 3;
		UTF8StringLength -= 3;
	}

	switch (OFUTF8StringCheck(UTF8String, UTF8StringLength, &length)) {
	case 1:
		_s->isUTF8 = true;
		break;
	case -1:
		@throw [OFInvalidEncodingException exception];
	}

	_s->hasHash = false;

	_s->cString = OFResizeMemory(_s->cString,
	    _s->cStringLength + UTF8StringLength + 1, 1);
	memcpy(_s->cString + _s->cStringLength, UTF8String, UTF8StringLength);

	_s->cStringLength += UTF8StringLength;
	_s->length += length;

	_s->cString[_s->cStringLength] = 0;
}

- (void)appendCString: (const char *)cString
	     encoding: (OFStringEncoding)encoding
{
	[self appendCString: cString
		   encoding: encoding
		     length: strlen(cString)];
}

- (void)appendCString: (const char *)cString
	     encoding: (OFStringEncoding)encoding
	       length: (size_t)cStringLength
{
	if (encoding == OFStringEncodingUTF8)
		[self appendUTF8String: cString length: cStringLength];

	else {
		void *pool = objc_autoreleasePoolPush();

		[self appendString:
		    [OFString stringWithCString: cString
				       encoding: encoding
					 length: cStringLength]];
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
	size_t UTF8StringLength;

	if (string == nil)
		@throw [OFInvalidArgumentException exception];

	UTF8StringLength = string.UTF8StringLength;

	_s->hashed = false;
	_s->cString = [self resizeMemory: _s->cString
				    size: _s->cStringLength +
					  UTF8StringLength + 1];
	memcpy(_s->cString + _s->cStringLength, string.UTF8String,
	    UTF8StringLength);

	_s->cStringLength += UTF8StringLength;
	_s->length += string.length;

	_s->cString[_s->cStringLength] = 0;

	if ([string isKindOfClass: [OFUTF8String class]] ||
	    [string isKindOfClass: [OFMutableUTF8String class]]) {
		if (((OFMutableUTF8String *)string)->_s->isUTF8)
			_s->isUTF8 = true;
	} else
		_s->isUTF8 = true;
}

- (void)appendCharacters: (const of_unichar_t *)characters
		  length: (size_t)length
{
	char *tmp;

	tmp = [self allocMemoryWithSize: (length * 4) + 1];
	@try {
		size_t j = 0;
		bool isUTF8 = false;

		for (size_t i = 0; i < length; i++) {
			size_t len = of_string_utf8_encode(characters[i],
			    tmp + j);

			if (len == 0)
				@throw [OFInvalidEncodingException exception];

			if (len > 1)
				isUTF8 = true;

			j += len;
		}

		tmp[j] = '\0';

		_s->hashed = false;
		_s->cString = [self resizeMemory: _s->cString
					    size: _s->cStringLength + j + 1];
		memcpy(_s->cString + _s->cStringLength, tmp, j + 1);

		_s->cStringLength += j;
		_s->length += length;

		if (isUTF8)
			_s->isUTF8 = true;
	} @finally {
		[self freeMemory: tmp];
	}
}

- (void)appendFormat: (OFConstantString *)format
	   arguments: (va_list)arguments
{
	char *UTF8String;
	int UTF8StringLength;

	if (format == nil)
		@throw [OFInvalidArgumentException exception];

	if ((UTF8StringLength = of_vasprintf(&UTF8String, format.UTF8String,
	    arguments)) == -1)
		@throw [OFInvalidFormatException exception];

	@try {
		[self appendUTF8String: UTF8String
				length: UTF8StringLength];
	} @finally {
		free(UTF8String);
	}
}

- (void)reverse
{
	size_t i, j;

	_s->hashed = false;

	/* We reverse all bytes and restore UTF-8 later, if necessary */
	for (i = 0, j = _s->cStringLength - 1; i < _s->cStringLength / 2;
	    i++, j--) {
		_s->cString[i] ^= _s->cString[j];
		_s->cString[j] ^= _s->cString[i];
		_s->cString[i] ^= _s->cString[j];







|
<
|
|
















|
<

|

<





|
<












|
|
|








|



|
<







|




|
<









|







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
	size_t UTF8StringLength;

	if (string == nil)
		@throw [OFInvalidArgumentException exception];

	UTF8StringLength = string.UTF8StringLength;

	_s->hasHash = false;

	_s->cString = OFResizeMemory(_s->cString,
	    _s->cStringLength + UTF8StringLength + 1, 1);
	memcpy(_s->cString + _s->cStringLength, string.UTF8String,
	    UTF8StringLength);

	_s->cStringLength += UTF8StringLength;
	_s->length += string.length;

	_s->cString[_s->cStringLength] = 0;

	if ([string isKindOfClass: [OFUTF8String class]] ||
	    [string isKindOfClass: [OFMutableUTF8String class]]) {
		if (((OFMutableUTF8String *)string)->_s->isUTF8)
			_s->isUTF8 = true;
	} else
		_s->isUTF8 = true;
}

- (void)appendCharacters: (const OFUnichar *)characters length: (size_t)length

{
	char *tmp = OFAllocMemory((length * 4) + 1, 1);


	@try {
		size_t j = 0;
		bool isUTF8 = false;

		for (size_t i = 0; i < length; i++) {
			size_t len = OFUTF8StringEncode(characters[i], tmp + j);


			if (len == 0)
				@throw [OFInvalidEncodingException exception];

			if (len > 1)
				isUTF8 = true;

			j += len;
		}

		tmp[j] = '\0';

		_s->hasHash = false;
		_s->cString = OFResizeMemory(_s->cString,
		    _s->cStringLength + j + 1, 1);
		memcpy(_s->cString + _s->cStringLength, tmp, j + 1);

		_s->cStringLength += j;
		_s->length += length;

		if (isUTF8)
			_s->isUTF8 = true;
	} @finally {
		OFFreeMemory(tmp);
	}
}

- (void)appendFormat: (OFConstantString *)format arguments: (va_list)arguments

{
	char *UTF8String;
	int UTF8StringLength;

	if (format == nil)
		@throw [OFInvalidArgumentException exception];

	if ((UTF8StringLength = OFVASPrintF(&UTF8String, format.UTF8String,
	    arguments)) == -1)
		@throw [OFInvalidFormatException exception];

	@try {
		[self appendUTF8String: UTF8String length: UTF8StringLength];

	} @finally {
		free(UTF8String);
	}
}

- (void)reverse
{
	size_t i, j;

	_s->hasHash = false;

	/* We reverse all bytes and restore UTF-8 later, if necessary */
	for (i = 0, j = _s->cStringLength - 1; i < _s->cStringLength / 2;
	    i++, j--) {
		_s->cString[i] ^= _s->cString[j];
		_s->cString[j] ^= _s->cString[i];
		_s->cString[i] ^= _s->cString[j];
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
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
		}

		/* UTF-8 does not allow more than 4 bytes per character */
		@throw [OFInvalidEncodingException exception];
	}
}

- (void)insertString: (OFString *)string
	     atIndex: (size_t)idx
{
	size_t newCStringLength;

	if (idx > _s->length)
		@throw [OFOutOfRangeException exception];

	if (_s->isUTF8)
		idx = of_string_utf8_get_position(_s->cString, idx,
		    _s->cStringLength);

	newCStringLength = _s->cStringLength + string.UTF8StringLength;
	_s->hashed = false;
	_s->cString = [self resizeMemory: _s->cString
				    size: newCStringLength + 1];

	memmove(_s->cString + idx + string.UTF8StringLength,
	    _s->cString + idx, _s->cStringLength - idx);
	memcpy(_s->cString + idx, string.UTF8String,
	    string.UTF8StringLength);
	_s->cString[newCStringLength] = '\0';

	_s->cStringLength = newCStringLength;
	_s->length += string.length;

	if ([string isKindOfClass: [OFUTF8String class]] ||
	    [string isKindOfClass: [OFMutableUTF8String class]]) {
		if (((OFMutableUTF8String *)string)->_s->isUTF8)
			_s->isUTF8 = true;
	} else
		_s->isUTF8 = true;
}

- (void)deleteCharactersInRange: (of_range_t)range
{
	size_t start = range.location;
	size_t end = range.location + range.length;

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

	if (_s->isUTF8) {
		start = of_string_utf8_get_position(_s->cString, start,
		    _s->cStringLength);
		end = of_string_utf8_get_position(_s->cString, end,
		    _s->cStringLength);
	}

	memmove(_s->cString + start, _s->cString + end,
	    _s->cStringLength - end);
	_s->hashed = false;
	_s->length -= range.length;
	_s->cStringLength -= end - start;
	_s->cString[_s->cStringLength] = 0;

	@try {
		_s->cString = [self resizeMemory: _s->cString
					    size: _s->cStringLength + 1];

	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)replaceCharactersInRange: (of_range_t)range
		      withString: (OFString *)replacement
{
	size_t start = range.location;
	size_t end = range.location + range.length;
	size_t newCStringLength, newLength;

	if (replacement == nil)
		@throw [OFInvalidArgumentException exception];

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

	newLength = _s->length - range.length + replacement.length;

	if (_s->isUTF8) {
		start = of_string_utf8_get_position(_s->cString, start,
		    _s->cStringLength);
		end = of_string_utf8_get_position(_s->cString, end,
		    _s->cStringLength);
	}

	newCStringLength = _s->cStringLength - (end - start) +
	    replacement.UTF8StringLength;
	_s->hashed = false;

	/*
	 * If the new string is bigger, we need to resize it first so we can
	 * memmove() the rest of the string to the end.
	 *
	 * We must not resize the string if the new string is smaller, because
	 * then we can't memmove() the rest of the string forward as the rest is
	 * lost due to the resize!
	 */
	if (newCStringLength > _s->cStringLength)
		_s->cString = [self resizeMemory: _s->cString
					    size: newCStringLength + 1];


	memmove(_s->cString + start + replacement.UTF8StringLength,
	    _s->cString + end, _s->cStringLength - end);
	memcpy(_s->cString + start, replacement.UTF8String,
	    replacement.UTF8StringLength);
	_s->cString[newCStringLength] = '\0';

	/*
	 * If the new string is smaller, we can safely resize it now as we're
	 * done with memmove().
	 */
	if (newCStringLength < _s->cStringLength)
		_s->cString = [self resizeMemory: _s->cString
					    size: newCStringLength + 1];


	_s->cStringLength = newCStringLength;
	_s->length = newLength;

	if ([replacement isKindOfClass: [OFUTF8String class]] ||
	    [replacement isKindOfClass: [OFMutableUTF8String class]]) {
		if (((OFMutableUTF8String *)replacement)->_s->isUTF8)
			_s->isUTF8 = true;
	} else
		_s->isUTF8 = true;
}

- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
			   options: (int)options
			     range: (of_range_t)range
{
	const char *searchString = string.UTF8String;
	const char *replacementString = replacement.UTF8String;
	size_t searchLength = string.UTF8StringLength;
	size_t replacementLength = replacement.UTF8StringLength;
	size_t last, newCStringLength, newLength;
	char *newCString;

	if (string == nil || replacement == nil)
		@throw [OFInvalidArgumentException exception];

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

	if (_s->isUTF8) {
		range.location = of_string_utf8_get_position(_s->cString,
		    range.location, _s->cStringLength);
		range.length = of_string_utf8_get_position(
		    _s->cString + range.location, range.length,
		    _s->cStringLength - range.location);
	}

	if (string.UTF8StringLength > range.length)
		return;

	newCString = NULL;
	newCStringLength = 0;
	newLength = _s->length;
	last = 0;

	for (size_t i = range.location; i <= range.length - searchLength; i++) {
		if (memcmp(_s->cString + i, searchString, searchLength) != 0)
			continue;

		@try {
			newCString = [self resizeMemory: newCString
						   size: newCStringLength +
							 i - last +
							 replacementLength + 1];

		} @catch (id e) {
			[self freeMemory: newCString];
			@throw e;
		}
		memcpy(newCString + newCStringLength, _s->cString + last,
		    i - last);
		memcpy(newCString + newCStringLength + i - last,
		    replacementString, replacementLength);

		newCStringLength += i - last + replacementLength;
		newLength = newLength - string.length + replacement.length;

		i += searchLength - 1;
		last = i + 1;
	}

	@try {
		newCString = [self resizeMemory: newCString
					   size: newCStringLength +
						 _s->cStringLength - last + 1];
	} @catch (id e) {
		[self freeMemory: newCString];
		@throw e;
	}
	memcpy(newCString + newCStringLength, _s->cString + last,
	    _s->cStringLength - last);
	newCStringLength += _s->cStringLength - last;
	newCString[newCStringLength] = 0;

	[self freeMemory: _s->cString];
	_s->hashed = false;
	_s->cString = newCString;
	_s->cStringLength = newCStringLength;
	_s->length = newLength;

	if ([replacement isKindOfClass: [OFUTF8String class]] ||
	    [replacement isKindOfClass: [OFMutableUTF8String class]]) {
		if (((OFMutableUTF8String *)replacement)->_s->isUTF8)
			_s->isUTF8 = true;
	} else
		_s->isUTF8 = true;
}

- (void)deleteLeadingWhitespaces
{
	size_t i;

	for (i = 0; i < _s->cStringLength; i++)
		if (!of_ascii_isspace(_s->cString[i]))
			break;

	_s->hashed = false;
	_s->cStringLength -= i;
	_s->length -= i;

	memmove(_s->cString, _s->cString + i, _s->cStringLength);
	_s->cString[_s->cStringLength] = '\0';

	@try {
		_s->cString = [self resizeMemory: _s->cString
					    size: _s->cStringLength + 1];

	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)deleteTrailingWhitespaces
{
	size_t d;
	char *p;

	_s->hashed = false;

	d = 0;
	for (p = _s->cString + _s->cStringLength - 1; p >= _s->cString; p--) {
		if (!of_ascii_isspace(*p))
			break;

		*p = '\0';
		d++;
	}

	_s->cStringLength -= d;
	_s->length -= d;

	@try {
		_s->cString = [self resizeMemory: _s->cString
					    size: _s->cStringLength + 1];

	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)deleteEnclosingWhitespaces
{
	size_t d, i;
	char *p;

	_s->hashed = false;

	d = 0;
	for (p = _s->cString + _s->cStringLength - 1; p >= _s->cString; p--) {
		if (!of_ascii_isspace(*p))
			break;

		*p = '\0';
		d++;
	}

	_s->cStringLength -= d;
	_s->length -= d;

	for (i = 0; i < _s->cStringLength; i++)
		if (!of_ascii_isspace(_s->cString[i]))
			break;

	_s->cStringLength -= i;
	_s->length -= i;

	memmove(_s->cString, _s->cString + i, _s->cStringLength);
	_s->cString[_s->cStringLength] = '\0';

	@try {
		_s->cString = [self resizeMemory: _s->cString
					    size: _s->cStringLength + 1];

	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)makeImmutable
{
	object_setClass(self, [OFUTF8String class]);
}
@end







|
<







|



|
<
|


















|








|

|





|





<
|
>





|















|

|





|










<
|
>












<
|
>















|
















|

|

















|
<
<
|
>

|















|
<
|

|







|
|

















|


|







<
|
>










|



|










<
|
>










|



|










|









<
|
>










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

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
		}

		/* UTF-8 does not allow more than 4 bytes per character */
		@throw [OFInvalidEncodingException exception];
	}
}

- (void)insertString: (OFString *)string atIndex: (size_t)idx

{
	size_t newCStringLength;

	if (idx > _s->length)
		@throw [OFOutOfRangeException exception];

	if (_s->isUTF8)
		idx = OFUTF8StringIndexToPosition(_s->cString, idx,
		    _s->cStringLength);

	newCStringLength = _s->cStringLength + string.UTF8StringLength;
	_s->hasHash = false;

	_s->cString = OFResizeMemory(_s->cString, newCStringLength + 1, 1);

	memmove(_s->cString + idx + string.UTF8StringLength,
	    _s->cString + idx, _s->cStringLength - idx);
	memcpy(_s->cString + idx, string.UTF8String,
	    string.UTF8StringLength);
	_s->cString[newCStringLength] = '\0';

	_s->cStringLength = newCStringLength;
	_s->length += string.length;

	if ([string isKindOfClass: [OFUTF8String class]] ||
	    [string isKindOfClass: [OFMutableUTF8String class]]) {
		if (((OFMutableUTF8String *)string)->_s->isUTF8)
			_s->isUTF8 = true;
	} else
		_s->isUTF8 = true;
}

- (void)deleteCharactersInRange: (OFRange)range
{
	size_t start = range.location;
	size_t end = range.location + range.length;

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

	if (_s->isUTF8) {
		start = OFUTF8StringIndexToPosition(_s->cString, start,
		    _s->cStringLength);
		end = OFUTF8StringIndexToPosition(_s->cString, end,
		    _s->cStringLength);
	}

	memmove(_s->cString + start, _s->cString + end,
	    _s->cStringLength - end);
	_s->hasHash = false;
	_s->length -= range.length;
	_s->cStringLength -= end - start;
	_s->cString[_s->cStringLength] = 0;

	@try {

		_s->cString = OFResizeMemory(_s->cString, _s->cStringLength + 1,
		    1);
	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)replaceCharactersInRange: (OFRange)range
		      withString: (OFString *)replacement
{
	size_t start = range.location;
	size_t end = range.location + range.length;
	size_t newCStringLength, newLength;

	if (replacement == nil)
		@throw [OFInvalidArgumentException exception];

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

	newLength = _s->length - range.length + replacement.length;

	if (_s->isUTF8) {
		start = OFUTF8StringIndexToPosition(_s->cString, start,
		    _s->cStringLength);
		end = OFUTF8StringIndexToPosition(_s->cString, end,
		    _s->cStringLength);
	}

	newCStringLength = _s->cStringLength - (end - start) +
	    replacement.UTF8StringLength;
	_s->hasHash = false;

	/*
	 * If the new string is bigger, we need to resize it first so we can
	 * memmove() the rest of the string to the end.
	 *
	 * We must not resize the string if the new string is smaller, because
	 * then we can't memmove() the rest of the string forward as the rest is
	 * lost due to the resize!
	 */
	if (newCStringLength > _s->cStringLength)

		_s->cString = OFResizeMemory(_s->cString, newCStringLength + 1,
		    1);

	memmove(_s->cString + start + replacement.UTF8StringLength,
	    _s->cString + end, _s->cStringLength - end);
	memcpy(_s->cString + start, replacement.UTF8String,
	    replacement.UTF8StringLength);
	_s->cString[newCStringLength] = '\0';

	/*
	 * If the new string is smaller, we can safely resize it now as we're
	 * done with memmove().
	 */
	if (newCStringLength < _s->cStringLength)

		_s->cString = OFResizeMemory(_s->cString, newCStringLength + 1,
		    1);

	_s->cStringLength = newCStringLength;
	_s->length = newLength;

	if ([replacement isKindOfClass: [OFUTF8String class]] ||
	    [replacement isKindOfClass: [OFMutableUTF8String class]]) {
		if (((OFMutableUTF8String *)replacement)->_s->isUTF8)
			_s->isUTF8 = true;
	} else
		_s->isUTF8 = true;
}

- (void)replaceOccurrencesOfString: (OFString *)string
			withString: (OFString *)replacement
			   options: (int)options
			     range: (OFRange)range
{
	const char *searchString = string.UTF8String;
	const char *replacementString = replacement.UTF8String;
	size_t searchLength = string.UTF8StringLength;
	size_t replacementLength = replacement.UTF8StringLength;
	size_t last, newCStringLength, newLength;
	char *newCString;

	if (string == nil || replacement == nil)
		@throw [OFInvalidArgumentException exception];

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

	if (_s->isUTF8) {
		range.location = OFUTF8StringIndexToPosition(_s->cString,
		    range.location, _s->cStringLength);
		range.length = OFUTF8StringIndexToPosition(
		    _s->cString + range.location, range.length,
		    _s->cStringLength - range.location);
	}

	if (string.UTF8StringLength > range.length)
		return;

	newCString = NULL;
	newCStringLength = 0;
	newLength = _s->length;
	last = 0;

	for (size_t i = range.location; i <= range.length - searchLength; i++) {
		if (memcmp(_s->cString + i, searchString, searchLength) != 0)
			continue;

		@try {
			newCString = OFResizeMemory(newCString,


			    newCStringLength + i - last + replacementLength + 1,
			    1);
		} @catch (id e) {
			OFFreeMemory(newCString);
			@throw e;
		}
		memcpy(newCString + newCStringLength, _s->cString + last,
		    i - last);
		memcpy(newCString + newCStringLength + i - last,
		    replacementString, replacementLength);

		newCStringLength += i - last + replacementLength;
		newLength = newLength - string.length + replacement.length;

		i += searchLength - 1;
		last = i + 1;
	}

	@try {
		newCString = OFResizeMemory(newCString,

		    newCStringLength + _s->cStringLength - last + 1, 1);
	} @catch (id e) {
		OFFreeMemory(newCString);
		@throw e;
	}
	memcpy(newCString + newCStringLength, _s->cString + last,
	    _s->cStringLength - last);
	newCStringLength += _s->cStringLength - last;
	newCString[newCStringLength] = 0;

	OFFreeMemory(_s->cString);
	_s->hasHash = false;
	_s->cString = newCString;
	_s->cStringLength = newCStringLength;
	_s->length = newLength;

	if ([replacement isKindOfClass: [OFUTF8String class]] ||
	    [replacement isKindOfClass: [OFMutableUTF8String class]]) {
		if (((OFMutableUTF8String *)replacement)->_s->isUTF8)
			_s->isUTF8 = true;
	} else
		_s->isUTF8 = true;
}

- (void)deleteLeadingWhitespaces
{
	size_t i;

	for (i = 0; i < _s->cStringLength; i++)
		if (!OFASCIIIsSpace(_s->cString[i]))
			break;

	_s->hasHash = false;
	_s->cStringLength -= i;
	_s->length -= i;

	memmove(_s->cString, _s->cString + i, _s->cStringLength);
	_s->cString[_s->cStringLength] = '\0';

	@try {

		_s->cString = OFResizeMemory(_s->cString, _s->cStringLength + 1,
		    1);
	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)deleteTrailingWhitespaces
{
	size_t d;
	char *p;

	_s->hasHash = false;

	d = 0;
	for (p = _s->cString + _s->cStringLength - 1; p >= _s->cString; p--) {
		if (!OFASCIIIsSpace(*p))
			break;

		*p = '\0';
		d++;
	}

	_s->cStringLength -= d;
	_s->length -= d;

	@try {

		_s->cString = OFResizeMemory(_s->cString, _s->cStringLength + 1,
		    1);
	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)deleteEnclosingWhitespaces
{
	size_t d, i;
	char *p;

	_s->hasHash = false;

	d = 0;
	for (p = _s->cString + _s->cStringLength - 1; p >= _s->cString; p--) {
		if (!OFASCIIIsSpace(*p))
			break;

		*p = '\0';
		d++;
	}

	_s->cStringLength -= d;
	_s->length -= d;

	for (i = 0; i < _s->cStringLength; i++)
		if (!OFASCIIIsSpace(_s->cString[i]))
			break;

	_s->cStringLength -= i;
	_s->length -= i;

	memmove(_s->cString, _s->cString + i, _s->cStringLength);
	_s->cString[_s->cStringLength] = '\0';

	@try {

		_s->cString = OFResizeMemory(_s->cString, _s->cStringLength + 1,
		    1);
	} @catch (OFOutOfMemoryException *e) {
		/* We don't really care, as we only made it smaller */
	}
}

- (void)makeImmutable
{
	object_setClass(self, [OFUTF8String class]);
}
@end

Modified src/OFMutableZIPArchiveEntry.h from [b1ed162368] to [7d57139a39].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFData *extraField;

/**
 * @brief The version which made the entry.
 *
 * The lower 8 bits are the ZIP specification version.@n
 * The upper 8 bits are the attribute compatibility.
 * See @ref of_zip_archive_entry_attribute_compatibility.
 */
@property (readwrite, nonatomic) uint16_t versionMadeBy;


/**
 * @brief The minimum version required to extract the file.
 *
 * The lower 8 bits are the ZIP specification version.@n
 * The upper 8 bits are the attribute compatibility.
 * See @ref of_zip_archive_entry_attribute_compatibility.
 */
@property (readwrite, nonatomic) uint16_t minVersionNeeded;


/**
 * @brief The last modification date of the entry's file.
 *
 * @note Due to limitations of the ZIP format, this has only 2 second precision.
 */
@property (readwrite, retain, nonatomic) OFDate *modificationDate;

/**
 * @brief The compression method of the entry.
 *
 * Supported values are:
 * Value                                             | Description
 * --------------------------------------------------|---------------
 * OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_NONE      | No compression
 * OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE   | Deflate
 * OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE64 | Deflate64
 *
 * Other values may be returned, but the file cannot be extracted then.
 */
@property (readwrite, nonatomic) uint16_t compressionMethod;


/**
 * @brief The compressed size of the entry's file.
 */
@property (readwrite, nonatomic) uint64_t compressedSize;

/**







|

|
>






|

|
>












|
|
|
|
|



|
>







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
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFData *extraField;

/**
 * @brief The version which made the entry.
 *
 * The lower 8 bits are the ZIP specification version.@n
 * The upper 8 bits are the attribute compatibility.
 * See @ref OFZIPArchiveEntryAttributeCompatibility.
 */
@property (readwrite, nonatomic)
    OFZIPArchiveEntryAttributeCompatibility versionMadeBy;

/**
 * @brief The minimum version required to extract the file.
 *
 * The lower 8 bits are the ZIP specification version.@n
 * The upper 8 bits are the attribute compatibility.
 * See @ref OFZIPArchiveEntryAttributeCompatibility.
 */
@property (readwrite, nonatomic)
    OFZIPArchiveEntryAttributeCompatibility minVersionNeeded;

/**
 * @brief The last modification date of the entry's file.
 *
 * @note Due to limitations of the ZIP format, this has only 2 second precision.
 */
@property (readwrite, retain, nonatomic) OFDate *modificationDate;

/**
 * @brief The compression method of the entry.
 *
 * Supported values are:
 * Value                                       | Description
 * --------------------------------------------|---------------
 * OFZIPArchiveEntryCompressionMethodNone      | No compression
 * OFZIPArchiveEntryCompressionMethodDeflate   | Deflate
 * OFZIPArchiveEntryCompressionMethodDeflate64 | Deflate64
 *
 * Other values may be returned, but the file cannot be extracted then.
 */
@property (readwrite, nonatomic)
    OFZIPArchiveEntryCompressionMethod compressionMethod;

/**
 * @brief The compressed size of the entry's file.
 */
@property (readwrite, nonatomic) uint64_t compressedSize;

/**

Modified src/OFMutableZIPArchiveEntry.m from [7972fcd174] to [bdaf9f4cc4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@dynamic modificationDate, compressionMethod, compressedSize, uncompressedSize;
@dynamic CRC32, versionSpecificAttributes, generalPurposeBitFlag;
@dynamic of_localFileHeaderOffset;

- (id)copy
{
	OFMutableZIPArchiveEntry *copy = [self mutableCopy];

	[copy makeImmutable];

	return copy;
}

- (void)setFileName: (OFString *)fileName
{
	void *pool = objc_autoreleasePoolPush();
	OFString *old;







<

<







29
30
31
32
33
34
35

36

37
38
39
40
41
42
43
@dynamic modificationDate, compressionMethod, compressedSize, uncompressedSize;
@dynamic CRC32, versionSpecificAttributes, generalPurposeBitFlag;
@dynamic of_localFileHeaderOffset;

- (id)copy
{
	OFMutableZIPArchiveEntry *copy = [self mutableCopy];

	[copy makeImmutable];

	return copy;
}

- (void)setFileName: (OFString *)fileName
{
	void *pool = objc_autoreleasePoolPush();
	OFString *old;
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
	old = _extraField;
	_extraField = [extraField copy];
	[old release];

	objc_autoreleasePoolPop(pool);
}

- (void)setVersionMadeBy: (uint16_t)versionMadeBy

{
	_versionMadeBy = versionMadeBy;
}

- (void)setMinVersionNeeded: (uint16_t)minVersionNeeded

{
	_minVersionNeeded = minVersionNeeded;
}

- (void)setModificationDate: (OFDate *)date
{
	void *pool = objc_autoreleasePoolPush();

	_lastModifiedFileDate = (((date.localYear - 1980) & 0xFF) << 9) |
	    ((date.localMonthOfYear & 0x0F) << 5) |
	    (date.localDayOfMonth & 0x1F);
	_lastModifiedFileTime = ((date.localHour & 0x1F) << 11) |
	    ((date.localMinute & 0x3F) << 5) | ((date.second >> 1) & 0x0F);

	objc_autoreleasePoolPop(pool);
}

- (void)setCompressionMethod: (uint16_t)compressionMethod

{
	_compressionMethod = compressionMethod;
}

- (void)setCompressedSize: (uint64_t)compressedSize
{
	_compressedSize = compressedSize;







|
>




|
>

















|
>







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
	old = _extraField;
	_extraField = [extraField copy];
	[old release];

	objc_autoreleasePoolPop(pool);
}

- (void)setVersionMadeBy:
    (OFZIPArchiveEntryAttributeCompatibility)versionMadeBy
{
	_versionMadeBy = versionMadeBy;
}

- (void)setMinVersionNeeded:
    (OFZIPArchiveEntryAttributeCompatibility)minVersionNeeded
{
	_minVersionNeeded = minVersionNeeded;
}

- (void)setModificationDate: (OFDate *)date
{
	void *pool = objc_autoreleasePoolPush();

	_lastModifiedFileDate = (((date.localYear - 1980) & 0xFF) << 9) |
	    ((date.localMonthOfYear & 0x0F) << 5) |
	    (date.localDayOfMonth & 0x1F);
	_lastModifiedFileTime = ((date.localHour & 0x1F) << 11) |
	    ((date.localMinute & 0x3F) << 5) | ((date.second >> 1) & 0x0F);

	objc_autoreleasePoolPop(pool);
}

- (void)setCompressionMethod:
    (OFZIPArchiveEntryCompressionMethod)compressionMethod
{
	_compressionMethod = compressionMethod;
}

- (void)setCompressedSize: (uint64_t)compressedSize
{
	_compressedSize = compressedSize;

Modified src/OFMutex.h from [bb9b8d8163] to [f277ea27da].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFLocking.h"

#import "mutex.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFMutex OFMutex.h ObjFW/OFMutex.h
 *
 * @brief A class for creating mutual exclusions.
 */
@interface OFMutex: OFObject <OFLocking>
{
	of_mutex_t _mutex;
	bool _initialized;
	OFString *_Nullable _name;
	OF_RESERVE_IVARS(OFMutex, 4)
}

/**
 * @brief Creates a new mutex.

<
<
|















<
|










|







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


 * 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 "OFLocking.h"

#import "OFPlainMutex.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFMutex OFMutex.h ObjFW/OFMutex.h
 *
 * @brief A class for creating mutual exclusions.
 */
@interface OFMutex: OFObject <OFLocking>
{
	OFPlainMutex _mutex;
	bool _initialized;
	OFString *_Nullable _name;
	OF_RESERVE_IVARS(OFMutex, 4)
}

/**
 * @brief Creates a new mutex.

Modified src/OFMutex.m from [2c5bdb0793] to [91291c7344].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	if (!of_mutex_new(&_mutex)) {
		Class c = self.class;
		[self release];
		@throw [OFInitializationFailedException exceptionWithClass: c];
	}

	_initialized = true;

	return self;
}

- (void)dealloc
{
	if (_initialized) {

		if (!of_mutex_free(&_mutex)) {

			OF_ENSURE(errno == EBUSY);

			@throw [OFStillLockedException exceptionWithLock: self];
		}
	}

	[_name release];

	[super dealloc];
}

- (void)lock
{
	if (!of_mutex_lock(&_mutex))


		@throw [OFLockFailedException exceptionWithLock: self
							  errNo: errno];
}

- (bool)tryLock
{

	if (!of_mutex_trylock(&_mutex)) {

		if (errno == EBUSY)
			return false;
		else
			@throw [OFLockFailedException exceptionWithLock: self
								  errNo: errno];
	}

	return true;
}

- (void)unlock
{

	if (!of_mutex_unlock(&_mutex))

		@throw [OFUnlockFailedException exceptionWithLock: self
							    errNo: errno];
}

- (OFString *)description
{
	if (_name == nil)
		return super.description;

	return [OFString stringWithFormat: @"<%@: %@>", self.className, _name];
}
@end







|













>
|
>
|












|
>
>

|




>
|
>
|



|







>
|
>

|










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
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	if (OFPlainMutexNew(&_mutex) != 0) {
		Class c = self.class;
		[self release];
		@throw [OFInitializationFailedException exceptionWithClass: c];
	}

	_initialized = true;

	return self;
}

- (void)dealloc
{
	if (_initialized) {
		int error = OFPlainMutexFree(&_mutex);

		if (error != 0) {
			OFEnsure(error == EBUSY);

			@throw [OFStillLockedException exceptionWithLock: self];
		}
	}

	[_name release];

	[super dealloc];
}

- (void)lock
{
	int error = OFPlainMutexLock(&_mutex);

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

- (bool)tryLock
{
	int error = OFPlainMutexTryLock(&_mutex);

	if (error != 0) {
		if (error == EBUSY)
			return false;
		else
			@throw [OFLockFailedException exceptionWithLock: self
								  errNo: error];
	}

	return true;
}

- (void)unlock
{
	int error = OFPlainMutexUnlock(&_mutex);

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

- (OFString *)description
{
	if (_name == nil)
		return super.description;

	return [OFString stringWithFormat: @"<%@: %@>", self.className, _name];
}
@end

Modified src/OFNonretainedObjectValue.h from [1970a050e9] to [3bd7c9c6b7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25


26
27
28

OF_ASSUME_NONNULL_BEGIN

@interface OFNonretainedObjectValue: OFValue
{
	id _object;
}


@end

OF_ASSUME_NONNULL_END







>
>



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

OF_ASSUME_NONNULL_BEGIN

@interface OFNonretainedObjectValue: OFValue
{
	id _object;
}

- (instancetype)initWithNonretainedObject: (id)object;
@end

OF_ASSUME_NONNULL_END

Modified src/OFNonretainedObjectValue.m from [ecf9f83c85] to [93108c2968].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
}

- (const char *)objCType
{
	return @encode(id);
}

- (void)getValue: (void *)value
	    size: (size_t)size
{
	if (size != sizeof(_object))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_object, sizeof(_object));
}

- (void *)pointerValue
{
	return _object;
}
@end







|
<












31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
}

- (const char *)objCType
{
	return @encode(id);
}

- (void)getValue: (void *)value size: (size_t)size

{
	if (size != sizeof(_object))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_object, sizeof(_object));
}

- (void *)pointerValue
{
	return _object;
}
@end

Added src/OFNotification.h version [de7bff6c2a].







































































































































































































































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

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFConstantString;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);

/**
 * @brief A name for a notification.
 */
typedef OFConstantString *OFNotificationName;

/**
 * @class OFNotification OFNotification.h ObjFW/OFNotification.h
 *
 * @brief A class to represent a notification for or from
 *	  @ref OFNotificationCenter.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFNotification: OFObject <OFCopying>
{
	OFNotificationName _name;
	id _Nullable _object;
	OFDictionary *_Nullable _userInfo;
}

/**
 * @brief The name of the notification.
 */
@property (readonly, nonatomic) OFNotificationName name;

/**
 * @brief The object of the notification. This is commonly the sender of the
 *	  notification.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id object;

/**
 * @brief Additional information about the notification.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFDictionary *userInfo;

/**
 * @brief Creates a new notification with the specified name and object.
 *
 * @param name The name for the notification
 * @param object The object for the notification. This is commonly the sender
 *		 of the notification.
 * @return A new, autoreleased OFNotification
 */
+ (instancetype)notificationWithName: (OFNotificationName)name
			      object: (nullable id)object;

/**
 * @brief Creates a new notification with the specified name, object and
 *	  additional information.
 *
 * @param name The name for the notification
 * @param object The object for the notification. This is commonly the sender
 *		 of the notification.
 * @param userInfo Additional information for the notification
 * @return A new, autoreleased OFNotification
 */
+ (instancetype)notificationWithName: (OFNotificationName)name
			      object: (nullable id)object
			    userInfo: (nullable OFDictionary *)userInfo;

/**
 * @brief Initializes an already allocated notification with the specified
 *	  name and object.
 *
 * @param name The name for the notification
 * @param object The object for the notification. This is commonly the sender
 *		 of the notification.
 * @return An initialized OFNotification
 */
- (instancetype)initWithName: (OFNotificationName)name
		      object: (nullable id)object;

/**
 * @brief Initializes an already allocated notification with the specified
 *	  name, object and additional information.
 *
 * @param name The name for the notification
 * @param object The object for the notification. This is commonly the sender
 *		 of the notification.
 * @param userInfo Additional information for the notification
 * @return An initialized OFNotification
 */
- (instancetype)initWithName: (OFNotificationName)name
		      object: (nullable id)object
		    userInfo: (nullable OFDictionary *)userInfo
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Added src/OFNotification.m version [d51e2a4110].



































































































































































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

#import "OFNotification.h"
#import "OFDictionary.h"
#import "OFString.h"

@implementation OFNotification
@synthesize name = _name, object = _object, userInfo = _userInfo;

+ (instancetype)notificationWithName: (OFNotificationName)name
			      object: (id)object
{
	return [[[self alloc] initWithName: name object: object] autorelease];
}

+ (instancetype)notificationWithName: (OFNotificationName)name
			      object: (id)object
			    userInfo: (OFDictionary *)userInfo
{
	return [[[self alloc] initWithName: name
				    object: object
				  userInfo: userInfo] autorelease];
}

- (instancetype)initWithName: (OFNotificationName)name object: (id)object
{
	return [self initWithName: name object: object userInfo: nil];
}

- (instancetype)initWithName: (OFNotificationName)name
		      object: (id)object
		    userInfo: (OFDictionary *)userInfo
{
	self = [super init];

	@try {
		_name = [name copy];
		_object = [object retain];
		_userInfo = [userInfo copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_name release];
	[_object release];
	[_userInfo release];

	[super dealloc];
}

- (id)copy
{
	return [self retain];
}
@end

Added src/OFNotificationCenter.h version [6d89a35135].







































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFNotification.h"

OF_ASSUME_NONNULL_BEGIN

@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
#ifdef OF_HAVE_THREADS
@class OFMutex;
#endif
@class OFNotificationCenterHandle;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when a notification has been posted.
 *
 * @param notification The notification that has been posted
 */
typedef void (^OFNotificationCenterBlock)(OFNotification *notification);
#endif

/**
 * @class OFNotificationCenter OFNotificationCenter.h \
 *	  ObjFW/OFNotificationCenter.h
 *
 * @brief A class to send and register for notifications.
 */
@interface OFNotificationCenter: OFObject
{
#ifdef OF_HAVE_THREADS
	OFMutex *_mutex;
#endif
	OFMutableDictionary *_handles;
	OF_RESERVE_IVARS(OFNotificationCenter, 4)
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) OFNotificationCenter *defaultCenter;
#endif

/**
 * @brief Returns the default notification center.
 */
+ (OFNotificationCenter *)defaultCenter;

/**
 * @brief Adds an observer for the specified notification and object.
 *
 * @param observer The object that should receive notifications
 * @param selector The selector to call on the observer on notifications. The
 *		   method must take exactly one object of type @ref
 *		   OFNotification.
 * @param name The name of the notification to observe
 * @param object The object that should be sending the notification, or `nil`
 *		 if the object should be ignored to determine what
 *		 notifications to deliver
 */
- (void)addObserver: (id)observer
	   selector: (SEL)selector
	       name: (OFNotificationName)name
	     object: (nullable id)object;

/**
 * @brief Removes an observer. All parameters must match those used with
 *	  @ref addObserver:selector:name:object:.
 *
 * @param observer The observer that was specified when adding the observer
 * @param selector The selector that was specified when adding the observer
 * @param name The name that was specified when adding the observer
 * @param object The object that was specified when adding the observer
 */
- (void)removeObserver: (id)observer
	      selector: (SEL)selector
		  name: (OFNotificationName)name
		object: (nullable id)object;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Adds an observer for the specified notification and object.
 *
 * To remove the observer again, use @ref removeObserver:.
 *
 * @param name The name of the notification to observe
 * @param object The object that should be sending the notification, or `nil`
 *		 if the object should be ignored to determine what
 *		 notifications to deliver
 * @param block The block to handle notifications
 * @return An opaque object to remove the observer again
 */
- (OFNotificationCenterHandle *)
    addObserverForName: (OFNotificationName)name
		object: (nullable id)object
	    usingBlock: (OFNotificationCenterBlock)block;

/**
 * @brief Removes an observer. The specified observer must be one returned by
 *	  @ref addObserver:selector:name:object:.
 *
 * @param observer The object that was returned when adding the observer
 */
- (void)removeObserver: (OFNotificationCenterHandle *)observer;
#endif

/**
 * @brief Posts the specified notification.
 *
 * @param notification The notification to post
 */
- (void)postNotification: (OFNotification *)notification;

/**
 * @brief Posts a notification with the specified name and object.
 *
 * @param name The name for the notification
 * @param object The object for the notification
 */
- (void)postNotificationName: (OFNotificationName)name
		      object: (nullable id)object;

/**
 * @brief Posts a notification with the specified name, object and additional
 *	  information.
 *
 * @param name The name for the notification
 * @param object The object for the notification
 * @param userInfo Additional information for the notification
 */
- (void)postNotificationName: (OFNotificationName)name
		      object: (nullable id)object
		    userInfo: (nullable OFDictionary *)userInfo;
@end

OF_ASSUME_NONNULL_END

Added src/OFNotificationCenter.m version [3332a25cc3].



























































































































































































































































































































































































































































































































































































































































































































































































































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

#import "OFNotificationCenter.h"
#import "OFArray.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif
#import "OFSet.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"

@interface OFDefaultNotificationCenter: OFNotificationCenter
@end

@interface OFNotificationCenterHandle: OFObject
{
@public
	OFNotificationName _name;
	id _observer;
	SEL _selector;
	unsigned long _selectorHash;
#ifdef OF_HAVE_BLOCKS
	OFNotificationCenterBlock _block;
#endif
	id _object;
}

- (instancetype)initWithName: (OFNotificationName)name
		    observer: (id)observer
		    selector: (SEL)selector
		      object: (id)object;
#ifdef OF_HAVE_BLOCKS
- (instancetype)initWithName: (OFNotificationName)name
		      object: (id)object
		       block: (OFNotificationCenterBlock)block;
#endif
@end

static OFNotificationCenter *defaultCenter;

@implementation OFNotificationCenterHandle
- (instancetype)initWithName: (OFNotificationName)name
		    observer: (id)observer
		    selector: (SEL)selector
		      object: (id)object
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		_name = [name copy];
		_observer = [observer retain];
		_selector = selector;
		_object = [object retain];

		_selectorHash = [[OFString stringWithUTF8String:
		    sel_getName(_selector)] hash];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

#ifdef OF_HAVE_BLOCKS
- (instancetype)initWithName: (OFNotificationName)name
		      object: (id)object
		       block: (OFNotificationCenterBlock)block
{
	self = [super init];

	@try {
		_name = [name copy];
		_object = [object retain];
		_block = [block copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
#endif

- (void)dealloc
{
	[_name release];
	[_observer release];
	[_object release];
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif

	[super dealloc];
}

- (bool)isEqual: (OFNotificationCenterHandle *)handle
{
	if (![handle isKindOfClass: [OFNotificationCenterHandle class]])
		return false;

	if (![handle->_name isEqual: _name])
		return false;

	if (handle->_observer != _observer &&
	    ![handle->_observer isEqual: _observer])
		return false;

	if (handle->_selector != _selector &&
	    !sel_isEqual(handle->_selector, _selector))
		return false;

#ifdef OF_HAVE_BLOCKS
	if (handle->_block != _block)
		return false;
#endif

	if (handle->_object != _object && ![handle->_object isEqual: _object])
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddHash(&hash, [_observer hash]);
	OFHashAddHash(&hash, _selectorHash);
#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		OFHashAddHash(&hash, (unsigned long)(uintptr_t)_block);
#endif
	OFHashAddHash(&hash, [_object hash]);

	OFHashFinalize(&hash);

	return hash;
}
@end

@implementation OFNotificationCenter
+ (void)initialize
{
	if (self != [OFNotificationCenter class])
		return;

	defaultCenter = [[OFDefaultNotificationCenter alloc] init];
}

+ (OFNotificationCenter *)defaultCenter
{
	return defaultCenter;
}

- (instancetype)init
{
	self = [super init];

	@try {
#ifdef OF_HAVE_THREADS
		_mutex = [[OFMutex alloc] init];
#endif
		_handles = [[OFMutableDictionary alloc] init];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
#ifdef OF_HAVE_THREADS
	[_mutex release];
#endif
	[_handles release];

	[super dealloc];
}

- (void)of_addObserver: (OFNotificationCenterHandle *)handle
{
#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {
#endif
		OFMutableSet *handlesForName =
		    [_handles objectForKey: handle->_name];

		if (handlesForName == nil) {
			handlesForName = [OFMutableSet set];
			[_handles setObject: handlesForName
				     forKey: handle->_name];
		}

		[handlesForName addObject: handle];
#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif
}

- (void)addObserver: (id)observer
	   selector: (SEL)selector
	       name: (OFNotificationName)name
	     object: (id)object
{
	void *pool = objc_autoreleasePoolPush();

	[self of_addObserver:
	    [[[OFNotificationCenterHandle alloc] initWithName: name
						     observer: observer
						     selector: selector
						       object: object]
	    autorelease]];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (OFNotificationCenterHandle *)
    addObserverForName: (OFNotificationName)name
		object: (id)object
	    usingBlock: (OFNotificationCenterBlock)block
{
	void *pool = objc_autoreleasePoolPush();
	OFNotificationCenterHandle *handle =
	    [[[OFNotificationCenterHandle alloc] initWithName: name
						       object: object
							block: block]
	    autorelease];

	[self of_addObserver: handle];

	[handle retain];

	objc_autoreleasePoolPop(pool);

	return [handle autorelease];
}
#endif

- (void)removeObserver: (OFNotificationCenterHandle *)handle
{
	void *pool = objc_autoreleasePoolPush();

	/* {} required to avoid -Wmisleading-indentation false positive. */
	if (![handle isKindOfClass: [OFNotificationCenterHandle class]]) {
		@throw [OFInvalidArgumentException exception];
	}

#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {
#endif
		OFNotificationName name = [[handle->_name copy] autorelease];
		OFMutableSet *handlesForName = [_handles objectForKey: name];

		[handlesForName removeObject: handle];

		if (handlesForName.count == 0)
			[_handles removeObjectForKey: name];
#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif

	objc_autoreleasePoolPop(pool);
}

- (void)removeObserver: (id)observer
	      selector: (SEL)selector
		  name: (OFNotificationName)name
		object: (id)object
{
	void *pool = objc_autoreleasePoolPush();

	[self removeObserver:
	    [[[OFNotificationCenterHandle alloc] initWithName: name
						     observer: observer
						     selector: selector
						       object: object]
	    autorelease]];

	objc_autoreleasePoolPop(pool);
}

- (void)postNotification: (OFNotification *)notification
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *matchedHandles = [OFMutableArray array];

#ifdef OF_HAVE_THREADS
	[_mutex lock];
	@try {
#endif
		for (OFNotificationCenterHandle *handle in
		    [_handles objectForKey: notification.name])
			if (handle->_object == nil ||
			    handle->_object == notification.object)
				[matchedHandles addObject: handle];
#ifdef OF_HAVE_THREADS
	} @finally {
		[_mutex unlock];
	}
#endif

	for (OFNotificationCenterHandle *handle in matchedHandles) {
#ifdef OF_HAVE_BLOCKS
		if (handle->_block != NULL)
			handle->_block(notification);
		else {
#endif
			void (*callback)(id, SEL, OFNotification *) =
			    (void (*)(id, SEL, OFNotification *))
			    [handle->_observer methodForSelector:
			    handle->_selector];

			callback(handle->_observer, handle->_selector,
			    notification);
#ifdef OF_HAVE_BLOCKS
		}
#endif
	}

	objc_autoreleasePoolPop(pool);
}

- (void)postNotificationName: (OFNotificationName)name
		      object: (nullable id)object
{
	[self postNotificationName: name object: object userInfo: nil];
}

- (void)postNotificationName: (OFNotificationName)name
		      object: (nullable id)object
		    userInfo: (nullable OFDictionary *)userInfo
{
	void *pool = objc_autoreleasePoolPush();

	[self postNotification:
	    [OFNotification notificationWithName: name
					  object: object
					userInfo: userInfo]];

	objc_autoreleasePoolPop(pool);
}
@end

@implementation OFDefaultNotificationCenter
- (instancetype)autorelease
{
	return self;
}

- (instancetype)retain
{
	return self;
}

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}
@end

Modified src/OFNull.h from [8747bb1b27] to [787aabc24c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFASN1DERRepresentation.h"
#import "OFJSONRepresentation.h"
#import "OFMessagePackRepresentation.h"
#import "OFSerialization.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFNull OFNull.h ObjFW/OFNull.h
 *
 * @brief A class for representing null values in collections.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFNull: OFObject <OFCopying, OFSerialization, OFJSONRepresentation,
    OFMessagePackRepresentation, OFASN1DERRepresentation>
/**
 * @brief Returns an OFNull singleton.
 *
 * @return An OFNull singleton
 */
+ (OFNull *)null;
@end

OF_ASSUME_NONNULL_END

<
<
|














<













|









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


 * 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 "OFJSONRepresentation.h"
#import "OFMessagePackRepresentation.h"
#import "OFSerialization.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFNull OFNull.h ObjFW/OFNull.h
 *
 * @brief A class for representing null values in collections.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFNull: OFObject <OFCopying, OFSerialization, OFJSONRepresentation,
    OFMessagePackRepresentation>
/**
 * @brief Returns an OFNull singleton.
 *
 * @return An OFNull singleton
 */
+ (OFNull *)null;
@end

OF_ASSUME_NONNULL_END

Modified src/OFNull.m from [43e1244281] to [bf841d1a2e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
#import "OFString.h"
#import "OFXMLElement.h"
#import "OFData.h"

#import "OFInvalidArgumentException.h"

@interface OFNull ()
- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth;
@end

static OFNull *null = nil;

@implementation OFNull
+ (void)initialize
{







|
>
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#import "OFString.h"
#import "OFXMLElement.h"
#import "OFData.h"

#import "OFInvalidArgumentException.h"

@interface OFNull ()
- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth;
@end

static OFNull *null = nil;

@implementation OFNull
+ (void)initialize
{
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
	void *pool;

	[self release];

	pool = objc_autoreleasePoolPush();

	if (![element.name isEqual: self.className] ||
	    ![element.namespace isEqual: OF_SERIALIZATION_NS])
		@throw [OFInvalidArgumentException exception];

	objc_autoreleasePoolPop(pool);

	return [OFNull null];
}








|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
	void *pool;

	[self release];

	pool = objc_autoreleasePoolPush();

	if (![element.name isEqual: self.className] ||
	    ![element.namespace isEqual: OFSerializationNS])
		@throw [OFInvalidArgumentException exception];

	objc_autoreleasePoolPop(pool);

	return [OFNull null];
}

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

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

	element = [OFXMLElement elementWithName: self.className
				      namespace: OF_SERIALIZATION_NS];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0
						depth: 0];
}

- (OFString *)JSONRepresentationWithOptions: (int)options

{
	return [self of_JSONRepresentationWithOptions: options
						depth: 0];
}

- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth
{
	return @"null";
}

- (OFData *)messagePackRepresentation
{
	uint8_t type = 0xC0;

	return [OFData dataWithItems: &type
			       count: 1];
}

- (OFData *)ASN1DERRepresentation
{
	const unsigned char bytes[] = { OF_ASN1_TAG_NUMBER_NULL, 0 };

	return [OFData dataWithItems: bytes
			       count: sizeof(bytes)];
}

- (instancetype)autorelease
{
	return self;
}

- (instancetype)retain
{
	return self;
}

- (void)release
{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

- (void)dealloc
{
	OF_DEALLOC_UNSUPPORTED
}
@end







|










|
<


|
>

|
<


|
>








<
|
<
<
<
<
<
<
<
<
<


















|







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

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

	element = [OFXMLElement elementWithName: self.className
				      namespace: OFSerializationNS];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0 depth: 0];

}

- (OFString *)JSONRepresentationWithOptions:
    (OFJSONRepresentationOptions)options
{
	return [self of_JSONRepresentationWithOptions: options depth: 0];

}

- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
					 depth: (size_t)depth
{
	return @"null";
}

- (OFData *)messagePackRepresentation
{
	uint8_t type = 0xC0;

	return [OFData dataWithItems: &type count: 1];









}

- (instancetype)autorelease
{
	return self;
}

- (instancetype)retain
{
	return self;
}

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

- (void)dealloc
{
	OF_DEALLOC_UNSUPPORTED
}
@end

Modified src/OFNumber.h from [028df04d4c] to [eff21822aa].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 */
#ifndef OF_NUMBER_M
OF_SUBCLASSING_RESTRICTED
#endif
@interface OFNumber: OFValue <OFComparing, OFSerialization,
    OFJSONRepresentation, OFMessagePackRepresentation>
{
	union of_number_value {
		double float_;
		long long signed_;
		unsigned long long unsigned_;
	} _value;
	const char *_typeEncoding;
}








|







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 */
#ifndef OF_NUMBER_M
OF_SUBCLASSING_RESTRICTED
#endif
@interface OFNumber: OFValue <OFComparing, OFSerialization,
    OFJSONRepresentation, OFMessagePackRepresentation>
{
	union {
		double float_;
		long long signed_;
		unsigned long long unsigned_;
	} _value;
	const char *_typeEncoding;
}

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@property (readonly, nonatomic) double doubleValue;

/**
 * @brief The OFNumber as a string.
 */
@property (readonly, nonatomic) OFString *stringValue;

#ifdef OF_HAVE_UNAVAILABLE
+ (instancetype)valueWithBytes: (const void *)bytes
		      objCType: (const char *)objCType OF_UNAVAILABLE;
+ (instancetype)valueWithPointer: (const void *)pointer OF_UNAVAILABLE;
+ (instancetype)valueWithNonretainedObject: (id)object OF_UNAVAILABLE;
+ (instancetype)valueWithRange: (of_range_t)range OF_UNAVAILABLE;
+ (instancetype)valueWithPoint: (of_point_t)point OF_UNAVAILABLE;
+ (instancetype)valueWithDimension: (of_dimension_t)dimension OF_UNAVAILABLE;
+ (instancetype)valueWithRectangle: (of_rectangle_t)rectangle OF_UNAVAILABLE;
#endif

/**
 * @brief Creates a new OFNumber with the specified `bool`.
 *
 * @param value The `bool` value which the OFNumber should contain
 * @return A new autoreleased OFNumber
 */







<




|
|
|
|
<







120
121
122
123
124
125
126

127
128
129
130
131
132
133
134

135
136
137
138
139
140
141
@property (readonly, nonatomic) double doubleValue;

/**
 * @brief The OFNumber as a string.
 */
@property (readonly, nonatomic) OFString *stringValue;


+ (instancetype)valueWithBytes: (const void *)bytes
		      objCType: (const char *)objCType OF_UNAVAILABLE;
+ (instancetype)valueWithPointer: (const void *)pointer OF_UNAVAILABLE;
+ (instancetype)valueWithNonretainedObject: (id)object OF_UNAVAILABLE;
+ (instancetype)valueWithRange: (OFRange)range OF_UNAVAILABLE;
+ (instancetype)valueWithPoint: (OFPoint)point OF_UNAVAILABLE;
+ (instancetype)valueWithSize: (OFSize)size OF_UNAVAILABLE;
+ (instancetype)valueWithRect: (OFRect)rect OF_UNAVAILABLE;


/**
 * @brief Creates a new OFNumber with the specified `bool`.
 *
 * @param value The `bool` value which the OFNumber should contain
 * @return A new autoreleased OFNumber
 */
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
 *
 * @param value The `double` value which the OFNumber should contain
 * @return A new autoreleased OFNumber
 */
+ (instancetype)numberWithDouble: (double)value;

- (instancetype)init OF_UNAVAILABLE;
#ifdef OF_HAVE_UNAVAILABLE
- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType OF_UNAVAILABLE;
- (instancetype)initWithPointer: (const void *)pointer OF_UNAVAILABLE;
- (instancetype)initWithNonretainedObject: (id)object OF_UNAVAILABLE;
- (instancetype)initWithRange: (of_range_t)range OF_UNAVAILABLE;
- (instancetype)initWithPoint: (of_point_t)point OF_UNAVAILABLE;
- (instancetype)initWithDimension: (of_dimension_t)dimension OF_UNAVAILABLE;
- (instancetype)initWithRectangle: (of_rectangle_t)rectangle OF_UNAVAILABLE;
#endif

/**
 * @brief Initializes an already allocated OFNumber with the specified `bool`.
 *
 * @param value The `bool` value which the OFNumber should contain
 * @return An initialized OFNumber
 */







<


<
<
<
<
<
<
<







234
235
236
237
238
239
240

241
242







243
244
245
246
247
248
249
 *
 * @param value The `double` value which the OFNumber should contain
 * @return A new autoreleased OFNumber
 */
+ (instancetype)numberWithDouble: (double)value;

- (instancetype)init OF_UNAVAILABLE;

- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType OF_UNAVAILABLE;








/**
 * @brief Initializes an already allocated OFNumber with the specified `bool`.
 *
 * @param value The `bool` value which the OFNumber should contain
 * @return An initialized OFNumber
 */
359
360
361
362
363
364
365








366
367
368
369
370
371
372
373
/**
 * @brief Initializes an already allocated OFNumber with the specified `double`.
 *
 * @param value The `double` value which the OFNumber should contain
 * @return An initialized OFNumber
 */
- (instancetype)initWithDouble: (double)value;








@end

OF_ASSUME_NONNULL_END

#if !defined(NSINTEGER_DEFINED) && !__has_feature(modules)
/* Required for number literals to work */
@compatibility_alias NSNumber OFNumber;
#endif







>
>
>
>
>
>
>
>








347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
/**
 * @brief Initializes an already allocated OFNumber with the specified `double`.
 *
 * @param value The `double` value which the OFNumber should contain
 * @return An initialized OFNumber
 */
- (instancetype)initWithDouble: (double)value;

/**
 * @brief Compares the number to another number.
 *
 * @param number The number to compare the number to
 * @return The result of the comparison
 */
- (OFComparisonResult)compare: (OFNumber *)number;
@end

OF_ASSUME_NONNULL_END

#if !defined(NSINTEGER_DEFINED) && !__has_feature(modules)
/* Required for number literals to work */
@compatibility_alias NSNumber OFNumber;
#endif

Modified src/OFNumber.m from [80a9f3f337] to [fd08ff8e66].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

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

@interface OFNumber ()
+ (instancetype)of_alloc;
- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth;
@end

@interface OFNumberPlaceholder: OFNumber
@end

@interface OFNumberSingleton: OFNumber
@end

#ifdef OF_OBJFW_RUNTIME
enum {
	TAG_CHAR,
	TAG_SHORT,
	TAG_INT,
	TAG_LONG,
	TAG_LONG_LONG,
	TAG_UNSIGNED_CHAR,
	TAG_UNSIGNED_SHORT,
	TAG_UNSIGNED_INT,
	TAG_UNSIGNED_LONG,
	TAG_UNSIGNED_LONG_LONG,
};
# define TAG_BITS 4
# define TAG_MASK 0xF

@interface OFTaggedPointerNumber: OFNumberSingleton
@end
#endif

static struct {
	Class isa;







|
>
|









|
|
|
|
|
|
|
|
|
|
|

|
|







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

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

@interface OFNumber ()
+ (instancetype)of_alloc;
- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth;
@end

@interface OFNumberPlaceholder: OFNumber
@end

@interface OFNumberSingleton: OFNumber
@end

#ifdef OF_OBJFW_RUNTIME
enum Tag {
	tagChar,
	tagShort,
	tagInt,
	tagLong,
	tagLongLong,
	tagUnsignedChar,
	tagUnsignedShort,
	tagUnsignedInt,
	tagUnsignedLong,
	tagUnsignedLongLong,
};
static const uint_fast8_t tagBits = 4;
static const uintptr_t tagMask = 0xF;

@interface OFTaggedPointerNumber: OFNumberSingleton
@end
#endif

static struct {
	Class isa;
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
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
	}
}

@implementation OFNumberPlaceholder
- (instancetype)initWithBool: (bool)value
{
	if (value) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, trueNumberInit);
		return (id)trueNumber;
	} else {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, falseNumberInit);
		return (id)falseNumber;
	}
}

#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#endif
- (instancetype)initWithChar: (signed char)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, charZeroNumberInit);
		return (id)charZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned char)value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned char)value << TAG_BITS) | TAG_CHAR);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithChar: value];
}

- (instancetype)initWithShort: (short)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, shortZeroNumberInit);
		return (id)shortZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned short)value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned short)value << TAG_BITS) | TAG_SHORT);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithShort: value];
}

- (instancetype)initWithInt: (int)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, intZeroNumberInit);
		return (id)intZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned int)value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned int)value << TAG_BITS) | TAG_INT);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithInt: value];
}

- (instancetype)initWithLong: (long)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, longZeroNumberInit);
		return (id)longZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned long)value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned long)value << TAG_BITS) | TAG_LONG);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithLong: value];
}

- (instancetype)initWithLongLong: (long long)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, longLongZeroNumberInit);
		return (id)longLongZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned long long)value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned long long)value << TAG_BITS) |
		    TAG_LONG_LONG);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithLongLong: value];
}

- (instancetype)initWithUnsignedChar: (unsigned char)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, unsignedCharZeroNumberInit);
		return (id)unsignedCharZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_CHAR);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedChar: value];
}

- (instancetype)initWithUnsignedShort: (unsigned short)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, unsignedShortZeroNumberInit);
		return (id)unsignedShortZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_SHORT);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedShort: value];
}

- (instancetype)initWithUnsignedInt: (unsigned int)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, unsignedIntZeroNumberInit);
		return (id)unsignedIntZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_INT);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedInt: value];
}

- (instancetype)initWithUnsignedLong: (unsigned long)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, unsignedLongZeroNumberInit);
		return (id)unsignedLongZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_LONG);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedLong: value];
}

- (instancetype)initWithUnsignedLongLong: (unsigned long long)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, unsignedLongLongZeroNumberInit);
		return (id)unsignedLongLongZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> TAG_BITS)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_LONG_LONG);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedLongLong: value];
}

- (instancetype)initWithFloat: (float)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, floatZeroNumberInit);
		return (id)floatZeroNumber;
	}

	return (id)[[OFNumber of_alloc] initWithFloat: value];
}

- (instancetype)initWithDouble: (double)value
{
	if (value == 0) {
		static of_once_t once = OF_ONCE_INIT;
		of_once(&once, doubleZeroNumberInit);
		return (id)doubleZeroNumber;
	}

	return (id)[[OFNumber of_alloc] initWithDouble: value];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element







|
|


|
|











|
|


|

|












|
|


|

|












|
|


|

|












|
|


|

|












|
|


|

|
|












|
|


|

|












|
|


|

|












|
|


|

|












|
|


|

|












|
|


|

|












|
|









|
|







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

@implementation OFNumberPlaceholder
- (instancetype)initWithBool: (bool)value
{
	if (value) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, trueNumberInit);
		return (id)trueNumber;
	} else {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, falseNumberInit);
		return (id)falseNumber;
	}
}

#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#endif
- (instancetype)initWithChar: (signed char)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, charZeroNumberInit);
		return (id)charZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned char)value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned char)value << tagBits) | tagChar);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithChar: value];
}

- (instancetype)initWithShort: (short)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, shortZeroNumberInit);
		return (id)shortZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned short)value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned short)value << tagBits) | tagShort);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithShort: value];
}

- (instancetype)initWithInt: (int)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, intZeroNumberInit);
		return (id)intZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned int)value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned int)value << tagBits) | tagInt);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithInt: value];
}

- (instancetype)initWithLong: (long)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, longZeroNumberInit);
		return (id)longZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned long)value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned long)value << tagBits) | tagLong);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithLong: value];
}

- (instancetype)initWithLongLong: (long long)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, longLongZeroNumberInit);
		return (id)longLongZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if ((unsigned long long)value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)(unsigned long long)value << tagBits) |
		    tagLongLong);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithLongLong: value];
}

- (instancetype)initWithUnsignedChar: (unsigned char)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, unsignedCharZeroNumberInit);
		return (id)unsignedCharZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << tagBits) | tagUnsignedChar);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedChar: value];
}

- (instancetype)initWithUnsignedShort: (unsigned short)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, unsignedShortZeroNumberInit);
		return (id)unsignedShortZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << tagBits) | tagUnsignedShort);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedShort: value];
}

- (instancetype)initWithUnsignedInt: (unsigned int)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, unsignedIntZeroNumberInit);
		return (id)unsignedIntZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << tagBits) | tagUnsignedInt);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedInt: value];
}

- (instancetype)initWithUnsignedLong: (unsigned long)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, unsignedLongZeroNumberInit);
		return (id)unsignedLongZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << tagBits) | tagUnsignedLong);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedLong: value];
}

- (instancetype)initWithUnsignedLongLong: (unsigned long long)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, unsignedLongLongZeroNumberInit);
		return (id)unsignedLongLongZeroNumber;
#ifdef OF_OBJFW_RUNTIME
	} else if (value <= (UINTPTR_MAX >> tagBits)) {
		id ret = objc_createTaggedPointer(numberTag,
		    ((uintptr_t)value << tagBits) | tagUnsignedLongLong);

		if (ret != nil)
			return ret;
#endif
	}

	return (id)[[OFNumber of_alloc] initWithUnsignedLongLong: value];
}

- (instancetype)initWithFloat: (float)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, floatZeroNumberInit);
		return (id)floatZeroNumber;
	}

	return (id)[[OFNumber of_alloc] initWithFloat: value];
}

- (instancetype)initWithDouble: (double)value
{
	if (value == 0) {
		static OFOnceControl onceControl = OFOnceControlInitValue;
		OFOnce(&onceControl, doubleZeroNumberInit);
		return (id)doubleZeroNumber;
	}

	return (id)[[OFNumber of_alloc] initWithDouble: value];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
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

- (void)release
{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}
@end

#ifdef OF_OBJFW_RUNTIME
@implementation OFTaggedPointerNumber
- (const char *)objCType
{
	uintptr_t value = object_getTaggedPointerValue(self);

	switch (value & TAG_MASK) {
	case TAG_CHAR:
		return @encode(signed char);
	case TAG_SHORT:
		return @encode(short);
	case TAG_INT:
		return @encode(int);
	case TAG_LONG:
		return @encode(long);
	case TAG_LONG_LONG:
		return @encode(long long);
	case TAG_UNSIGNED_CHAR:
		return @encode(unsigned char);
	case TAG_UNSIGNED_SHORT:
		return @encode(unsigned short);
	case TAG_UNSIGNED_INT:
		return @encode(unsigned int);
	case TAG_UNSIGNED_LONG:
		return @encode(unsigned long);
	case TAG_UNSIGNED_LONG_LONG:
		return @encode(unsigned long long);
	default:
		@throw [OFInvalidArgumentException exception];
	}
}

# define RETURN_VALUE							   \
	uintptr_t value = object_getTaggedPointerValue(self);		   \
									   \
	switch (value & TAG_MASK) {					   \
	case TAG_CHAR:							   \
		return (signed char)(unsigned char)(value >> TAG_BITS);	   \
	case TAG_SHORT:							   \
		return (short)(unsigned short)(value >> TAG_BITS);	   \
	case TAG_INT:							   \
		return (int)(unsigned int)(value >> TAG_BITS);		   \
	case TAG_LONG:							   \
		return (long)(unsigned long)(value >> TAG_BITS);	   \
	case TAG_LONG_LONG:						   \
		return (long long)(unsigned long long)(value >> TAG_BITS); \
	case TAG_UNSIGNED_CHAR:						   \
		return (unsigned char)(value >> TAG_BITS);		   \
	case TAG_UNSIGNED_SHORT:					   \
		return (unsigned short)(value >> TAG_BITS);		   \
	case TAG_UNSIGNED_INT:						   \
		return (unsigned int)(value >> TAG_BITS);		   \
	case TAG_UNSIGNED_LONG:						   \
		return (unsigned long)(value >> TAG_BITS);		   \
	case TAG_UNSIGNED_LONG_LONG:					   \
		return (unsigned long long)(value >> TAG_BITS);		   \
	default:							   \
		@throw [OFInvalidArgumentException exception];		   \
	}
- (long long)longLongValue
{
	RETURN_VALUE
}

- (unsigned long long)unsignedLongLongValue







|









|
|

|

|

|

|

|

|

|

|

|






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







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

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}
@end

#ifdef OF_OBJFW_RUNTIME
@implementation OFTaggedPointerNumber
- (const char *)objCType
{
	uintptr_t value = object_getTaggedPointerValue(self);

	switch (value & tagMask) {
	case tagChar:
		return @encode(signed char);
	case tagShort:
		return @encode(short);
	case tagInt:
		return @encode(int);
	case tagLong:
		return @encode(long);
	case tagLongLong:
		return @encode(long long);
	case tagUnsignedChar:
		return @encode(unsigned char);
	case tagUnsignedShort:
		return @encode(unsigned short);
	case tagUnsignedInt:
		return @encode(unsigned int);
	case tagUnsignedLong:
		return @encode(unsigned long);
	case tagUnsignedLongLong:
		return @encode(unsigned long long);
	default:
		@throw [OFInvalidArgumentException exception];
	}
}

# define RETURN_VALUE							  \
	uintptr_t value = object_getTaggedPointerValue(self);		  \
									  \
	switch (value & tagMask) {					  \
	case tagChar:							  \
		return (signed char)(unsigned char)(value >> tagBits);	  \
	case tagShort:							  \
		return (short)(unsigned short)(value >> tagBits);	  \
	case tagInt:							  \
		return (int)(unsigned int)(value >> tagBits);		  \
	case tagLong:							  \
		return (long)(unsigned long)(value >> tagBits);		  \
	case tagLongLong:						  \
		return (long long)(unsigned long long)(value >> tagBits); \
	case tagUnsignedChar:						  \
		return (unsigned char)(value >> tagBits);		  \
	case tagUnsignedShort:						  \
		return (unsigned short)(value >> tagBits);		  \
	case tagUnsignedInt:						  \
		return (unsigned int)(value >> tagBits);		  \
	case tagUnsignedLong:						  \
		return (unsigned long)(value >> tagBits);		  \
	case tagUnsignedLongLong:					  \
		return (unsigned long long)(value >> tagBits);		  \
	default:							  \
		@throw [OFInvalidArgumentException exception];		  \
	}
- (long long)longLongValue
{
	RETURN_VALUE
}

- (unsigned long long)unsignedLongLongValue
508
509
510
511
512
513
514




































515
516
517
518
519
520
521
+ (instancetype)alloc
{
	if (self == [OFNumber class])
		return (id)&placeholder;

	return [super alloc];
}





































+ (instancetype)numberWithBool: (bool)value
{
	return [[[self alloc] initWithBool: value] autorelease];
}

+ (instancetype)numberWithChar: (signed char)value







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







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
+ (instancetype)alloc
{
	if (self == [OFNumber class])
		return (id)&placeholder;

	return [super alloc];
}

+ (instancetype)valueWithBytes: (const void *)bytes
		      objCType: (const char *)objCType
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)valueWithPointer: (const void *)pointer
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)valueWithNonretainedObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)valueWithRange: (OFRange)range
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)valueWithPoint: (OFPoint)point
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)valueWithSize: (OFSize)size
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)valueWithRect: (OFRect)rect
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)numberWithBool: (bool)value
{
	return [[[self alloc] initWithBool: value] autorelease];
}

+ (instancetype)numberWithChar: (signed char)value
576
577
578
579
580
581
582






583
584
585
586
587
588
589
+ (instancetype)numberWithDouble: (double)value
{
	return [[[self alloc] initWithDouble: value] autorelease];
}

- (instancetype)init
{






	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithBool: (bool)value
{
	self = [super init];








>
>
>
>
>
>







611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
+ (instancetype)numberWithDouble: (double)value
{
	return [[[self alloc] initWithDouble: value] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithBool: (bool)value
{
	self = [super init];

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
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFString *typeString;

		if (![element.name isEqual: @"OFNumber"] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		typeString = [element attributeForName: @"type"].stringValue;

		if ([typeString isEqual: @"bool"]) {
			OFString *stringValue = element.stringValue;
			if ([stringValue isEqual: @"true"])
				self = [self initWithBool: true];
			else if ([stringValue isEqual: @"false"])
				self = [self initWithBool: false];
			else
				@throw [OFInvalidArgumentException exception];
		} else if ([typeString isEqual: @"float"]) {
			unsigned long long value =
			    [element unsignedLongLongValueWithBase: 16];

			if (value > UINT64_MAX)
				@throw [OFOutOfRangeException exception];

			self = [self initWithDouble: OF_BSWAP_DOUBLE_IF_LE(
			    OF_INT_TO_DOUBLE_RAW(OF_BSWAP64_IF_LE(value)))];
		} else if ([typeString isEqual: @"signed"])
			self = [self initWithLongLong: element.longLongValue];
		else if ([typeString isEqual: @"unsigned"])
			self = [self initWithUnsignedLongLong:
			    element.unsignedLongLongValue];
		else
			@throw [OFInvalidArgumentException exception];







|



















|
|







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
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFString *typeString;

		if (![element.name isEqual: @"OFNumber"] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		typeString = [element attributeForName: @"type"].stringValue;

		if ([typeString isEqual: @"bool"]) {
			OFString *stringValue = element.stringValue;
			if ([stringValue isEqual: @"true"])
				self = [self initWithBool: true];
			else if ([stringValue isEqual: @"false"])
				self = [self initWithBool: false];
			else
				@throw [OFInvalidArgumentException exception];
		} else if ([typeString isEqual: @"float"]) {
			unsigned long long value =
			    [element unsignedLongLongValueWithBase: 16];

			if (value > UINT64_MAX)
				@throw [OFOutOfRangeException exception];

			self = [self initWithDouble: OFFromBigEndianDouble(
			    OFRawUInt64ToDouble(OFToBigEndian64(value)))];
		} else if ([typeString isEqual: @"signed"])
			self = [self initWithLongLong: element.longLongValue];
		else if ([typeString isEqual: @"unsigned"])
			self = [self initWithUnsignedLongLong:
			    element.unsignedLongLongValue];
		else
			@throw [OFInvalidArgumentException exception];
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
}

- (const char *)objCType
{
	return _typeEncoding;
}

- (void)getValue: (void *)value
	    size: (size_t)size
{
	switch (*self.objCType) {
#define CASE(enc, type, property)					\
	case enc: {							\
		type tmp = (type)self.property;				\
									\
		if (size != sizeof(type))				\







|
<







833
834
835
836
837
838
839
840

841
842
843
844
845
846
847
}

- (const char *)objCType
{
	return _typeEncoding;
}

- (void)getValue: (void *)value size: (size_t)size

{
	switch (*self.objCType) {
#define CASE(enc, type, property)					\
	case enc: {							\
		type tmp = (type)self.property;				\
									\
		if (size != sizeof(type))				\
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

	if (isSigned(self) || isSigned(number))
		return (number.longLongValue == self.longLongValue);

	return (number.unsignedLongLongValue == self.unsignedLongLongValue);
}

- (of_comparison_result_t)compare: (id <OFComparing>)object
{
	OFNumber *number;

	if (![(id)object isKindOfClass: [OFNumber class]])
		@throw [OFInvalidArgumentException exception];

	number = (OFNumber *)object;

	if (isFloat(self) || isFloat(number)) {
		double double1 = self.doubleValue;
		double double2 = number.doubleValue;

		if (double1 > double2)
			return OF_ORDERED_DESCENDING;
		if (double1 < double2)
			return OF_ORDERED_ASCENDING;

		return OF_ORDERED_SAME;
	} else if (isSigned(self) || isSigned(number)) {
		long long int1 = self.longLongValue;
		long long int2 = number.longLongValue;

		if (int1 > int2)
			return OF_ORDERED_DESCENDING;
		if (int1 < int2)
			return OF_ORDERED_ASCENDING;

		return OF_ORDERED_SAME;
	} else {
		unsigned long long uint1 = self.unsignedLongLongValue;
		unsigned long long uint2 = number.unsignedLongLongValue;

		if (uint1 > uint2)
			return OF_ORDERED_DESCENDING;
		if (uint1 < uint2)
			return OF_ORDERED_ASCENDING;

		return OF_ORDERED_SAME;
	}
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	if (isFloat(self)) {
		double d;

		if (isnan(self.doubleValue))
			return 0;

		d = OF_BSWAP_DOUBLE_IF_BE(self.doubleValue);

		for (uint_fast8_t i = 0; i < sizeof(double); i++)
			OF_HASH_ADD(hash, ((char *)&d)[i]);
	} else if (isSigned(self) || isUnsigned(self)) {
		unsigned long long value = self.unsignedLongLongValue;

		while (value != 0) {
			OF_HASH_ADD(hash, value & 0xFF);
			value >>= 8;
		}
	} else
		@throw [OFInvalidFormatException exception];

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
	return [self retain];







<
<
|
|
|

<
<






|

|

|





|

|

|





|

|

|





|

|







|


|




|





|







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

	if (isSigned(self) || isSigned(number))
		return (number.longLongValue == self.longLongValue);

	return (number.unsignedLongLongValue == self.unsignedLongLongValue);
}



- (OFComparisonResult)compare: (OFNumber *)number
{
	if (![number isKindOfClass: [OFNumber class]])
		@throw [OFInvalidArgumentException exception];



	if (isFloat(self) || isFloat(number)) {
		double double1 = self.doubleValue;
		double double2 = number.doubleValue;

		if (double1 > double2)
			return OFOrderedDescending;
		if (double1 < double2)
			return OFOrderedAscending;

		return OFOrderedSame;
	} else if (isSigned(self) || isSigned(number)) {
		long long int1 = self.longLongValue;
		long long int2 = number.longLongValue;

		if (int1 > int2)
			return OFOrderedDescending;
		if (int1 < int2)
			return OFOrderedAscending;

		return OFOrderedSame;
	} else {
		unsigned long long uint1 = self.unsignedLongLongValue;
		unsigned long long uint2 = number.unsignedLongLongValue;

		if (uint1 > uint2)
			return OFOrderedDescending;
		if (uint1 < uint2)
			return OFOrderedAscending;

		return OFOrderedSame;
	}
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	if (isFloat(self)) {
		double d;

		if (isnan(self.doubleValue))
			return 0;

		d = OFToLittleEndianDouble(self.doubleValue);

		for (uint_fast8_t i = 0; i < sizeof(double); i++)
			OFHashAdd(&hash, ((char *)&d)[i]);
	} else if (isSigned(self) || isUnsigned(self)) {
		unsigned long long value = self.unsignedLongLongValue;

		while (value != 0) {
			OFHashAdd(&hash, value & 0xFF);
			value >>= 8;
		}
	} else
		@throw [OFInvalidFormatException exception];

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{
	return [self retain];
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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

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

	element = [OFXMLElement elementWithName: @"OFNumber"
				      namespace: OF_SERIALIZATION_NS
				    stringValue: self.description];

	if (*self.objCType == 'B')
		[element addAttributeWithName: @"type"
				  stringValue: @"bool"];
	else if (isFloat(self)) {
		[element addAttributeWithName: @"type"
				  stringValue: @"float"];
		element.stringValue = [OFString
		    stringWithFormat: @"%016" PRIx64,
		    OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE(

		    self.doubleValue)))];
	} else if (isSigned(self))
		[element addAttributeWithName: @"type"
				  stringValue: @"signed"];
	else if (isUnsigned(self))
		[element addAttributeWithName: @"type"
				  stringValue: @"unsigned"];
	else
		@throw [OFInvalidFormatException exception];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0
						depth: 0];
}

- (OFString *)JSONRepresentationWithOptions: (int)options

{
	return [self of_JSONRepresentationWithOptions: options
						depth: 0];
}

- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth
{
	double doubleValue;

	if (*self.objCType == 'B')
		return (self.boolValue ? @"true" : @"false");

	doubleValue = self.doubleValue;
	if (isinf(doubleValue)) {
		if (options & OF_JSON_REPRESENTATION_JSON5) {
			if (doubleValue > 0)
				return @"Infinity";
			else
				return @"-Infinity";
		} else
			@throw [OFInvalidArgumentException exception];
	}

	return self.description;
}

- (OFData *)messagePackRepresentation
{
	OFMutableData *data;
	const char *typeEncoding = self.objCType;

	if (*typeEncoding == 'B') {
		uint8_t type = (self.boolValue ? 0xC3 : 0xC2);

		data = [OFMutableData dataWithItems: &type
					      count: 1];
	} else if (*typeEncoding == 'f') {
		uint8_t type = 0xCA;
		float tmp = OF_BSWAP_FLOAT_IF_LE(self.floatValue);

		data = [OFMutableData dataWithItemSize: 1
					      capacity: 5];

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else if (*typeEncoding == 'd') {
		uint8_t type = 0xCB;
		double tmp = OF_BSWAP_DOUBLE_IF_LE(self.doubleValue);

		data = [OFMutableData dataWithItemSize: 1
					      capacity: 9];

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else if (isSigned(self)) {
		long long value = self.longLongValue;

		if (value >= -32 && value < 0) {
			uint8_t tmp = 0xE0 | ((uint8_t)(value - 32) & 0x1F);

			data = [OFMutableData dataWithItems: &tmp
						      count: 1];
		} else if (value >= INT8_MIN && value <= INT8_MAX) {
			uint8_t type = 0xD0;
			int8_t tmp = (int8_t)value;

			data = [OFMutableData dataWithItemSize: 1
						      capacity: 2];

			[data addItem: &type];
			[data addItem: &tmp];
		} else if (value >= INT16_MIN && value <= INT16_MAX) {
			uint8_t type = 0xD1;
			int16_t tmp = OF_BSWAP16_IF_LE((int16_t)value);

			data = [OFMutableData dataWithItemSize: 1
						      capacity: 3];

			[data addItem: &type];
			[data addItems: &tmp
				 count: sizeof(tmp)];
		} else if (value >= INT32_MIN && value <= INT32_MAX) {
			uint8_t type = 0xD2;
			int32_t tmp = OF_BSWAP32_IF_LE((int32_t)value);

			data = [OFMutableData dataWithItemSize: 1
						      capacity: 5];

			[data addItem: &type];
			[data addItems: &tmp
				 count: sizeof(tmp)];
		} else if (value >= INT64_MIN && value <= INT64_MAX) {
			uint8_t type = 0xD3;
			int64_t tmp = OF_BSWAP64_IF_LE((int64_t)value);

			data = [OFMutableData dataWithItemSize: 1
						      capacity: 9];

			[data addItem: &type];
			[data addItems: &tmp
				 count: sizeof(tmp)];
		} else
			@throw [OFOutOfRangeException exception];
	} else if (isUnsigned(self)) {
		unsigned long long value = self.unsignedLongLongValue;

		if (value <= 127) {
			uint8_t tmp = ((uint8_t)value & 0x7F);

			data = [OFMutableData dataWithItems: &tmp
						      count: 1];
		} else if (value <= UINT8_MAX) {
			uint8_t type = 0xCC;
			uint8_t tmp = (uint8_t)value;

			data = [OFMutableData dataWithItemSize: 1
						      capacity: 2];

			[data addItem: &type];
			[data addItem: &tmp];
		} else if (value <= UINT16_MAX) {
			uint8_t type = 0xCD;
			uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)value);

			data = [OFMutableData dataWithItemSize: 1
						      capacity: 3];

			[data addItem: &type];
			[data addItems: &tmp
				 count: sizeof(tmp)];
		} else if (value <= UINT32_MAX) {
			uint8_t type = 0xCE;
			uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)value);

			data = [OFMutableData dataWithItemSize: 1
						      capacity: 5];

			[data addItem: &type];
			[data addItems: &tmp
				 count: sizeof(tmp)];
		} else if (value <= UINT64_MAX) {
			uint8_t type = 0xCF;
			uint64_t tmp = OF_BSWAP64_IF_LE((uint64_t)value);

			data = [OFMutableData dataWithItemSize: 1
						      capacity: 9];

			[data addItem: &type];
			[data addItems: &tmp
				 count: sizeof(tmp)];
		} else
			@throw [OFOutOfRangeException exception];
	} else
		@throw [OFInvalidFormatException exception];

	[data makeImmutable];

	return data;
}
@end







|



|
<

|
<


<
>


|
<















|
<


|
>

|
<


|
>
|








|


















<
|
<


|

|
<
<

|
<


|

|
<
<

|
<






|
<




|
<
<




|

|
<
<

|
<


|

|
<
<

|
<


|

|
<
<

|
<







<
|
<




|
<
<




|

|
<
<

|
<


|

|
<
<

|
<


|

|
<
<

|
<










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


1182
1183
1184
1185
1186
1187
1188


1189
1190

1191
1192
1193
1194
1195


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

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

	element = [OFXMLElement elementWithName: @"OFNumber"
				      namespace: OFSerializationNS
				    stringValue: self.description];

	if (*self.objCType == 'B')
		[element addAttributeWithName: @"type" stringValue: @"bool"];

	else if (isFloat(self)) {
		[element addAttributeWithName: @"type" stringValue: @"float"];

		element.stringValue = [OFString
		    stringWithFormat: @"%016" PRIx64,

		    OFFromBigEndian64(OFDoubleToRawUInt64(OFToBigEndianDouble(
		    self.doubleValue)))];
	} else if (isSigned(self))
		[element addAttributeWithName: @"type" stringValue: @"signed"];

	else if (isUnsigned(self))
		[element addAttributeWithName: @"type"
				  stringValue: @"unsigned"];
	else
		@throw [OFInvalidFormatException exception];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0 depth: 0];

}

- (OFString *)JSONRepresentationWithOptions:
    (OFJSONRepresentationOptions)options
{
	return [self of_JSONRepresentationWithOptions: options depth: 0];

}

- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth
{
	double doubleValue;

	if (*self.objCType == 'B')
		return (self.boolValue ? @"true" : @"false");

	doubleValue = self.doubleValue;
	if (isinf(doubleValue)) {
		if (options & OFJSONRepresentationOptionJSON5) {
			if (doubleValue > 0)
				return @"Infinity";
			else
				return @"-Infinity";
		} else
			@throw [OFInvalidArgumentException exception];
	}

	return self.description;
}

- (OFData *)messagePackRepresentation
{
	OFMutableData *data;
	const char *typeEncoding = self.objCType;

	if (*typeEncoding == 'B') {
		uint8_t type = (self.boolValue ? 0xC3 : 0xC2);

		data = [OFMutableData dataWithItems: &type count: 1];

	} else if (*typeEncoding == 'f') {
		uint8_t type = 0xCA;
		float tmp = OFToBigEndianFloat(self.floatValue);

		data = [OFMutableData dataWithCapacity: 5];


		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else if (*typeEncoding == 'd') {
		uint8_t type = 0xCB;
		double tmp = OFToBigEndianDouble(self.doubleValue);

		data = [OFMutableData dataWithCapacity: 9];


		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else if (isSigned(self)) {
		long long value = self.longLongValue;

		if (value >= -32 && value < 0) {
			uint8_t tmp = 0xE0 | ((uint8_t)(value - 32) & 0x1F);

			data = [OFMutableData dataWithItems: &tmp count: 1];

		} else if (value >= INT8_MIN && value <= INT8_MAX) {
			uint8_t type = 0xD0;
			int8_t tmp = (int8_t)value;

			data = [OFMutableData dataWithCapacity: 2];


			[data addItem: &type];
			[data addItem: &tmp];
		} else if (value >= INT16_MIN && value <= INT16_MAX) {
			uint8_t type = 0xD1;
			int16_t tmp = OFToBigEndian16((int16_t)value);

			data = [OFMutableData dataWithCapacity: 3];


			[data addItem: &type];
			[data addItems: &tmp count: sizeof(tmp)];

		} else if (value >= INT32_MIN && value <= INT32_MAX) {
			uint8_t type = 0xD2;
			int32_t tmp = OFToBigEndian32((int32_t)value);

			data = [OFMutableData dataWithCapacity: 5];


			[data addItem: &type];
			[data addItems: &tmp count: sizeof(tmp)];

		} else if (value >= INT64_MIN && value <= INT64_MAX) {
			uint8_t type = 0xD3;
			int64_t tmp = OFToBigEndian64((int64_t)value);

			data = [OFMutableData dataWithCapacity: 9];


			[data addItem: &type];
			[data addItems: &tmp count: sizeof(tmp)];

		} else
			@throw [OFOutOfRangeException exception];
	} else if (isUnsigned(self)) {
		unsigned long long value = self.unsignedLongLongValue;

		if (value <= 127) {
			uint8_t tmp = ((uint8_t)value & 0x7F);

			data = [OFMutableData dataWithItems: &tmp count: 1];

		} else if (value <= UINT8_MAX) {
			uint8_t type = 0xCC;
			uint8_t tmp = (uint8_t)value;

			data = [OFMutableData dataWithCapacity: 2];


			[data addItem: &type];
			[data addItem: &tmp];
		} else if (value <= UINT16_MAX) {
			uint8_t type = 0xCD;
			uint16_t tmp = OFToBigEndian16((uint16_t)value);

			data = [OFMutableData dataWithCapacity: 3];


			[data addItem: &type];
			[data addItems: &tmp count: sizeof(tmp)];

		} else if (value <= UINT32_MAX) {
			uint8_t type = 0xCE;
			uint32_t tmp = OFToBigEndian32((uint32_t)value);

			data = [OFMutableData dataWithCapacity: 5];


			[data addItem: &type];
			[data addItems: &tmp count: sizeof(tmp)];

		} else if (value <= UINT64_MAX) {
			uint8_t type = 0xCF;
			uint64_t tmp = OFToBigEndian64((uint64_t)value);

			data = [OFMutableData dataWithCapacity: 9];


			[data addItem: &type];
			[data addItems: &tmp count: sizeof(tmp)];

		} else
			@throw [OFOutOfRangeException exception];
	} else
		@throw [OFInvalidFormatException exception];

	[data makeImmutable];

	return data;
}
@end

Modified src/OFObject+KeyValueCoding.h from [9dc6563122] to [c0e7932d00].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFObject+KeyValueCoding.m from [907d6adb03] to [8a83d2aa9e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
		char *name;

		if ((keyLength = key.UTF8StringLength) < 1) {
			objc_autoreleasePoolPop(pool);
			return [self valueForUndefinedKey: key];
		}

		name = of_malloc(1, keyLength + 3);
		@try {
			memcpy(name, "is", 2);
			memcpy(name + 2, key.UTF8String, keyLength);
			name[keyLength + 2] = '\0';

			name[2] = of_ascii_toupper(name[2]);

			selector = sel_registerName(name);
		} @finally {
			of_free(name);
		}

		methodSignature = [self methodSignatureForSelector: selector];

		if (methodSignature == NULL) {
			objc_autoreleasePoolPop(pool);
			return [self valueForUndefinedKey: key];







|





|



|







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
		char *name;

		if ((keyLength = key.UTF8StringLength) < 1) {
			objc_autoreleasePoolPop(pool);
			return [self valueForUndefinedKey: key];
		}

		name = OFAllocMemory(keyLength + 3, 1);
		@try {
			memcpy(name, "is", 2);
			memcpy(name + 2, key.UTF8String, keyLength);
			name[keyLength + 2] = '\0';

			name[2] = OFASCIIToUpper(name[2]);

			selector = sel_registerName(name);
		} @finally {
			OFFreeMemory(name);
		}

		methodSignature = [self methodSignatureForSelector: selector];

		if (methodSignature == NULL) {
			objc_autoreleasePoolPop(pool);
			return [self valueForUndefinedKey: key];
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
	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (id)valueForUndefinedKey: (OFString *)key
{
	@throw [OFUndefinedKeyException exceptionWithObject: self
							key: key];
}

- (void)setValue: (id)value
	  forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();
	size_t keyLength;
	char *name;
	SEL selector;
	OFMethodSignature *methodSignature;
	const char *valueType;

	if ((keyLength = key.UTF8StringLength) < 1) {
		objc_autoreleasePoolPop(pool);
		[self	   setValue: value
		    forUndefinedKey: key];
		return;
	}

	name = of_malloc(1, keyLength + 5);
	@try {
		memcpy(name, "set", 3);
		memcpy(name + 3, key.UTF8String, keyLength);
		memcpy(name + keyLength + 3, ":", 2);

		name[3] = of_ascii_toupper(name[3]);

		selector = sel_registerName(name);
	} @finally {
		of_free(name);
	}

	methodSignature = [self methodSignatureForSelector: selector];

	if (methodSignature == nil ||
	    methodSignature.numberOfArguments != 3 ||
	    *methodSignature.methodReturnType != 'v' ||
	    *[methodSignature argumentTypeAtIndex: 0] != '@' ||
	    *[methodSignature argumentTypeAtIndex: 1] != ':') {
		objc_autoreleasePoolPop(pool);
		[self	   setValue: value
		    forUndefinedKey: key];
		return;
	}

	valueType = [methodSignature argumentTypeAtIndex: 2];

	if (*valueType != '@' && *valueType != '#' && value == nil) {
		objc_autoreleasePoolPop(pool);







|
<


|
<










<
|



|





|



|










<
|







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
	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (id)valueForUndefinedKey: (OFString *)key
{
	@throw [OFUndefinedKeyException exceptionWithObject: self key: key];

}

- (void)setValue: (id)value forKey: (OFString *)key

{
	void *pool = objc_autoreleasePoolPush();
	size_t keyLength;
	char *name;
	SEL selector;
	OFMethodSignature *methodSignature;
	const char *valueType;

	if ((keyLength = key.UTF8StringLength) < 1) {
		objc_autoreleasePoolPop(pool);

		[self setValue: value forUndefinedKey: key];
		return;
	}

	name = OFAllocMemory(keyLength + 5, 1);
	@try {
		memcpy(name, "set", 3);
		memcpy(name + 3, key.UTF8String, keyLength);
		memcpy(name + keyLength + 3, ":", 2);

		name[3] = OFASCIIToUpper(name[3]);

		selector = sel_registerName(name);
	} @finally {
		OFFreeMemory(name);
	}

	methodSignature = [self methodSignatureForSelector: selector];

	if (methodSignature == nil ||
	    methodSignature.numberOfArguments != 3 ||
	    *methodSignature.methodReturnType != 'v' ||
	    *[methodSignature argumentTypeAtIndex: 0] != '@' ||
	    *[methodSignature argumentTypeAtIndex: 1] != ':') {
		objc_autoreleasePoolPop(pool);

		[self setValue: value forUndefinedKey: key];
		return;
	}

	valueType = [methodSignature argumentTypeAtIndex: 2];

	if (*valueType != '@' && *valueType != '#' && value == nil) {
		objc_autoreleasePoolPop(pool);
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
	CASE('L', unsigned long, unsignedLongValue)
	CASE('Q', unsigned long long, unsignedLongLongValue)
	CASE('f', float, floatValue)
	CASE('d', double, doubleValue)
#undef CASE
	default:
		objc_autoreleasePoolPop(pool);
		[self	   setValue: value
		    forUndefinedKey: key];
		return;
	}

	objc_autoreleasePoolPop(pool);
}

- (void)setValue: (id)value
      forKeyPath: (OFString *)keyPath
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *keys = [keyPath componentsSeparatedByString: @"."];
	size_t keysCount = keys.count;
	id object = self;
	size_t i = 0;

	for (OFString *key in keys) {
		if (++i == keysCount)
			[object setValue: value
				  forKey: key];
		else
			object = [object valueForKey: key];
	}

	objc_autoreleasePoolPop(pool);
}

-  (void)setValue: (id)value
  forUndefinedKey: (OFString *)key
{
	@throw [OFUndefinedKeyException exceptionWithObject: self
							key: key
						      value: value];
}

- (void)setNilValueForKey: (OFString *)key
{
	@throw [OFInvalidArgumentException exception];
}
@end







<
|






|
<









|
<







|
<











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
	CASE('L', unsigned long, unsignedLongValue)
	CASE('Q', unsigned long long, unsignedLongLongValue)
	CASE('f', float, floatValue)
	CASE('d', double, doubleValue)
#undef CASE
	default:
		objc_autoreleasePoolPop(pool);

		[self setValue: value forUndefinedKey: key];
		return;
	}

	objc_autoreleasePoolPop(pool);
}

- (void)setValue: (id)value forKeyPath: (OFString *)keyPath

{
	void *pool = objc_autoreleasePoolPush();
	OFArray *keys = [keyPath componentsSeparatedByString: @"."];
	size_t keysCount = keys.count;
	id object = self;
	size_t i = 0;

	for (OFString *key in keys) {
		if (++i == keysCount)
			[object setValue: value forKey: key];

		else
			object = [object valueForKey: key];
	}

	objc_autoreleasePoolPop(pool);
}

-  (void)setValue: (id)value forUndefinedKey: (OFString *)key

{
	@throw [OFUndefinedKeyException exceptionWithObject: self
							key: key
						      value: value];
}

- (void)setNilValueForKey: (OFString *)key
{
	@throw [OFInvalidArgumentException exception];
}
@end

Modified src/OFObject+Serialization.h from [d3572770d9] to [ed19e260ae].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFObject+Serialization.m from [bd3824da74] to [0bfdaef011].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
		abort();
	}

	pool = objc_autoreleasePoolPush();
	element = ((id <OFSerialization>)self).XMLElementBySerializing;

	root = [OFXMLElement elementWithName: @"serialization"
				   namespace: OF_SERIALIZATION_NS];
	[root addAttributeWithName: @"version"
		       stringValue: @"1"];
	[root addChild: element];

	ret = [@"<?xml version='1.0' encoding='UTF-8'?>\n"
	    stringByAppendingString: [root XMLStringWithIndentation: 2]];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end







|
|
<












38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
56
57
58
		abort();
	}

	pool = objc_autoreleasePoolPush();
	element = ((id <OFSerialization>)self).XMLElementBySerializing;

	root = [OFXMLElement elementWithName: @"serialization"
				   namespace: OFSerializationNS];
	[root addAttributeWithName: @"version" stringValue: @"1"];

	[root addChild: element];

	ret = [@"<?xml version='1.0' encoding='UTF-8'?>\n"
	    stringByAppendingString: [root XMLStringWithIndentation: 2]];

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

Modified src/OFObject.h from [6ddaa1e48b] to [48eed47979].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#endif

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>

#include "block.h"
#include "macros.h"
#include "once.h"

/*
 * Some versions of MinGW require <winsock2.h> to be included before
 * <windows.h>. Do this here to make sure this is always done in the correct
 * order, even if another header includes just <windows.h>.
 */
#ifdef __MINGW32__







|
|
|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#endif

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>

#include "macros.h"

#include "OFOnce.h"

/*
 * Some versions of MinGW require <winsock2.h> to be included before
 * <windows.h>. Do this here to make sure this is always done in the correct
 * order, even if another header includes just <windows.h>.
 */
#ifdef __MINGW32__
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
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
/** @file */

/**
 * @brief A result of a comparison.
 */
typedef enum {
	/** The left object is smaller than the right */
	OF_ORDERED_ASCENDING = -1,
	/** Both objects are equal */
	OF_ORDERED_SAME = 0,
	/** The left object is bigger than the right */
	OF_ORDERED_DESCENDING = 1
} of_comparison_result_t;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A comparator to compare two objects.
 *
 * @param left The left object
 * @param right The right object
 * @return The order of the objects
 */
typedef of_comparison_result_t (^of_comparator_t)(id _Nonnull left,
    id _Nonnull right);
#endif

/**
 * @brief An enum for storing endianess.
 */
typedef enum {
	/** Most significant byte first (big endian) */
	OF_BYTE_ORDER_BIG_ENDIAN,
	/** Least significant byte first (little endian) */


	OF_BYTE_ORDER_LITTLE_ENDIAN




} of_byte_order_t;

/**
 * @struct of_range_t OFObject.h ObjFW/OFObject.h
 *
 * @brief A range.
 */
struct OF_BOXABLE of_range_t {
	/** The start of the range */
	size_t location;
	/** The length of the range */
	size_t length;
};
typedef struct of_range_t of_range_t;

/**
 * @brief Creates a new of_range_t.
 *
 * @param start The starting index of the range
 * @param length The length of the range
 * @return An of_range with the specified start and length
 */
static OF_INLINE of_range_t OF_CONST_FUNC
of_range(size_t start, size_t length)
{
	of_range_t range = { start, length };

	return range;
}

/**
 * @brief Returns whether the two ranges are equal.
 *
 * @param range1 The first range for the comparison
 * @param range2 The second range for the comparison
 * @return Whether the two ranges are equal
 */
static OF_INLINE bool
of_range_equal(of_range_t range1, of_range_t range2)
{
	if (range1.location != range2.location)
		return false;

	if (range1.length != range2.length)
		return false;

	return true;
}

/**
 * @brief A time interval in seconds.
 */
typedef double of_time_interval_t;

/**
 * @struct of_point_t OFObject.h ObjFW/OFObject.h
 *
 * @brief A point.
 */
struct OF_BOXABLE of_point_t {
	/** The x coordinate of the point */
	float x;
	/** The y coordinate of the point */
	float y;
};
typedef struct of_point_t of_point_t;

/**
 * @brief Creates a new of_point_t.
 *
 * @param x The x coordinate of the point
 * @param y The x coordinate of the point
 * @return An of_point_t with the specified coordinates
 */
static OF_INLINE of_point_t OF_CONST_FUNC
of_point(float x, float y)
{
	of_point_t point = { x, y };

	return point;
}

/**
 * @brief Returns whether the two points are equal.
 *
 * @param point1 The first point for the comparison
 * @param point2 The second point for the comparison
 * @return Whether the two points are equal
 */
static OF_INLINE bool
of_point_equal(of_point_t point1, of_point_t point2)
{
	if (point1.x != point2.x)
		return false;

	if (point1.y != point2.y)
		return false;

	return true;
}

/**
 * @struct of_dimension_t OFObject.h ObjFW/OFObject.h
 *
 * @brief A dimension.
 */
struct OF_BOXABLE of_dimension_t {
	/** The width of the dimension */
	float width;
	/** The height of the dimension */
	float height;
};
typedef struct of_dimension_t of_dimension_t;

/**
 * @brief Creates a new of_dimension_t.
 *
 * @param width The width of the dimension
 * @param height The height of the dimension
 * @return An of_dimension_t with the specified width and height
 */
static OF_INLINE of_dimension_t OF_CONST_FUNC
of_dimension(float width, float height)
{
	of_dimension_t dimension = { width, height };




















































	return dimension;
}

/**
 * @brief Returns whether the two dimensions are equal.
 *
 * @param dimension1 The first dimension for the comparison
 * @param dimension2 The second dimension for the comparison
 * @return Whether the two dimensions are equal
 */
static OF_INLINE bool
of_dimension_equal(of_dimension_t dimension1, of_dimension_t dimension2)
{
	if (dimension1.width != dimension2.width)
		return false;

	if (dimension1.height != dimension2.height)
		return false;

	return true;
}

/**
 * @struct of_rectangle_t OFObject.h ObjFW/OFObject.h
 *

 * @brief A rectangle.
 */
struct OF_BOXABLE of_rectangle_t {
	/** The point from where the rectangle originates */
	of_point_t origin;
	/** The size of the rectangle */

	of_dimension_t size;
};



typedef struct of_rectangle_t of_rectangle_t;



/**
 * @brief Creates a new of_rectangle_t.
 *
 * @param x The x coordinate of the top left corner of the rectangle
 * @param y The y coordinate of the top left corner of the rectangle
 * @param width The width of the rectangle
 * @param height The height of the rectangle
 * @return An of_rectangle_t with the specified origin and size
 */
static OF_INLINE of_rectangle_t OF_CONST_FUNC
of_rectangle(float x, float y, float width, float height)

{
	of_rectangle_t rectangle = {
		of_point(x, y),
		of_dimension(width, height)
	};

	return rectangle;
}

/**
 * @brief Returns whether the two rectangles are equal.
 *
 * @param rectangle1 The first rectangle for the comparison
 * @param rectangle2 The second rectangle for the comparison
 * @return Whether the two rectangles are equal
 */
static OF_INLINE bool
of_rectangle_equal(of_rectangle_t rectangle1, of_rectangle_t rectangle2)

{

	if (!of_point_equal(rectangle1.origin, rectangle2.origin))
		return false;




	if (!of_dimension_equal(rectangle1.size, rectangle2.size))
		return false;


	return true;
}


#ifdef __OBJC__
@class OFMethodSignature;
@class OFString;
@class OFThread;

/**







|

|

|
|









<
|



|



|

>
>
|
>
>
>
>
|


|



|




<
|


|



|

|
|

|












|













|


|



|




<
|


|



|

|
|

|












|











|

|

|
|

|

|
<


|

|
|
|

|
|

|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|



|

|
|
|


|

|


|






|

>
|

|
<
|
<
>
|
|
>
>
>
|
>
|
>

|

<
<
|
|
<

|
<
>

|
|
|
<
|
<



|

|
<
<

|
<
>

>
|
<
>
>
>

<
<
>
|
<
|
>







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

/**
 * @brief A result of a comparison.
 */
typedef enum {
	/** The left object is smaller than the right */
	OFOrderedAscending = -1,
	/** Both objects are equal */
	OFOrderedSame = 0,
	/** The left object is bigger than the right */
	OFOrderedDescending = 1
} OFComparisonResult;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A comparator to compare two objects.
 *
 * @param left The left object
 * @param right The right object
 * @return The order of the objects
 */

typedef OFComparisonResult (^OFComparator)(id _Nonnull left, id _Nonnull right);
#endif

/**
 * @brief An enum for representing endianess.
 */
typedef enum {
	/** Most significant byte first (big endian) */
	OFByteOrderBigEndian,
	/** Least significant byte first (little endian) */
	OFByteOrderLittleEndian,
	/** Native byte order of the system */
#ifdef OF_BIG_ENDIAN
	OFByteOrderNative = OFByteOrderBigEndian
#else
	OFByteOrderNative = OFByteOrderLittleEndian
#endif
} OFByteOrder;

/**
 * @struct OFRange OFObject.h ObjFW/OFObject.h
 *
 * @brief A range.
 */
typedef struct OF_BOXABLE {
	/** The start of the range */
	size_t location;
	/** The length of the range */
	size_t length;

} OFRange;

/**
 * @brief Creates a new OFRange.
 *
 * @param start The starting index of the range
 * @param length The length of the range
 * @return An OFRangeith the specified start and length
 */
static OF_INLINE OFRange OF_CONST_FUNC
OFRangeMake(size_t start, size_t length)
{
	OFRange range = { start, length };

	return range;
}

/**
 * @brief Returns whether the two ranges are equal.
 *
 * @param range1 The first range for the comparison
 * @param range2 The second range for the comparison
 * @return Whether the two ranges are equal
 */
static OF_INLINE bool
OFRangeEqual(OFRange range1, OFRange range2)
{
	if (range1.location != range2.location)
		return false;

	if (range1.length != range2.length)
		return false;

	return true;
}

/**
 * @brief A time interval in seconds.
 */
typedef double OFTimeInterval;

/**
 * @struct OFPoint OFObject.h ObjFW/OFObject.h
 *
 * @brief A point.
 */
typedef struct OF_BOXABLE {
	/** The x coordinate of the point */
	float x;
	/** The y coordinate of the point */
	float y;

} OFPoint;

/**
 * @brief Creates a new OFPoint.
 *
 * @param x The x coordinate of the point
 * @param y The x coordinate of the point
 * @return An OFPoint with the specified coordinates
 */
static OF_INLINE OFPoint OF_CONST_FUNC
OFPointMake(float x, float y)
{
	OFPoint point = { x, y };

	return point;
}

/**
 * @brief Returns whether the two points are equal.
 *
 * @param point1 The first point for the comparison
 * @param point2 The second point for the comparison
 * @return Whether the two points are equal
 */
static OF_INLINE bool
OFPointEqual(OFPoint point1, OFPoint point2)
{
	if (point1.x != point2.x)
		return false;

	if (point1.y != point2.y)
		return false;

	return true;
}

/**
 * @struct OFSize OFObject.h ObjFW/OFObject.h
 *
 * @brief A size.
 */
typedef struct OF_BOXABLE {
	/** The width of the size */
	float width;
	/** The height of the size */
	float height;
} OFSize;


/**
 * @brief Creates a new OFSize.
 *
 * @param width The width of the size
 * @param height The height of the size
 * @return An OFSize with the specified width and height
 */
static OF_INLINE OFSize OF_CONST_FUNC
OFSizeMake(float width, float height)
{
	OFSize size = { width, height };

	return size;
}

/**
 * @brief Returns whether the two sizes are equal.
 *
 * @param size1 The first size for the comparison
 * @param size2 The second size for the comparison
 * @return Whether the two sizes are equal
 */
static OF_INLINE bool
OFSizeEqual(OFSize size1, OFSize size2)
{
	if (size1.width != size2.width)
		return false;

	if (size1.height != size2.height)
		return false;

	return true;
}

/**
 * @struct OFRect OFObject.h ObjFW/OFObject.h
 *
 * @brief A rectangle.
 */
typedef struct OF_BOXABLE {
	/** The point from where the rectangle originates */
	OFPoint origin;
	/** The size of the rectangle */
	OFSize size;
} OFRect;

/**
 * @brief Creates a new OFRect.
 *
 * @param x The x coordinate of the top left corner of the rectangle
 * @param y The y coordinate of the top left corner of the rectangle
 * @param width The width of the rectangle
 * @param height The height of the rectangle
 * @return An OFRect with the specified origin and size
 */
static OF_INLINE OFRect OF_CONST_FUNC
OFRectMake(float x, float y, float width, float height)
{
	OFRect rect = {
		OFPointMake(x, y),
		OFSizeMake(width, height)
	};

	return rect;
}

/**
 * @brief Returns whether the two rectangles are equal.
 *
 * @param rect1 The first rectangle for the comparison
 * @param rect2 The second rectangle for the comparison
 * @return Whether the two rectangles are equal
 */
static OF_INLINE bool
OFRectEqual(OFRect rect1, OFRect rect2)
{
	if (!OFPointEqual(rect1.origin, rect2.origin))
		return false;

	if (!OFSizeEqual(rect1.size, rect2.size))
		return false;

	return true;
}

/**
 * @brief Adds the specified byte to the hash.
 *
 * @param hash A pointer to a hash to add the byte to
 * @param byte The byte to add to the hash
 */
static OF_INLINE void

OFHashAdd(unsigned long *_Nonnull hash, unsigned char byte)

{
	uint32_t tmp = (uint32_t)*hash;

	tmp += byte;
	tmp += tmp << 10;
	tmp ^= tmp >> 6;

	*hash = tmp;
}

/**
 * @brief Adds the specified hash to the hash.
 *


 * @param hash A pointer to a hash to add the hash to
 * @param otherHash The hash to add to the hash

 */
static OF_INLINE void

OFHashAddHash(unsigned long *_Nonnull hash, unsigned long otherHash)
{
	OFHashAdd(hash, (otherHash >> 24) & 0xFF);
	OFHashAdd(hash, (otherHash >> 16) & 0xFF);
	OFHashAdd(hash, (otherHash >>  8) & 0xFF);

	OFHashAdd(hash, otherHash & 0xFF);

}

/**
 * @brief Finalizes the specified hash.
 *
 * @param hash A pointer to the hash to finalize


 */
static OF_INLINE void

OFHashFinalize(unsigned long *_Nonnull hash)
{
	uint32_t tmp = (uint32_t)*hash;


	tmp += tmp << 3;
	tmp ^= tmp >> 11;
	tmp += tmp << 15;



	*hash = tmp;
}


static const size_t OFNotFound = SIZE_MAX;

#ifdef __OBJC__
@class OFMethodSignature;
@class OFString;
@class OFThread;

/**
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
 * @brief Performs the specified selector with the specified object.
 *
 * @param selector The selector to perform
 * @param object The object that is passed to the method specified by the
 *		 selector
 * @return The object returned by the method specified by the selector
 */
- (nullable id)performSelector: (SEL)selector
		    withObject: (nullable id)object;

/**
 * @brief Performs the specified selector with the specified objects.
 *
 * @param selector The selector to perform
 * @param object1 The first object that is passed to the method specified by the
 *		  selector







|
<







450
451
452
453
454
455
456
457

458
459
460
461
462
463
464
 * @brief Performs the specified selector with the specified object.
 *
 * @param selector The selector to perform
 * @param object The object that is passed to the method specified by the
 *		 selector
 * @return The object returned by the method specified by the selector
 */
- (nullable id)performSelector: (SEL)selector withObject: (nullable id)object;


/**
 * @brief Performs the specified selector with the specified objects.
 *
 * @param selector The selector to perform
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
@property (class, readonly, nonatomic, getter=class) Class class_;
#  endif
@property (class, readonly, nonatomic) OFString *className;
@property (class, readonly, nullable, nonatomic) Class superclass;
@property (class, readonly, nonatomic) OFString *description;
# endif

# ifdef __cplusplus
@property (readonly, nonatomic) Class class;
# else
@property (readonly, nonatomic, getter=class) Class class_;
#endif
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) Class superclass;
@property (readonly, nonatomic) unsigned long hash;
@property (readonly, nonatomic) unsigned int retainCount;







|







585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
@property (class, readonly, nonatomic, getter=class) Class class_;
#  endif
@property (class, readonly, nonatomic) OFString *className;
@property (class, readonly, nullable, nonatomic) Class superclass;
@property (class, readonly, nonatomic) OFString *description;
# endif

# ifndef __cplusplus
@property (readonly, nonatomic) Class class;
# else
@property (readonly, nonatomic, getter=class) Class class_;
#endif
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) Class superclass;
@property (readonly, nonatomic) unsigned long hash;
@property (readonly, nonatomic) unsigned int retainCount;
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
/**
 * @brief A method which is called when the class is unloaded from the runtime.
 *
 * Derived classes can override this to execute their own code when the class
 * is unloaded.
 *
 * @warning This is not supported by the Apple runtime and currently only
 *	    called by the ObjFW runtime when objc_unregister_class() or
 *	    objc_exit() has been called!
 *	    In the future, this might also be called by the ObjFW runtime when
 *	    the class is part of a plugin that has been unloaded.
 */
+ (void)unload;

/**
 * @brief A method which is called the moment before the first call to the class
 *	  is being made.
 *







|
<

|







625
626
627
628
629
630
631
632

633
634
635
636
637
638
639
640
641
/**
 * @brief A method which is called when the class is unloaded from the runtime.
 *
 * Derived classes can override this to execute their own code when the class
 * is unloaded.
 *
 * @warning This is not supported by the Apple runtime and currently only
 *	    called by the ObjFW runtime when @ref objc_deinit has been called!

 *	    In the future, this might also be called by the ObjFW runtime when
 *	    the class is part of a plugin that is being unloaded.
 */
+ (void)unload;

/**
 * @brief A method which is called the moment before the first call to the class
 *	  is being made.
 *
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
 *
 * @param selector The selector for which the method signature should be
 *		   returned
 * @return The method signature for the specified selector
 */
- (nullable OFMethodSignature *)methodSignatureForSelector: (SEL)selector;

/**
 * @brief Allocates memory and stores it in the object's memory pool.
 *
 * It will be free'd automatically when the object is deallocated.
 *
 * @param size The size of the memory to allocate
 * @return A pointer to the allocated memory. May return NULL if the specified
 *	   size is 0.
 */
- (nullable void *)allocMemoryWithSize: (size_t)size OF_WARN_UNUSED_RESULT;

/**
 * @brief Allocates memory for the specified number of items and stores it in
 *	  the object's memory pool.
 *
 * It will be free'd automatically when the object is deallocated.
 *
 * @param size The size of each item to allocate
 * @param count The number of items to allocate
 * @return A pointer to the allocated memory. May return NULL if the specified
 *	   size or count is 0.
 */
- (nullable void *)allocMemoryWithSize: (size_t)size
				 count: (size_t)count OF_WARN_UNUSED_RESULT;

/**
 * @brief Allocates memory, initializes it with zeros and stores it in the
 *	  object's memory pool.
 *
 * It will be free'd automatically when the object is deallocated.
 *
 * @param size The size of the memory to allocate
 * @return A pointer to the allocated memory. May return NULL if the specified
 *	   size is 0.
 */
- (nullable void *)allocZeroedMemoryWithSize: (size_t)size
    OF_WARN_UNUSED_RESULT;

/**
 * @brief Allocates memory for the specified number of items, initializes it
 *	  with zeros and stores it in the object's memory pool.
 *
 * It will be free'd automatically when the object is deallocated.
 *
 * @param size The size of each item to allocate
 * @param count The number of items to allocate
 * @return A pointer to the allocated memory. May return NULL if the specified
 *	   size or count is 0.
 */
- (nullable void *)allocZeroedMemoryWithSize: (size_t)size
				       count: (size_t)count
    OF_WARN_UNUSED_RESULT;

/**
 * @brief Resizes memory in the object's memory pool to the specified size.
 *
 * If the pointer is NULL, this is equivalent to allocating memory.
 * If the size is 0, this is equivalent to freeing memory.
 *
 * @param pointer A pointer to the already allocated memory
 * @param size The new size for the memory chunk
 * @return A pointer to the resized memory chunk
 */
- (nullable void *)resizeMemory: (nullable void *)pointer
			   size: (size_t)size OF_WARN_UNUSED_RESULT;

/**
 * @brief Resizes memory in the object's memory pool to the specific number of
 *	  items of the specified size.
 *
 * If the pointer is NULL, this is equivalent to allocating memory.
 * If the size or number of items is 0, this is equivalent to freeing memory.
 *
 * @param pointer A pointer to the already allocated memory
 * @param size The size of each item to resize to
 * @param count The number of items to resize to
 * @return A pointer to the resized memory chunk
 */
- (nullable void *)resizeMemory: (nullable void *)pointer
			   size: (size_t)size
			  count: (size_t)count OF_WARN_UNUSED_RESULT;

/**
 * @brief Frees allocated memory and removes it from the object's memory pool.
 *
 * Does nothing if the pointer is NULL.
 *
 * @param pointer A pointer to the allocated memory
 */
- (void)freeMemory: (nullable void *)pointer;

/**
 * @brief Deallocates the object.
 *
 * It is automatically called when the retain count reaches zero.
 *
 * This also frees all memory in its memory pool.
 */
- (void)dealloc;

/**
 * @brief Performs the specified selector after the specified delay.
 *
 * @param selector The selector to perform
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     afterDelay: (of_time_interval_t)delay;

/**
 * @brief Performs the specified selector with the specified object after the
 *	  specified delay.
 *
 * @param selector The selector to perform
 * @param object The object that is passed to the method specified by the
 *		 selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     withObject: (nullable id)object
	     afterDelay: (of_time_interval_t)delay;

/**
 * @brief Performs the specified selector with the specified objects after the
 *	  specified delay.
 *
 * @param selector The selector to perform
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
 * @param object2 The second object that is passed to the method specified by
 *		  the selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     afterDelay: (of_time_interval_t)delay;

/**
 * @brief Performs the specified selector with the specified objects after the
 *	  specified delay.
 *
 * @param selector The selector to perform
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
 * @param object2 The second object that is passed to the method specified by
 *		  the selector
 * @param object3 The third object that is passed to the method specified by the
 *		  selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     withObject: (nullable id)object3
	     afterDelay: (of_time_interval_t)delay;

/**
 * @brief Performs the specified selector with the specified objects after the
 *	  specified delay.
 *
 * @param selector The selector to perform
 * @param object1 The first object that is passed to the method specified by the







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















|
<












|















|


















|







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
 *
 * @param selector The selector for which the method signature should be
 *		   returned
 * @return The method signature for the specified selector
 */
- (nullable OFMethodSignature *)methodSignatureForSelector: (SEL)selector;




























































































/**
 * @brief Deallocates the object.
 *
 * It is automatically called when the retain count reaches zero.
 *
 * This also frees all memory in its memory pool.
 */
- (void)dealloc;

/**
 * @brief Performs the specified selector after the specified delay.
 *
 * @param selector The selector to perform
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector afterDelay: (OFTimeInterval)delay;


/**
 * @brief Performs the specified selector with the specified object after the
 *	  specified delay.
 *
 * @param selector The selector to perform
 * @param object The object that is passed to the method specified by the
 *		 selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     withObject: (nullable id)object
	     afterDelay: (OFTimeInterval)delay;

/**
 * @brief Performs the specified selector with the specified objects after the
 *	  specified delay.
 *
 * @param selector The selector to perform
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
 * @param object2 The second object that is passed to the method specified by
 *		  the selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     afterDelay: (OFTimeInterval)delay;

/**
 * @brief Performs the specified selector with the specified objects after the
 *	  specified delay.
 *
 * @param selector The selector to perform
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
 * @param object2 The second object that is passed to the method specified by
 *		  the selector
 * @param object3 The third object that is passed to the method specified by the
 *		  selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     withObject: (nullable id)object3
	     afterDelay: (OFTimeInterval)delay;

/**
 * @brief Performs the specified selector with the specified objects after the
 *	  specified delay.
 *
 * @param selector The selector to perform
 * @param object1 The first object that is passed to the method specified by the
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     withObject: (nullable id)object3
	     withObject: (nullable id)object4
	     afterDelay: (of_time_interval_t)delay;

# ifdef OF_HAVE_THREADS
/**
 * @brief Performs the specified selector on the specified thread.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector







|







936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     withObject: (nullable id)object3
	     withObject: (nullable id)object4
	     afterDelay: (OFTimeInterval)delay;

# ifdef OF_HAVE_THREADS
/**
 * @brief Performs the specified selector on the specified thread.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
1223
1224
1225
1226
1227
1228
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     afterDelay: (of_time_interval_t)delay;

/**
 * @brief Performs the specified selector on the specified thread with the
 *	  specified object after the specified delay.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
 * @param object The object that is passed to the method specified by the
 *		 selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (nullable id)object
	     afterDelay: (of_time_interval_t)delay;

/**
 * @brief Performs the specified selector on the specified thread with the
 *	  specified objects after the specified delay.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
 * @param object2 The second object that is passed to the method specified by
 *		  the selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     afterDelay: (of_time_interval_t)delay;

/**
 * @brief Performs the specified selector on the specified thread with the
 *	  specified objects after the specified delay.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
 * @param object2 The second object that is passed to the method specified by
 *		  the selector
 * @param object3 The third object that is passed to the method specified by the
 *		  selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     withObject: (nullable id)object3
	     afterDelay: (of_time_interval_t)delay;

/**
 * @brief Performs the specified selector on the specified thread with the
 *	  specified objects after the specified delay.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector







|














|

















|




















|







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
1182
1183
1184
1185
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     afterDelay: (OFTimeInterval)delay;

/**
 * @brief Performs the specified selector on the specified thread with the
 *	  specified object after the specified delay.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
 * @param object The object that is passed to the method specified by the
 *		 selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (nullable id)object
	     afterDelay: (OFTimeInterval)delay;

/**
 * @brief Performs the specified selector on the specified thread with the
 *	  specified objects after the specified delay.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
 * @param object2 The second object that is passed to the method specified by
 *		  the selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     afterDelay: (OFTimeInterval)delay;

/**
 * @brief Performs the specified selector on the specified thread with the
 *	  specified objects after the specified delay.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
 * @param object1 The first object that is passed to the method specified by the
 *		  selector
 * @param object2 The second object that is passed to the method specified by
 *		  the selector
 * @param object3 The third object that is passed to the method specified by the
 *		  selector
 * @param delay The delay after which the selector will be performed
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     withObject: (nullable id)object3
	     afterDelay: (OFTimeInterval)delay;

/**
 * @brief Performs the specified selector on the specified thread with the
 *	  specified objects after the specified delay.
 *
 * @param selector The selector to perform
 * @param thread The thread on which to perform the selector
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     withObject: (nullable id)object3
	     withObject: (nullable id)object4
	     afterDelay: (of_time_interval_t)delay;
# endif

/**
 * @brief This method is called when @ref resolveClassMethod: or
 *	  @ref resolveInstanceMethod: returned false. It should return a target
 *	  to which the message should be forwarded.
 *







|







1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
 */
- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (nullable id)object1
	     withObject: (nullable id)object2
	     withObject: (nullable id)object3
	     withObject: (nullable id)object4
	     afterDelay: (OFTimeInterval)delay;
# endif

/**
 * @brief This method is called when @ref resolveClassMethod: or
 *	  @ref resolveInstanceMethod: returned false. It should return a target
 *	  to which the message should be forwarded.
 *
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
@end

/**
 * @protocol OFComparing OFObject.h ObjFW/OFObject.h
 *
 * @brief A protocol for comparing objects.
 *
 * This protocol is implemented by objects that can be compared.
 */
@protocol OFComparing
/**
 * @brief Compares the object with another object.
 *
 * @param object An object to compare the object to
 * @return The result of the comparison
 */
- (of_comparison_result_t)compare: (id <OFComparing>)object;
@end
#endif

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Allocates memory for the specified number of items.



 *
 * Throws @ref OFOutOfMemoryException if allocating failed and
 * @ref OFOutOfRangeException if the requested size exceeds the address space.
 *
 * @param count The number of items to allocate
 * @param size The size of each item to allocate
 * @return A pointer to the allocated memory. May return NULL if the specified
 *	   size or count is 0.
 */
extern void *_Nullable of_malloc(size_t count, size_t size)
    OF_WARN_UNUSED_RESULT;

/**
 * @brief Allocates memory for the specified number of items and initializes it
 *	  with zeros.


 *
 * Throws @ref OFOutOfMemoryException if allocating failed and
 * @ref OFOutOfRangeException if the requested size exceeds the address space.
 *
 * @param size The size of each item to allocate
 * @param count The number of items to allocate
 * @return A pointer to the allocated memory. May return NULL if the specified
 *	   size or count is 0.
 */
extern void *_Nullable of_calloc(size_t count, size_t size)
    OF_WARN_UNUSED_RESULT;

/**
 * @brief Resizes memory to the specific number of items of the specified size.


 *
 * If the pointer is NULL, this is equivalent to allocating memory.
 * If the size or number of items is 0, this is equivalent to freeing memory.
 *
 * Throws @ref OFOutOfMemoryException if allocating failed and
 * @ref OFOutOfRangeException if the requested size exceeds the address space.
 *
 * @param pointer A pointer to the already allocated memory
 * @param size The size of each item to resize to
 * @param count The number of items to resize to
 * @return A pointer to the resized memory chunk
 */
extern void *_Nullable of_realloc(void *_Nullable pointer, size_t count,
    size_t size) OF_WARN_UNUSED_RESULT;

/**
 * @brief Frees allocated memory.
 *
 * Does nothing if the pointer is NULL.
 *
 * @param pointer A pointer to the allocated memory

 */
extern void of_free(void *_Nullable pointer);

#ifdef OF_APPLE_RUNTIME
extern void *_Null_unspecified objc_autoreleasePoolPush(void);
extern void objc_autoreleasePoolPop(void *_Null_unspecified pool);
# ifndef __OBJC2__
extern id _Nullable objc_constructInstance(Class _Nullable class_,
    void *_Nullable bytes);
extern void *_Nullable objc_destructInstance(id _Nullable object);
# endif
#endif
extern id of_alloc_object(Class class_, size_t extraSize,
    size_t extraAlignment, void *_Nullable *_Nullable extra);
extern void OF_NO_RETURN_FUNC of_method_not_found(id self, SEL _cmd);
extern uint32_t of_hash_seed;



/* These do *NOT* provide cryptographically secure randomness! */

extern uint16_t of_random16(void);






extern uint32_t of_random32(void);






extern uint64_t of_random64(void);







#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END



#ifdef __OBJC__
# import "OFObject+KeyValueCoding.h"
# import "OFObject+Serialization.h"
#endif

#endif







|



|




|







|
>
>
>









|



|
|
>
>









|



|
>
>












|



|
|
<

|
>

|










|
|
|
|
>
>
>
|
>
|
>
>
>
>
>
>
|
>
>
>
>
>
>
|
>
>
>
>
>
>
>





>
>







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
@end

/**
 * @protocol OFComparing OFObject.h ObjFW/OFObject.h
 *
 * @brief A protocol for comparing objects.
 *
 * This protocol is implemented by objects that can be compared. Its only method, @ref compare:, should be overridden with a stronger type.
 */
@protocol OFComparing
/**
 * @brief Compares the object to another object.
 *
 * @param object An object to compare the object to
 * @return The result of the comparison
 */
- (OFComparisonResult)compare: (id <OFComparing>)object;
@end
#endif

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Allocates memory for the specified number of items of the specified
 *	  size.
 *
 * To free the allocated memory, use @ref OFFreeMemory.
 *
 * Throws @ref OFOutOfMemoryException if allocating failed and
 * @ref OFOutOfRangeException if the requested size exceeds the address space.
 *
 * @param count The number of items to allocate
 * @param size The size of each item to allocate
 * @return A pointer to the allocated memory. May return NULL if the specified
 *	   size or count is 0.
 */
extern void *_Nullable OFAllocMemory(size_t count, size_t size)
    OF_WARN_UNUSED_RESULT;

/**
 * @brief Allocates memory for the specified number of items of the specified
 *	  size and initializes it with zeros.
 *
 * To free the allocated memory, use @ref OFFreeMemory.
 *
 * Throws @ref OFOutOfMemoryException if allocating failed and
 * @ref OFOutOfRangeException if the requested size exceeds the address space.
 *
 * @param size The size of each item to allocate
 * @param count The number of items to allocate
 * @return A pointer to the allocated memory. May return NULL if the specified
 *	   size or count is 0.
 */
extern void *_Nullable OFAllocZeroedMemory(size_t count, size_t size)
    OF_WARN_UNUSED_RESULT;

/**
 * @brief Resizes memory to the specified number of items of the specified size.
 *
 * To free the allocated memory, use @ref OFFreeMemory.
 *
 * If the pointer is NULL, this is equivalent to allocating memory.
 * If the size or number of items is 0, this is equivalent to freeing memory.
 *
 * Throws @ref OFOutOfMemoryException if allocating failed and
 * @ref OFOutOfRangeException if the requested size exceeds the address space.
 *
 * @param pointer A pointer to the already allocated memory
 * @param size The size of each item to resize to
 * @param count The number of items to resize to
 * @return A pointer to the resized memory chunk
 */
extern void *_Nullable OFResizeMemory(void *_Nullable pointer, size_t count,
    size_t size) OF_WARN_UNUSED_RESULT;

/**
 * @brief Frees memory allocated by @ref OFAllocMemory, @ref OFAllocZeroedMemory
 *	  or @ref OFResizeMemory.

 *
 * @param pointer A pointer to the memory to free or nil (passing nil ooes
 *		  nothing)
 */
extern void OFFreeMemory(void *_Nullable pointer);

#ifdef OF_APPLE_RUNTIME
extern void *_Null_unspecified objc_autoreleasePoolPush(void);
extern void objc_autoreleasePoolPop(void *_Null_unspecified pool);
# ifndef __OBJC2__
extern id _Nullable objc_constructInstance(Class _Nullable class_,
    void *_Nullable bytes);
extern void *_Nullable objc_destructInstance(id _Nullable object);
# endif
#endif
extern id OFAllocObject(Class class_, size_t extraSize, size_t extraAlignment,
    void *_Nullable *_Nullable extra);
extern void OF_NO_RETURN_FUNC OFMethodNotFound(id self, SEL _cmd);

/**
 * @brief Returns 16 bit or non-cryptographical randomness.
 *
 * @return 16 bit or non-cryptographical randomness
 */
extern uint16_t OFRandom16(void);

/**
 * @brief Returns 32 bit or non-cryptographical randomness.
 *
 * @return 32 bit or non-cryptographical randomness
 */
extern uint32_t OFRandom32(void);

/**
 * @brief Returns 64 bit or non-cryptographical randomness.
 *
 * @return 64 bit or non-cryptographical randomness
 */
extern uint64_t OFRandom64(void);

/**
 * @brief Initializes the specified hash.
 *
 * @param hash A pointer to the hash to initialize
 */
extern void OFHashInit(unsigned long *_Nonnull hash);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#include "OFBlock.h"

#ifdef __OBJC__
# import "OFObject+KeyValueCoding.h"
# import "OFObject+Serialization.h"
#endif

#endif

Modified src/OFObject.m from [133c2d9091] to [fe35d606d6].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdio.h>
#include <stdlib.h>
#include <string.h>


#include <assert.h>

#ifdef OF_APPLE_RUNTIME
# include <dlfcn.h>
#endif

#ifdef HAVE_GETRANDOM
# include <sys/random.h>
#endif

#import "OFObject.h"
#import "OFArray.h"



#import "OFLocale.h"
#import "OFMethodSignature.h"
#import "OFRunLoop.h"




#import "OFThread.h"
#import "OFTimer.h"

#import "OFAllocFailedException.h"
#import "OFEnumerationMutationException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFMemoryNotPartOfObjectException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#if defined(OF_APPLE_RUNTIME) && __OBJC2__
# import <objc/objc-exception.h>
#elif defined(OF_OBJFW_RUNTIME)
# import "ObjFWRT.h"
#endif

#ifdef OF_WINDOWS
# include <windows.h>
#endif

#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif

#import "OFString.h"

#if defined(OF_HAVE_ATOMIC_OPS)
# import "atomic.h"
#elif defined(OF_HAVE_THREADS)
# import "mutex.h"
#endif

#ifdef OF_APPLE_RUNTIME
extern id _Nullable _objc_rootAutorelease(id _Nullable object);
#endif
#if defined(OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR)
extern id of_forward(id, SEL, ...);
extern struct stret of_forward_stret(id, SEL, ...);
#else
# define of_forward of_method_not_found
# define of_forward_stret of_method_not_found_stret
#endif

struct pre_ivar {
	int retainCount;
#if !defined(OF_HAVE_ATOMIC_OPS) && !defined(OF_AMIGAOS)
	of_spinlock_t retainCountSpinlock;
#endif
	struct pre_mem *firstMem, *lastMem;
};

struct pre_mem {
	struct pre_mem *prev, *next;
	id owner;
};

#define PRE_IVARS_ALIGN ((sizeof(struct pre_ivar) + \
    (OF_BIGGEST_ALIGNMENT - 1)) & ~(OF_BIGGEST_ALIGNMENT - 1))
#define PRE_IVARS ((struct pre_ivar *)(void *)((char *)self - PRE_IVARS_ALIGN))

#define PRE_MEM_ALIGN ((sizeof(struct pre_mem) + \
    (OF_BIGGEST_ALIGNMENT - 1)) & ~(OF_BIGGEST_ALIGNMENT - 1))
#define PRE_MEM(mem) ((struct pre_mem *)(void *)((char *)mem - PRE_MEM_ALIGN))

static struct {
	Class isa;
} allocFailedException;

uint32_t of_hash_seed;

void *
of_malloc(size_t count, size_t size)
{
	void *pointer;

	if OF_UNLIKELY (count == 0 || size == 0)
		return NULL;

	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	if OF_UNLIKELY ((pointer = malloc(count * size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];

	return pointer;
}

void *
of_calloc(size_t count, size_t size)
{
	void *pointer;

	if OF_UNLIKELY (count == 0 || size == 0)
		return NULL;

	/* Not all calloc implementations check for overflow. */
	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	if OF_UNLIKELY ((pointer = calloc(count, size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];

	return pointer;
}

void *
of_realloc(void *pointer, size_t count, size_t size)
{
	if OF_UNLIKELY (count == 0 || size == 0)

		return NULL;


	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	if OF_UNLIKELY ((pointer = realloc(pointer, count * size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];

	return pointer;
}

void
of_free(void *pointer)
{
	free(pointer);
}

#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_GETRANDOM)
static void
initRandom(void)

<
<
|


















>













>
>
>



>
>
>
>







<


















<
<
<
<
<
<
<
<




|
|

|
|


|


|

<


<
<
<
<
<
|

|
<
<
<
<





|


|

















|


















|

|
>

>












|







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


 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unistd_wrapper.h"

#include <assert.h>

#ifdef OF_APPLE_RUNTIME
# include <dlfcn.h>
#endif

#ifdef HAVE_GETRANDOM
# include <sys/random.h>
#endif

#import "OFObject.h"
#import "OFArray.h"
#ifdef OF_HAVE_ATOMIC_OPS
# import "OFAtomic.h"
#endif
#import "OFLocale.h"
#import "OFMethodSignature.h"
#import "OFRunLoop.h"
#if !defined(OF_HAVE_ATOMIC_OPS) && defined(OF_HAVE_THREADS)
# import "OFPlainMutex.h"	/* For OFSpinlock */
#endif
#import "OFString.h"
#import "OFThread.h"
#import "OFTimer.h"

#import "OFAllocFailedException.h"
#import "OFEnumerationMutationException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"

#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#if defined(OF_APPLE_RUNTIME) && __OBJC2__
# import <objc/objc-exception.h>
#elif defined(OF_OBJFW_RUNTIME)
# import "ObjFWRT.h"
#endif

#ifdef OF_WINDOWS
# include <windows.h>
#endif

#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif









#ifdef OF_APPLE_RUNTIME
extern id _Nullable _objc_rootAutorelease(id _Nullable object);
#endif
#if defined(OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR)
extern id OFForward(id, SEL, ...);
extern struct Stret OFForward_stret(id, SEL, ...);
#else
# define OFForward OFMethodNotFound
# define OFForward_stret OFMethodNotFound_stret
#endif

struct PreIvars {
	int retainCount;
#if !defined(OF_HAVE_ATOMIC_OPS) && !defined(OF_AMIGAOS)
	OFSpinlock retainCountSpinlock;
#endif

};






#define PRE_IVARS_ALIGN ((sizeof(struct PreIvars) + \
    (OF_BIGGEST_ALIGNMENT - 1)) & ~(OF_BIGGEST_ALIGNMENT - 1))
#define PRE_IVARS ((struct PreIvars *)(void *)((char *)self - PRE_IVARS_ALIGN))





static struct {
	Class isa;
} allocFailedException;

unsigned long OFHashSeed;

void *
OFAllocMemory(size_t count, size_t size)
{
	void *pointer;

	if OF_UNLIKELY (count == 0 || size == 0)
		return NULL;

	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	if OF_UNLIKELY ((pointer = malloc(count * size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];

	return pointer;
}

void *
OFAllocZeroedMemory(size_t count, size_t size)
{
	void *pointer;

	if OF_UNLIKELY (count == 0 || size == 0)
		return NULL;

	/* Not all calloc implementations check for overflow. */
	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	if OF_UNLIKELY ((pointer = calloc(count, size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];

	return pointer;
}

void *
OFResizeMemory(void *pointer, size_t count, size_t size)
{
	if OF_UNLIKELY (count == 0 || size == 0) {
		free(pointer);
		return NULL;
	}

	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	if OF_UNLIKELY ((pointer = realloc(pointer, count * size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];

	return pointer;
}

void
OFFreeMemory(void *pointer)
{
	free(pointer);
}

#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_GETRANDOM)
static void
initRandom(void)
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
	gettimeofday(&tv, NULL);
	srand((unsigned)(tv.tv_sec ^ tv.tv_usec));
# endif
}
#endif

uint16_t
of_random16(void)
{
#if defined(HAVE_ARC4RANDOM)
	return arc4random();
#elif defined(HAVE_GETRANDOM)
	uint16_t buffer;

	OF_ENSURE(getrandom(&buffer, sizeof(buffer), 0) == sizeof(buffer));

	return buffer;
#else
	static of_once_t onceControl = OF_ONCE_INIT;

	of_once(&onceControl, initRandom);
# ifdef HAVE_RANDOM
	return random() & 0xFFFF;
# else
	return rand() & 0xFFFF;
# endif
#endif
}

uint32_t
of_random32(void)
{
#if defined(HAVE_ARC4RANDOM)
	return arc4random();
#elif defined(HAVE_GETRANDOM)
	uint32_t buffer;

	OF_ENSURE(getrandom(&buffer, sizeof(buffer), 0) == sizeof(buffer));

	return buffer;
#else
	return ((uint32_t)of_random16() << 16) | of_random16();
#endif
}

uint64_t
of_random64(void)
{
#if defined(HAVE_ARC4RANDOM_BUF)
	uint64_t buffer;

	arc4random_buf(&buffer, sizeof(buffer));

	return buffer;
#elif defined(HAVE_GETRANDOM)
	uint64_t buffer;

	OF_ENSURE(getrandom(&buffer, sizeof(buffer), 0) == sizeof(buffer));

	return buffer;
#else
	return ((uint64_t)of_random32() << 32) | of_random32();
#endif
}







static const char *
typeEncodingForSelector(Class class, SEL selector)
{
	Method method;

	if ((method = class_getInstanceMethod(class, selector)) == NULL)
		return NULL;

	return method_getTypeEncoding(method);
}

#if !defined(OF_APPLE_RUNTIME) || defined(__OBJC2__)
static void
uncaughtExceptionHandler(id exception)
{
	OFString *description = [exception description];
	OFArray *backtrace = nil;
	of_string_encoding_t encoding = [OFLocale encoding];

	fprintf(stderr, "\nRuntime error: Unhandled exception:\n%s\n",
	    [description cStringWithEncoding: encoding]);

	if ([exception respondsToSelector: @selector(backtrace)])
		backtrace = [exception backtrace];








|






|



<
|
|









|






|



|




|










|



|


>
>
>
>
>
>


















|







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
	gettimeofday(&tv, NULL);
	srand((unsigned)(tv.tv_sec ^ tv.tv_usec));
# endif
}
#endif

uint16_t
OFRandom16(void)
{
#if defined(HAVE_ARC4RANDOM)
	return arc4random();
#elif defined(HAVE_GETRANDOM)
	uint16_t buffer;

	OFEnsure(getrandom(&buffer, sizeof(buffer), 0) == sizeof(buffer));

	return buffer;
#else

	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, initRandom);
# ifdef HAVE_RANDOM
	return random() & 0xFFFF;
# else
	return rand() & 0xFFFF;
# endif
#endif
}

uint32_t
OFRandom32(void)
{
#if defined(HAVE_ARC4RANDOM)
	return arc4random();
#elif defined(HAVE_GETRANDOM)
	uint32_t buffer;

	OFEnsure(getrandom(&buffer, sizeof(buffer), 0) == sizeof(buffer));

	return buffer;
#else
	return ((uint32_t)OFRandom16() << 16) | OFRandom16();
#endif
}

uint64_t
OFRandom64(void)
{
#if defined(HAVE_ARC4RANDOM_BUF)
	uint64_t buffer;

	arc4random_buf(&buffer, sizeof(buffer));

	return buffer;
#elif defined(HAVE_GETRANDOM)
	uint64_t buffer;

	OFEnsure(getrandom(&buffer, sizeof(buffer), 0) == sizeof(buffer));

	return buffer;
#else
	return ((uint64_t)OFRandom32() << 32) | OFRandom32();
#endif
}

void
OFHashInit(unsigned long *hash)
{
	*hash = OFHashSeed;
}

static const char *
typeEncodingForSelector(Class class, SEL selector)
{
	Method method;

	if ((method = class_getInstanceMethod(class, selector)) == NULL)
		return NULL;

	return method_getTypeEncoding(method);
}

#if !defined(OF_APPLE_RUNTIME) || defined(__OBJC2__)
static void
uncaughtExceptionHandler(id exception)
{
	OFString *description = [exception description];
	OFArray *backtrace = nil;
	OFStringEncoding encoding = [OFLocale encoding];

	fprintf(stderr, "\nRuntime error: Unhandled exception:\n%s\n",
	    [description cStringWithEncoding: encoding]);

	if ([exception respondsToSelector: @selector(backtrace)])
		backtrace = [exception backtrace];

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
static void
enumerationMutationHandler(id object)
{
	@throw [OFEnumerationMutationException exceptionWithObject: object];
}

void OF_NO_RETURN_FUNC
of_method_not_found(id object, SEL selector)
{
	[object doesNotRecognizeSelector: selector];

	/*
	 * Just in case doesNotRecognizeSelector: returned, even though it must
	 * never return.
	 */
	abort();

	OF_UNREACHABLE
}

void OF_NO_RETURN_FUNC
of_method_not_found_stret(void *stret, id object, SEL selector)
{
	of_method_not_found(object, selector);
}

id
of_alloc_object(Class class, size_t extraSize, size_t extraAlignment,
    void **extra)
{
	OFObject *instance;
	size_t instanceSize;

	instanceSize = class_getInstanceSize(class);

	if OF_UNLIKELY (extraAlignment > 1)
		extraAlignment = ((instanceSize + extraAlignment - 1) &
		    ~(extraAlignment - 1)) - extraAlignment;

	instance = calloc(1, PRE_IVARS_ALIGN + instanceSize +
	    extraAlignment + extraSize);

	if OF_UNLIKELY (instance == nil) {
		allocFailedException.isa = [OFAllocFailedException class];
		@throw (id)&allocFailedException;
	}

	((struct pre_ivar *)instance)->retainCount = 1;

#if !defined(OF_HAVE_ATOMIC_OPS) && !defined(OF_AMIGAOS)
	if OF_UNLIKELY (!of_spinlock_new(
	    &((struct pre_ivar *)instance)->retainCountSpinlock)) {
		free(instance);
		@throw [OFInitializationFailedException
		    exceptionWithClass: class];
	}
#endif

	instance = (OFObject *)(void *)((char *)instance + PRE_IVARS_ALIGN);







|













|

|



|



















|


|
|







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
static void
enumerationMutationHandler(id object)
{
	@throw [OFEnumerationMutationException exceptionWithObject: object];
}

void OF_NO_RETURN_FUNC
OFMethodNotFound(id object, SEL selector)
{
	[object doesNotRecognizeSelector: selector];

	/*
	 * Just in case doesNotRecognizeSelector: returned, even though it must
	 * never return.
	 */
	abort();

	OF_UNREACHABLE
}

void OF_NO_RETURN_FUNC
OFMethodNotFound_stret(void *stret, id object, SEL selector)
{
	OFMethodNotFound(object, selector);
}

id
OFAllocObject(Class class, size_t extraSize, size_t extraAlignment,
    void **extra)
{
	OFObject *instance;
	size_t instanceSize;

	instanceSize = class_getInstanceSize(class);

	if OF_UNLIKELY (extraAlignment > 1)
		extraAlignment = ((instanceSize + extraAlignment - 1) &
		    ~(extraAlignment - 1)) - extraAlignment;

	instance = calloc(1, PRE_IVARS_ALIGN + instanceSize +
	    extraAlignment + extraSize);

	if OF_UNLIKELY (instance == nil) {
		allocFailedException.isa = [OFAllocFailedException class];
		@throw (id)&allocFailedException;
	}

	((struct PreIvars *)instance)->retainCount = 1;

#if !defined(OF_HAVE_ATOMIC_OPS) && !defined(OF_AMIGAOS)
	if OF_UNLIKELY (OFSpinlockNew(
	    &((struct PreIvars *)instance)->retainCountSpinlock) != 0) {
		free(instance);
		@throw [OFInitializationFailedException
		    exceptionWithClass: class];
	}
#endif

	instance = (OFObject *)(void *)((char *)instance + PRE_IVARS_ALIGN);
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
	 * handler on load, we should not set ours, as this will break
	 * Foundation.
	 *
	 * Unfortunately, there is no way to check if a forward handler has
	 * already been set, so this is the best we can do.
	 */
	if (dlsym(RTLD_DEFAULT, "NSFoundationVersionNumber") == NULL)
		objc_setForwardHandler((void *)&of_forward,
		    (void *)&of_forward_stret);
#else
	objc_setForwardHandler((IMP)&of_forward, (IMP)&of_forward_stret);
#endif

	objc_setEnumerationMutationHandler(enumerationMutationHandler);

	do {
		of_hash_seed = of_random32();
	} while (of_hash_seed == 0);

#ifdef OF_OBJFW_RUNTIME
	objc_setTaggedPointerSecret(sizeof(uintptr_t) == 4
	    ? (uintptr_t)of_random32() : (uintptr_t)of_random64());
#endif
}

+ (void)unload
{
}

+ (void)initialize
{
}

+ (instancetype)alloc
{
	return of_alloc_object(self, 0, 0, NULL);
}

+ (instancetype)new
{
	return [[self alloc] init];
}

+ (Class)class
{
	return self;
}

+ (OFString *)className
{
	return [OFString stringWithCString: class_getName(self)
				  encoding: OF_STRING_ENCODING_ASCII];
}

+ (bool)isSubclassOfClass: (Class)class
{
	for (Class iter = self; iter != Nil; iter = class_getSuperclass(iter))
		if (iter == class)
			return true;







|
|

|





|
|



|













|















|







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
	 * handler on load, we should not set ours, as this will break
	 * Foundation.
	 *
	 * Unfortunately, there is no way to check if a forward handler has
	 * already been set, so this is the best we can do.
	 */
	if (dlsym(RTLD_DEFAULT, "NSFoundationVersionNumber") == NULL)
		objc_setForwardHandler((void *)&OFForward,
		    (void *)&OFForward_stret);
#else
	objc_setForwardHandler((IMP)&OFForward, (IMP)&OFForward_stret);
#endif

	objc_setEnumerationMutationHandler(enumerationMutationHandler);

	do {
		OFHashSeed = OFRandom32();
	} while (OFHashSeed == 0);

#ifdef OF_OBJFW_RUNTIME
	objc_setTaggedPointerSecret(sizeof(uintptr_t) == 4
	    ? (uintptr_t)OFRandom32() : (uintptr_t)OFRandom64());
#endif
}

+ (void)unload
{
}

+ (void)initialize
{
}

+ (instancetype)alloc
{
	return OFAllocObject(self, 0, 0, NULL);
}

+ (instancetype)new
{
	return [[self alloc] init];
}

+ (Class)class
{
	return self;
}

+ (OFString *)className
{
	return [OFString stringWithCString: class_getName(self)
				  encoding: OFStringEncodingASCII];
}

+ (bool)isSubclassOfClass: (Class)class
{
	for (Class iter = self; iter != Nil; iter = class_getSuperclass(iter))
		if (iter == class)
			return true;
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
}

+ (OFString *)description
{
	return [self className];
}

+ (IMP)replaceClassMethod: (SEL)selector
      withMethodFromClass: (Class)class
{
	IMP method = [class methodForSelector: selector];

	if (method == NULL)
		@throw [OFInvalidArgumentException exception];

	return class_replaceMethod(object_getClass(self), selector, method,
	    typeEncodingForSelector(object_getClass(class), selector));
}

+ (IMP)replaceInstanceMethod: (SEL)selector
	 withMethodFromClass: (Class)class
{
	IMP method = [class instanceMethodForSelector: selector];

	if (method == NULL)
		@throw [OFInvalidArgumentException exception];

	return class_replaceMethod(self, selector, method,







|
<










|
<







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
}

+ (OFString *)description
{
	return [self className];
}

+ (IMP)replaceClassMethod: (SEL)selector withMethodFromClass: (Class)class

{
	IMP method = [class methodForSelector: selector];

	if (method == NULL)
		@throw [OFInvalidArgumentException exception];

	return class_replaceMethod(object_getClass(self), selector, method,
	    typeEncodingForSelector(object_getClass(class), selector));
}

+ (IMP)replaceInstanceMethod: (SEL)selector withMethodFromClass: (Class)class

{
	IMP method = [class instanceMethodForSelector: selector];

	if (method == NULL)
		@throw [OFInvalidArgumentException exception];

	return class_replaceMethod(self, selector, method,
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
{
	return class_getSuperclass(object_getClass(self));
}

- (OFString *)className
{
	return [OFString stringWithCString: object_getClassName(self)
				  encoding: OF_STRING_ENCODING_ASCII];
}

- (bool)isKindOfClass: (Class)class
{
	for (Class iter = object_getClass(self); iter != Nil;
	    iter = class_getSuperclass(iter))
		if (iter == class)







|







570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
{
	return class_getSuperclass(object_getClass(self));
}

- (OFString *)className
{
	return [OFString stringWithCString: object_getClassName(self)
				  encoding: OFStringEncodingASCII];
}

- (bool)isKindOfClass: (Class)class
{
	for (Class iter = object_getClass(self); iter != Nil;
	    iter = class_getSuperclass(iter))
		if (iter == class)
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
#elif defined(OF_APPLE_RUNTIME)
	id (*imp)(id, SEL) = (id (*)(id, SEL))objc_msgSend;
#endif

	return imp(self, selector);
}

- (id)performSelector: (SEL)selector
	   withObject: (id)object
{
#if defined(OF_OBJFW_RUNTIME)
	id (*imp)(id, SEL, id) =
	    (id (*)(id, SEL, id))objc_msg_lookup(self, selector);
#elif defined(OF_APPLE_RUNTIME)
	id (*imp)(id, SEL, id) = (id (*)(id, SEL, id))objc_msgSend;
#endif







|
<







614
615
616
617
618
619
620
621

622
623
624
625
626
627
628
#elif defined(OF_APPLE_RUNTIME)
	id (*imp)(id, SEL) = (id (*)(id, SEL))objc_msgSend;
#endif

	return imp(self, selector);
}

- (id)performSelector: (SEL)selector withObject: (id)object

{
#if defined(OF_OBJFW_RUNTIME)
	id (*imp)(id, SEL, id) =
	    (id (*)(id, SEL, id))objc_msg_lookup(self, selector);
#elif defined(OF_APPLE_RUNTIME)
	id (*imp)(id, SEL, id) = (id (*)(id, SEL, id))objc_msgSend;
#endif
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
761
762
763
764
765
	id (*imp)(id, SEL, id, id, id, id) =
	    (id (*)(id, SEL, id, id, id, id))objc_msgSend;
#endif

	return imp(self, selector, object1, object2, object3, object4);
}

- (void)performSelector: (SEL)selector
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					repeats: false];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	     withObject: (id)object
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					 object: object
					repeats: false];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	     withObject: (id)object1
	     withObject: (id)object2
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					 object: object1
					 object: object2
					repeats: false];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	     withObject: (id)object1
	     withObject: (id)object2
	     withObject: (id)object3
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					 object: object1
					 object: object2
					 object: object3
					repeats: false];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	     withObject: (id)object1
	     withObject: (id)object2
	     withObject: (id)object3
	     withObject: (id)object4
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					 object: object1







|
<













|















|

















|



















|







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
	id (*imp)(id, SEL, id, id, id, id) =
	    (id (*)(id, SEL, id, id, id, id))objc_msgSend;
#endif

	return imp(self, selector, object1, object2, object3, object4);
}

- (void)performSelector: (SEL)selector afterDelay: (OFTimeInterval)delay

{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					repeats: false];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	     withObject: (id)object
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					 object: object
					repeats: false];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	     withObject: (id)object1
	     withObject: (id)object2
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					 object: object1
					 object: object2
					repeats: false];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	     withObject: (id)object1
	     withObject: (id)object2
	     withObject: (id)object3
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					 object: object1
					 object: object2
					 object: object3
					repeats: false];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	     withObject: (id)object1
	     withObject: (id)object2
	     withObject: (id)object3
	     withObject: (id)object4
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[OFTimer scheduledTimerWithTimeInterval: delay
					 target: self
				       selector: selector
					 object: object1
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
		[timer waitUntilDone];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							 repeats: false]];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (id)object
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							  object: object
							 repeats: false]];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (id)object1
	     withObject: (id)object2
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							  object: object1
							  object: object2
							 repeats: false]];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (id)object1
	     withObject: (id)object2
	     withObject: (id)object3
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							  object: object1
							  object: object2
							  object: object3
							 repeats: false]];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (id)object1
	     withObject: (id)object2
	     withObject: (id)object3
	     withObject: (id)object4
	     afterDelay: (of_time_interval_t)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							  object: object1







|














|
















|


















|




















|







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
		[timer waitUntilDone];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							 repeats: false]];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (id)object
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							  object: object
							 repeats: false]];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (id)object1
	     withObject: (id)object2
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							  object: object1
							  object: object2
							 repeats: false]];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (id)object1
	     withObject: (id)object2
	     withObject: (id)object3
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							  object: object1
							  object: object2
							  object: object3
							 repeats: false]];

	objc_autoreleasePoolPop(pool);
}

- (void)performSelector: (SEL)selector
	       onThread: (OFThread *)thread
	     withObject: (id)object1
	     withObject: (id)object2
	     withObject: (id)object3
	     withObject: (id)object4
	     afterDelay: (OFTimeInterval)delay
{
	void *pool = objc_autoreleasePoolPush();

	[thread.runLoop addTimer: [OFTimer timerWithTimeInterval: delay
							  target: self
							selector: selector
							  object: object1
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
{
	return (object == self);
}

- (unsigned long)hash
{
	uintptr_t ptr = (uintptr_t)self;
	uint32_t hash;

	OF_HASH_INIT(hash);

	for (size_t i = 0; i < sizeof(ptr); i++) {
		OF_HASH_ADD(hash, ptr & 0xFF);
		ptr >>= 8;
	}

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	/* Classes containing data should reimplement this! */

	return [OFString stringWithFormat: @"<%@>", self.className];
}

- (void *)allocMemoryWithSize: (size_t)size
{
	void *pointer;
	struct pre_mem *preMem;

	if OF_UNLIKELY (size == 0)
		return NULL;

	if OF_UNLIKELY (size > SIZE_MAX - PRE_IVARS_ALIGN)
		@throw [OFOutOfRangeException exception];

	if OF_UNLIKELY ((pointer = malloc(PRE_MEM_ALIGN + size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];

	preMem = pointer;
	preMem->owner = self;
	preMem->prev = PRE_IVARS->lastMem;
	preMem->next = NULL;

	if OF_LIKELY (PRE_IVARS->lastMem != NULL)
		PRE_IVARS->lastMem->next = preMem;

	if OF_UNLIKELY (PRE_IVARS->firstMem == NULL)
		PRE_IVARS->firstMem = preMem;

	PRE_IVARS->lastMem = preMem;

	return (char *)pointer + PRE_MEM_ALIGN;
}

- (void *)allocMemoryWithSize: (size_t)size
			count: (size_t)count
{
	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	return [self allocMemoryWithSize: size * count];
}

- (void *)allocZeroedMemoryWithSize: (size_t)size
{
	void *pointer;
	struct pre_mem *preMem;

	if OF_UNLIKELY (size == 0)
		return NULL;

	if OF_UNLIKELY (size > SIZE_MAX - PRE_IVARS_ALIGN)
		@throw [OFOutOfRangeException exception];

	if OF_UNLIKELY ((pointer = calloc(1, PRE_MEM_ALIGN + size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];

	preMem = pointer;
	preMem->owner = self;
	preMem->prev = PRE_IVARS->lastMem;

	if OF_LIKELY (PRE_IVARS->lastMem != NULL)
		PRE_IVARS->lastMem->next = preMem;

	if OF_UNLIKELY (PRE_IVARS->firstMem == NULL)
		PRE_IVARS->firstMem = preMem;

	PRE_IVARS->lastMem = preMem;

	return (char *)pointer + PRE_MEM_ALIGN;
}

- (void *)allocZeroedMemoryWithSize: (size_t)size
			      count: (size_t)count
{
	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	return [self allocZeroedMemoryWithSize: size * count];
}

- (void *)resizeMemory: (void *)pointer
		  size: (size_t)size
{
	void *new;
	struct pre_mem *preMem;

	if OF_UNLIKELY (pointer == NULL)
		return [self allocMemoryWithSize: size];

	if OF_UNLIKELY (size == 0) {
		[self freeMemory: pointer];
		return NULL;
	}

	if OF_UNLIKELY (PRE_MEM(pointer)->owner != self)
		@throw [OFMemoryNotPartOfObjectException
		    exceptionWithPointer: pointer
				  object: self];

	if OF_UNLIKELY ((new = realloc(PRE_MEM(pointer),
	    PRE_MEM_ALIGN + size)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: size];
	preMem = new;

	if OF_UNLIKELY (preMem != PRE_MEM(pointer)) {
		if OF_LIKELY (preMem->prev != NULL)
			preMem->prev->next = preMem;
		if OF_LIKELY (preMem->next != NULL)
			preMem->next->prev = preMem;

		if OF_UNLIKELY (PRE_IVARS->firstMem == PRE_MEM(pointer))
			PRE_IVARS->firstMem = preMem;
		if OF_UNLIKELY (PRE_IVARS->lastMem == PRE_MEM(pointer))
			PRE_IVARS->lastMem = preMem;
	}

	return (char *)new + PRE_MEM_ALIGN;
}

- (void *)resizeMemory: (void *)pointer
		  size: (size_t)size
		 count: (size_t)count
{
	if OF_UNLIKELY (pointer == NULL)
		return [self allocMemoryWithSize: size
					   count: count];

	if OF_UNLIKELY (size == 0 || count == 0) {
		[self freeMemory: pointer];
		return NULL;
	}

	if OF_UNLIKELY (count > SIZE_MAX / size)
		@throw [OFOutOfRangeException exception];

	return [self resizeMemory: pointer
			     size: size * count];
}

- (void)freeMemory: (void *)pointer
{
	if OF_UNLIKELY (pointer == NULL)
		return;

	if OF_UNLIKELY (PRE_MEM(pointer)->owner != self)
		@throw [OFMemoryNotPartOfObjectException
		    exceptionWithPointer: pointer
				  object: self];

	if OF_LIKELY (PRE_MEM(pointer)->prev != NULL)
		PRE_MEM(pointer)->prev->next = PRE_MEM(pointer)->next;
	if OF_LIKELY (PRE_MEM(pointer)->next != NULL)
		PRE_MEM(pointer)->next->prev = PRE_MEM(pointer)->prev;

	if OF_UNLIKELY (PRE_IVARS->firstMem == PRE_MEM(pointer))
		PRE_IVARS->firstMem = PRE_MEM(pointer)->next;
	if OF_UNLIKELY (PRE_IVARS->lastMem == PRE_MEM(pointer))
		PRE_IVARS->lastMem = PRE_MEM(pointer)->prev;

	/* To detect double-free */
	PRE_MEM(pointer)->owner = nil;

	free(PRE_MEM(pointer));
}

- (id)forwardingTargetForSelector: (SEL)selector
{
	return nil;
}

- (void)doesNotRecognizeSelector: (SEL)selector
{
	@throw [OFNotImplementedException exceptionWithSelector: selector
							 object: self];
}

- (instancetype)retain
{
#if defined(OF_HAVE_ATOMIC_OPS)
	of_atomic_int_inc(&PRE_IVARS->retainCount);
#elif defined(OF_AMIGAOS)
	/*
	 * On AmigaOS, we can only have one CPU. As increasing a variable is a
	 * single instruction on M68K, we don't need Forbid() / Permit() on
	 * M68K.
	 */
# ifndef OF_AMIGAOS_M68K
	Forbid();
# endif
	PRE_IVARS->retainCount++;
# ifndef OF_AMIGAOS_M68K
	Permit();
# endif
#else
	OF_ENSURE(of_spinlock_lock(&PRE_IVARS->retainCountSpinlock));
	PRE_IVARS->retainCount++;
	OF_ENSURE(of_spinlock_unlock(&PRE_IVARS->retainCountSpinlock));
#endif

	return self;
}

- (unsigned int)retainCount
{
	assert(PRE_IVARS->retainCount >= 0);
	return PRE_IVARS->retainCount;
}

- (void)release
{
#if defined(OF_HAVE_ATOMIC_OPS)
	of_memory_barrier_release();

	if (of_atomic_int_dec(&PRE_IVARS->retainCount) <= 0) {
		of_memory_barrier_acquire();

		[self dealloc];
	}
#elif defined(OF_AMIGAOS)
	int retainCount;

	Forbid();
	retainCount = --PRE_IVARS->retainCount;
	Permit();

	if (retainCount == 0)
		[self dealloc];
#else
	int retainCount;

	OF_ENSURE(of_spinlock_lock(&PRE_IVARS->retainCountSpinlock));
	retainCount = --PRE_IVARS->retainCount;
	OF_ENSURE(of_spinlock_unlock(&PRE_IVARS->retainCountSpinlock));

	if (retainCount == 0)
		[self dealloc];
#endif
}

- (instancetype)autorelease







|

|


|



|











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














|














|

|














|

|
|















|

|







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
{
	return (object == self);
}

- (unsigned long)hash
{
	uintptr_t ptr = (uintptr_t)self;
	unsigned long hash;

	OFHashInit(&hash);

	for (size_t i = 0; i < sizeof(ptr); i++) {
		OFHashAdd(&hash, ptr & 0xFF);
		ptr >>= 8;
	}

	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)description
{
	/* Classes containing data should reimplement this! */

	return [OFString stringWithFormat: @"<%@>", self.className];
}






































































































































































- (id)forwardingTargetForSelector: (SEL)selector
{
	return nil;
}

- (void)doesNotRecognizeSelector: (SEL)selector
{
	@throw [OFNotImplementedException exceptionWithSelector: selector
							 object: self];
}

- (instancetype)retain
{
#if defined(OF_HAVE_ATOMIC_OPS)
	OFAtomicIntIncrease(&PRE_IVARS->retainCount);
#elif defined(OF_AMIGAOS)
	/*
	 * On AmigaOS, we can only have one CPU. As increasing a variable is a
	 * single instruction on M68K, we don't need Forbid() / Permit() on
	 * M68K.
	 */
# ifndef OF_AMIGAOS_M68K
	Forbid();
# endif
	PRE_IVARS->retainCount++;
# ifndef OF_AMIGAOS_M68K
	Permit();
# endif
#else
	OFEnsure(OFSpinlockLock(&PRE_IVARS->retainCountSpinlock) == 0);
	PRE_IVARS->retainCount++;
	OFEnsure(OFSpinlockUnlock(&PRE_IVARS->retainCountSpinlock) == 0);
#endif

	return self;
}

- (unsigned int)retainCount
{
	assert(PRE_IVARS->retainCount >= 0);
	return PRE_IVARS->retainCount;
}

- (void)release
{
#if defined(OF_HAVE_ATOMIC_OPS)
	OFReleaseMemoryBarrier();

	if (OFAtomicIntDecrease(&PRE_IVARS->retainCount) <= 0) {
		OFAcquireMemoryBarrier();

		[self dealloc];
	}
#elif defined(OF_AMIGAOS)
	int retainCount;

	Forbid();
	retainCount = --PRE_IVARS->retainCount;
	Permit();

	if (retainCount == 0)
		[self dealloc];
#else
	int retainCount;

	OFEnsure(OFSpinlockLock(&PRE_IVARS->retainCountSpinlock) == 0);
	retainCount = --PRE_IVARS->retainCount;
	OFEnsure(OFSpinlockUnlock(&PRE_IVARS->retainCountSpinlock) == 0);

	if (retainCount == 0)
		[self dealloc];
#endif
}

- (instancetype)autorelease
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
	[self retain];

	return true;
}

- (void)dealloc
{
	struct pre_mem *iter;

	objc_destructInstance(self);

	iter = PRE_IVARS->firstMem;
	while (iter != NULL) {
		struct pre_mem *next = iter->next;

		/*
		 * We can use owner as a sentinel to prevent exploitation in
		 * case there is a buffer underflow somewhere.
		 */
		OF_ENSURE(iter->owner == self);

		free(iter);

		iter = next;
	}

	free((char *)self - PRE_IVARS_ALIGN);
}

/* Required to use properties with the Apple runtime */
- (id)copyWithZone: (void *)zone
{
	if OF_UNLIKELY (zone != NULL) {







<
<


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







1197
1198
1199
1200
1201
1202
1203


1204
1205















1206
1207
1208
1209
1210
1211
1212
	[self retain];

	return true;
}

- (void)dealloc
{


	objc_destructInstance(self);
















	free((char *)self - PRE_IVARS_ALIGN);
}

/* Required to use properties with the Apple runtime */
- (id)copyWithZone: (void *)zone
{
	if OF_UNLIKELY (zone != NULL) {
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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
		abort();
	}

	return [(id)self mutableCopy];
}

/*
 * Those are needed as the root class is the superclass of the root class's
 * metaclass and thus instance methods can be sent to class objects as well.

 */
+ (void *)allocMemoryWithSize: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (void *)allocMemoryWithSize: (size_t)size
		       count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (void *)resizeMemory: (void *)pointer
		  size: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (void *)resizeMemory: (void *)pointer
		  size: (size_t)size
		 count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (void)freeMemory: (void *)pointer
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (id)retain
{
	return self;
}

+ (id)autorelease
{
	return self;
}

+ (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

+ (void)release
{
}

+ (void)dealloc







|
|
>

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













|







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
		abort();
	}

	return [(id)self mutableCopy];
}

/*
 * The following are needed as the root class is the superclass of the root
 * class's metaclass and thus instance methods can be sent to class objects as
 * well.
 */





























+ (id)retain
{
	return self;
}

+ (id)autorelease
{
	return self;
}

+ (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

+ (void)release
{
}

+ (void)dealloc

Renamed and modified src/once.h [862d2c15bd] to src/OFOnce.h [3af1572f79].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "objfw-defs.h"

#include "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_once_t of_once_t;
# define OF_ONCE_INIT PTHREAD_ONCE_INIT
#elif defined(OF_HAVE_ATOMIC_OPS)
typedef volatile int of_once_t;
# define OF_ONCE_INIT 0
#elif defined(OF_AMIGAOS) || !defined(OF_HAVE_THREADS)
typedef int of_once_t;
# define OF_ONCE_INIT 0
#endif

#ifdef __cplusplus
extern "C" {
#endif








extern void of_once(of_once_t *control, void (*func)(void));
#ifdef __cplusplus
}
#endif

<
<
|



















|
|

|
|

|
|





>
>
>
>
>
>
>
>
|



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


 * 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 "objfw-defs.h"

#include "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_once_t OFOnceControl;
# define OFOnceControlInitValue PTHREAD_ONCE_INIT
#elif defined(OF_HAVE_ATOMIC_OPS)
typedef volatile int OFOnceControl;
# define OFOnceControlInitValue 0
#elif defined(OF_AMIGAOS) || !defined(OF_HAVE_THREADS)
typedef int OFOnceControl;
# define OFOnceControlInitValue 0
#endif

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Executes the specified function exactly once in the application's
 *	  lifetime, even in a multi-threaded environment.
 *
 * @param control An OFOnceControl. This should be a static variable
 *		  preinitialized to `OFOnceControlInitValue`.
 * @param function The function to execute once
 */
extern void OFOnce(OFOnceControl *control, void (*function)(void));
#ifdef __cplusplus
}
#endif

Renamed and modified src/once.m [dedad9ba7b] to src/OFOnce.m [72d5ba7049].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdbool.h>

#import "once.h"





#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif

#if defined(OF_HAVE_THREADS) && defined(OF_HAVE_ATOMIC_OPS)
# import "atomic.h"
# import "mutex.h"
#endif

void
of_once(of_once_t *control, void (*func)(void))
{
#if !defined(OF_HAVE_THREADS)
	if (*control == 0) {
		func();
		*control = 1;
	}
#elif defined(OF_HAVE_PTHREADS)
	pthread_once(control, func);
#elif defined(OF_HAVE_ATOMIC_OPS)
	/* Avoid atomic operations in case it's already done. */
	if (*control == 2)
		return;

	if (of_atomic_int_cmpswap(control, 0, 1)) {
		func();

		of_memory_barrier();

		of_atomic_int_inc(control);
	} else
		while (*control == 1)
			of_thread_yield();
#elif defined(OF_AMIGAOS)
	bool run = false;

	/* Avoid Forbid() in case it's already done. */
	if (*control == 2)
		return;


<
<
|

















|
>
>
>
>





<
<
<
<
<

|



|



|





|
|

|

|


|







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


 * 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 <stdbool.h>

#import "OFOnce.h"
#if defined(OF_HAVE_THREADS) && defined(OF_HAVE_ATOMIC_OPS)
# import "OFAtomic.h"
# import "OFPlainMutex.h"
#endif

#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif






void
OFOnce(OFOnceControl *control, void (*function)(void))
{
#if !defined(OF_HAVE_THREADS)
	if (*control == 0) {
		function();
		*control = 1;
	}
#elif defined(OF_HAVE_PTHREADS)
	pthread_once(control, function);
#elif defined(OF_HAVE_ATOMIC_OPS)
	/* Avoid atomic operations in case it's already done. */
	if (*control == 2)
		return;

	if (OFAtomicIntCompareAndSwap(control, 0, 1)) {
		function();

		OFMemoryBarrier();

		OFAtomicIntIncrease(control);
	} else
		while (*control == 1)
			OFYieldThread();
#elif defined(OF_AMIGAOS)
	bool run = false;

	/* Avoid Forbid() in case it's already done. */
	if (*control == 2)
		return;

74
75
76
77
78
79
80
81
82
83
84
85
86
87
			Forbid();
		}
	}

	Permit();

	if (run) {
		func();
		*control = 2;
	}
#else
# error No of_once available
#endif
}







|



|


71
72
73
74
75
76
77
78
79
80
81
82
83
84
			Forbid();
		}
	}

	Permit();

	if (run) {
		function();
		*control = 2;
	}
#else
# error No OFOnce available
#endif
}

Modified src/OFOptionsParser.h from [02d9a0ef41] to [14db018626].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#import "OFString.h"

@class OFMapTable;

OF_ASSUME_NONNULL_BEGIN

/**
 * @struct of_options_parser_option_t OFOptionsParser.h ObjFW/OFOptionsParser.h
 *
 * @brief An option which can be parsed by an @ref OFOptionsParser.
 */
struct of_options_parser_option_t {
	/** The short version (e.g. `-v`) of the option or `\0` for none. */
	of_unichar_t shortOption;

	/**
	 * The long version (e.g. `--verbose`) of the option or `nil` for none.
	 */
	OFString *__unsafe_unretained _Nullable longOption;

	/**







|



|

|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#import "OFString.h"

@class OFMapTable;

OF_ASSUME_NONNULL_BEGIN

/**
 * @struct OFOptionsParserOption OFOptionsParser.h ObjFW/OFOptionsParser.h
 *
 * @brief An option which can be parsed by an @ref OFOptionsParser.
 */
typedef struct {
	/** The short version (e.g. `-v`) of the option or `\0` for none. */
	OFUnichar shortOption;

	/**
	 * The long version (e.g. `--verbose`) of the option or `nil` for none.
	 */
	OFString *__unsafe_unretained _Nullable longOption;

	/**
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
	bool *_Nullable isSpecifiedPtr;

	/**
	 * An optional pointer to an `OFString *` that is set to the
	 * argument specified for the option or `nil` for no argument.
	 */
	OFString *__autoreleasing _Nullable *_Nullable argumentPtr;
};
typedef struct of_options_parser_option_t of_options_parser_option_t;

/**
 * @class OFOptionsParser OFOptionsParser.h ObjFW/OFOptionsParser.h
 *
 * @brief A class for parsing the program options specified on the command line.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFOptionsParser: OFObject
{
	of_options_parser_option_t *_options;
	OFMapTable *_longOptions;
	OFArray OF_GENERIC(OFString *) *_arguments;
	size_t _index, _subIndex;
	of_unichar_t _lastOption;
	OFString *_Nullable _lastLongOption, *_Nullable _argument;
	bool _done;
}

/**
 * @brief The last parsed option.
 *
 * If @ref nextOption returned `?` or `:`, this returns the option which was
 * unknown or for which the argument was missing.@n
 * If this returns `-`, the last option is only available as a long option (see
 * lastLongOption).
 */
@property (readonly, nonatomic) of_unichar_t lastOption;

/**
 * @brief The long option for the last parsed option, or `nil` if the last
 *	  parsed option was not passed as a long option by the user.
 *
 * In case @ref nextOption returned `?`, this contains the unknown long
 * option.@n







<
|









|



|












|







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
	bool *_Nullable isSpecifiedPtr;

	/**
	 * An optional pointer to an `OFString *` that is set to the
	 * argument specified for the option or `nil` for no argument.
	 */
	OFString *__autoreleasing _Nullable *_Nullable argumentPtr;

} OFOptionsParserOption;

/**
 * @class OFOptionsParser OFOptionsParser.h ObjFW/OFOptionsParser.h
 *
 * @brief A class for parsing the program options specified on the command line.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFOptionsParser: OFObject
{
	OFOptionsParserOption *_options;
	OFMapTable *_longOptions;
	OFArray OF_GENERIC(OFString *) *_arguments;
	size_t _index, _subIndex;
	OFUnichar _lastOption;
	OFString *_Nullable _lastLongOption, *_Nullable _argument;
	bool _done;
}

/**
 * @brief The last parsed option.
 *
 * If @ref nextOption returned `?` or `:`, this returns the option which was
 * unknown or for which the argument was missing.@n
 * If this returns `-`, the last option is only available as a long option (see
 * lastLongOption).
 */
@property (readonly, nonatomic) OFUnichar lastOption;

/**
 * @brief The long option for the last parsed option, or `nil` if the last
 *	  parsed option was not passed as a long option by the user.
 *
 * In case @ref nextOption returned `?`, this contains the unknown long
 * option.@n
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
 */
@property (readonly, nonatomic)
    OFArray OF_GENERIC(OFString *) *remainingArguments;

/**
 * @brief Creates a new OFOptionsParser which accepts the specified options.
 *
 * @param options An array of @ref of_options_parser_option_t specifying all
 *		  accepted options, terminated with an option whose short
 *		  option is `\0` and long option is `nil`.
 *
 * @return A new, autoreleased OFOptionsParser
 */
+ (instancetype)parserWithOptions: (const of_options_parser_option_t *)options;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFOptionsParser so that it accepts
 *	  the specified options.
 *
 * @param options An array of @ref of_options_parser_option_t specifying all
 *		  accepted options, terminated with an option whose short
 *		  option is `\0` and long option is `nil`.
 *
 * @return An initialized OFOptionsParser
 */
- (instancetype)initWithOptions: (const of_options_parser_option_t *)options
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Returns the next option.
 *
 * If the option is only available as a long option, `-` is returned.
 * Otherwise, the short option is returned, even if it was specified as a long
 * option.@n
 * If an unknown option is specified, `?` is returned.@n
 * If the argument for the option is missing, `:` is returned.@n
 * If there is an argument for the option even though it takes none, `=` is
 * returned.@n
 * If all options have been parsed, `\0` is returned.
 *
 * @note You need to call @ref nextOption repeatedly until it returns `\0` to
 *	 make sure all options have been parsed, even if you only rely on the
 *	 optional pointers specified and don't do any parsing yourself.
 *
 * @return The next option
 */
- (of_unichar_t)nextOption;
@end

OF_ASSUME_NONNULL_END







|





|







|





|




















|



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
 */
@property (readonly, nonatomic)
    OFArray OF_GENERIC(OFString *) *remainingArguments;

/**
 * @brief Creates a new OFOptionsParser which accepts the specified options.
 *
 * @param options An array of @ref OFOptionsParserOption specifying all
 *		  accepted options, terminated with an option whose short
 *		  option is `\0` and long option is `nil`.
 *
 * @return A new, autoreleased OFOptionsParser
 */
+ (instancetype)parserWithOptions: (const OFOptionsParserOption *)options;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFOptionsParser so that it accepts
 *	  the specified options.
 *
 * @param options An array of @ref OFOptionsParserOption specifying all
 *		  accepted options, terminated with an option whose short
 *		  option is `\0` and long option is `nil`.
 *
 * @return An initialized OFOptionsParser
 */
- (instancetype)initWithOptions: (const OFOptionsParserOption *)options
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Returns the next option.
 *
 * If the option is only available as a long option, `-` is returned.
 * Otherwise, the short option is returned, even if it was specified as a long
 * option.@n
 * If an unknown option is specified, `?` is returned.@n
 * If the argument for the option is missing, `:` is returned.@n
 * If there is an argument for the option even though it takes none, `=` is
 * returned.@n
 * If all options have been parsed, `\0` is returned.
 *
 * @note You need to call @ref nextOption repeatedly until it returns `\0` to
 *	 make sure all options have been parsed, even if you only rely on the
 *	 optional pointers specified and don't do any parsing yourself.
 *
 * @return The next option
 */
- (OFUnichar)nextOption;
@end

OF_ASSUME_NONNULL_END

Modified src/OFOptionsParser.m from [194fc0cb2f] to [6586be74a2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	return [(OFString *)object1 isEqual: (OFString *)object2];
}

@implementation OFOptionsParser
@synthesize lastOption = _lastOption, lastLongOption = _lastLongOption;
@synthesize argument = _argument;

+ (instancetype)parserWithOptions: (const of_options_parser_option_t *)options
{
	return [[[self alloc] initWithOptions: options] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithOptions: (const of_options_parser_option_t *)options
{
	self = [super init];

	@try {
		size_t count = 0;
		const of_options_parser_option_t *iter;
		of_options_parser_option_t *iter2;
		const of_map_table_functions_t keyFunctions = {
			.hash = stringHash,
			.equal = stringEqual
		};
		const of_map_table_functions_t objectFunctions = { NULL };

		/* Count, sanity check, initialize pointers */
		for (iter = options;
		    iter->shortOption != '\0' || iter->longOption != nil;
		    iter++) {
			if (iter->hasArgument < -1 || iter->hasArgument > 1)
				@throw [OFInvalidArgumentException exception];







|









|





|
|
|



|







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
	return [(OFString *)object1 isEqual: (OFString *)object2];
}

@implementation OFOptionsParser
@synthesize lastOption = _lastOption, lastLongOption = _lastLongOption;
@synthesize argument = _argument;

+ (instancetype)parserWithOptions: (const OFOptionsParserOption *)options
{
	return [[[self alloc] initWithOptions: options] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithOptions: (const OFOptionsParserOption *)options
{
	self = [super init];

	@try {
		size_t count = 0;
		const OFOptionsParserOption *iter;
		OFOptionsParserOption *iter2;
		const OFMapTableFunctions keyFunctions = {
			.hash = stringHash,
			.equal = stringEqual
		};
		const OFMapTableFunctions objectFunctions = { NULL };

		/* Count, sanity check, initialize pointers */
		for (iter = options;
		    iter->shortOption != '\0' || iter->longOption != nil;
		    iter++) {
			if (iter->hasArgument < -1 || iter->hasArgument > 1)
				@throw [OFInvalidArgumentException exception];
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98
99
100
101
				*iter->isSpecifiedPtr = false;
			if (iter->argumentPtr)
				*iter->argumentPtr = nil;

			count++;
		}


		_longOptions = [[OFMapTable alloc]
		    initWithKeyFunctions: keyFunctions
			 objectFunctions: objectFunctions];
		_options = [self
		    allocMemoryWithSize: sizeof(*_options)
				  count: count + 1];

		for (iter = options, iter2 = _options;
		    iter->shortOption != '\0' || iter->longOption != nil;
		    iter++, iter2++) {
			iter2->shortOption = iter->shortOption;
			iter2->longOption = nil;
			iter2->hasArgument = iter->hasArgument;







>



<
<
<







80
81
82
83
84
85
86
87
88
89
90



91
92
93
94
95
96
97
				*iter->isSpecifiedPtr = false;
			if (iter->argumentPtr)
				*iter->argumentPtr = nil;

			count++;
		}

		_options = OFAllocMemory(count + 1, sizeof(*_options));
		_longOptions = [[OFMapTable alloc]
		    initWithKeyFunctions: keyFunctions
			 objectFunctions: objectFunctions];




		for (iter = options, iter2 = _options;
		    iter->shortOption != '\0' || iter->longOption != nil;
		    iter++, iter2++) {
			iter2->shortOption = iter->shortOption;
			iter2->longOption = nil;
			iter2->hasArgument = iter->hasArgument;
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
	}

	return self;
}

- (void)dealloc
{
	of_options_parser_option_t *iter;

	[_longOptions release];

	if (_options != NULL)
		for (iter = _options;
		    iter->shortOption != '\0' || iter->longOption != nil;
		    iter++)
			[iter->longOption release];




	[_arguments release];
	[_argument release];

	[super dealloc];
}

- (of_unichar_t)nextOption
{
	of_options_parser_option_t *iter;
	OFString *argument;

	if (_done || _index >= _arguments.count)
		return '\0';

	[_lastLongOption release];
	[_argument release];







<
<
<
<

|



>
>
>







|

|







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
	}

	return self;
}

- (void)dealloc
{




	if (_options != NULL)
		for (OFOptionsParserOption *iter = _options;
		    iter->shortOption != '\0' || iter->longOption != nil;
		    iter++)
			[iter->longOption release];

	OFFreeMemory(_options);
	[_longOptions release];

	[_arguments release];
	[_argument release];

	[super dealloc];
}

- (OFUnichar)nextOption
{
	OFOptionsParserOption *iter;
	OFString *argument;

	if (_done || _index >= _arguments.count)
		return '\0';

	[_lastLongOption release];
	[_argument release];
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
			_index++;
			return '\0';
		}

		if ([argument hasPrefix: @"--"]) {
			void *pool = objc_autoreleasePoolPush();
			size_t pos;
			of_options_parser_option_t *option;

			_lastOption = '-';
			_index++;

			if ((pos = [argument rangeOfString: @"="].location) !=
			    OF_NOT_FOUND)
				_argument = [[argument
				    substringFromIndex: pos + 1] copy];
			else
				pos = argument.length;

			_lastLongOption = [[argument substringWithRange:
			    of_range(2, pos - 2)] copy];

			objc_autoreleasePoolPop(pool);

			option = [_longOptions objectForKey: _lastLongOption];
			if (option == NULL)
				return '?';








|





|






|







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
			_index++;
			return '\0';
		}

		if ([argument hasPrefix: @"--"]) {
			void *pool = objc_autoreleasePoolPush();
			size_t pos;
			OFOptionsParserOption *option;

			_lastOption = '-';
			_index++;

			if ((pos = [argument rangeOfString: @"="].location) !=
			    OFNotFound)
				_argument = [[argument
				    substringFromIndex: pos + 1] copy];
			else
				pos = argument.length;

			_lastLongOption = [[argument substringWithRange:
			    OFRangeMake(2, pos - 2)] copy];

			objc_autoreleasePoolPop(pool);

			option = [_longOptions objectForKey: _lastLongOption];
			if (option == NULL)
				return '?';

272
273
274
275
276
277
278
279
280
281

	return '?';
}

- (OFArray *)remainingArguments
{
	return [_arguments objectsInRange:
	    of_range(_index, _arguments.count - _index)];
}
@end







|


267
268
269
270
271
272
273
274
275
276

	return '?';
}

- (OFArray *)remainingArguments
{
	return [_arguments objectsInRange:
	    OFRangeMake(_index, _arguments.count - _index)];
}
@end

Renamed and modified src/pbkdf2.h [906ee7a0eb] to src/OFPBKDF2.h [fc763da9ca].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFHMAC;

/**
 * @brief The parameters for @ref of_pbkdf2.
 */
typedef struct of_pbkdf2_parameters_t {
	/** @brief The HMAC to use to derive a key. */
	__unsafe_unretained OFHMAC *HMAC;
	/** @brief The number of iterations to perform. */
	size_t iterations;
	/** @brief The salt to derive a key with. */
	const unsigned char *salt;
	/** @brief The length of the salt. */







|

|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFHMAC;

/**
 * @brief The parameters for @ref OFPBKDF2.
 */
typedef struct {
	/** @brief The HMAC to use to derive a key. */
	__unsafe_unretained OFHMAC *HMAC;
	/** @brief The number of iterations to perform. */
	size_t iterations;
	/** @brief The salt to derive a key with. */
	const unsigned char *salt;
	/** @brief The length of the salt. */
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
	 * @brief The desired length for the derived key.
	 *
	 * @ref key needs to have enough storage.
	 */
	size_t keyLength;
	/** @brief Whether data may be stored in swappable memory. */
	bool allowsSwappableMemory;
} of_pbkdf2_parameters_t;

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Derives a key from a password and a salt using PBKDF2.
 *
 * @note This will call @ref OFHMAC::reset on the `HMAC` first, making it
 *	 possible to reuse the `HMAC`, but also meaning all previous results
 *	 from the `HMAC` get invalidated if they have not been copied.
 *
 * @param param The parameters to use
 */
extern void of_pbkdf2(of_pbkdf2_parameters_t param);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|













|





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
	 * @brief The desired length for the derived key.
	 *
	 * @ref key needs to have enough storage.
	 */
	size_t keyLength;
	/** @brief Whether data may be stored in swappable memory. */
	bool allowsSwappableMemory;
} OFPBKDF2Parameters;

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Derives a key from a password and a salt using PBKDF2.
 *
 * @note This will call @ref OFHMAC::reset on the `HMAC` first, making it
 *	 possible to reuse the `HMAC`, but also meaning all previous results
 *	 from the `HMAC` get invalidated if they have not been copied.
 *
 * @param param The parameters to use
 */
extern void OFPBKDF2(OFPBKDF2Parameters param);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Renamed and modified src/pbkdf2.m [ae8f98e1e6] to src/OFPBKDF2.m [cca92e4a20].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdlib.h>


#import "OFHMAC.h"
#import "OFSecureData.h"

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

#import "pbkdf2.h"

void
of_pbkdf2(of_pbkdf2_parameters_t param)
{
	void *pool = objc_autoreleasePoolPush();
	size_t blocks, digestSize = param.HMAC.digestSize;
	OFSecureData *buffer = [OFSecureData
		    dataWithCount: digestSize
	    allowsSwappableMemory: param.allowsSwappableMemory];
	OFSecureData *digest = [OFSecureData

<
<
|

















>







<
<

|







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


 * 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 <stdlib.h>

#import "OFPBKDF2.h"
#import "OFHMAC.h"
#import "OFSecureData.h"

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



void
OFPBKDF2(OFPBKDF2Parameters param)
{
	void *pool = objc_autoreleasePoolPush();
	size_t blocks, digestSize = param.HMAC.digestSize;
	OFSecureData *buffer = [OFSecureData
		    dataWithCount: digestSize
	    allowsSwappableMemory: param.allowsSwappableMemory];
	OFSecureData *digest = [OFSecureData
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

	extendedSalt = [OFSecureData
		    dataWithCount: param.saltLength + 4
	    allowsSwappableMemory: param.allowsSwappableMemory];
	extendedSaltItems = extendedSalt.mutableItems;

	@try {
		uint32_t i = OF_BSWAP32_IF_LE(1);

		[param.HMAC setKey: param.password
			    length: param.passwordLength];

		memcpy(extendedSaltItems, param.salt, param.saltLength);

		while (param.keyLength > 0) {
			size_t length;

			memcpy(extendedSaltItems + param.saltLength, &i, 4);

			[param.HMAC reset];
			[param.HMAC updateWithBuffer: extendedSaltItems
					      length: param.saltLength + 4];

			memcpy(bufferItems, param.HMAC.digest, digestSize);
			memcpy(digestItems, param.HMAC.digest, digestSize);

			for (size_t j = 1; j < param.iterations; j++) {
				[param.HMAC reset];
				[param.HMAC updateWithBuffer: digestItems
						      length: digestSize];

				memcpy(digestItems, param.HMAC.digest,
				    digestSize);

				for (size_t k = 0; k < digestSize; k++)
					bufferItems[k] ^= digestItems[k];
			}

			length = digestSize;
			if (length > param.keyLength)
				length = param.keyLength;

			memcpy(param.key, bufferItems, length);
			param.key += length;
			param.keyLength -= length;

			i = OF_BSWAP32_IF_LE(OF_BSWAP32_IF_LE(i) + 1);
		}
	} @catch (id e) {
		[extendedSalt zero];
		[buffer zero];
		[digest zero];

		@throw e;
	} @finally {
		[param.HMAC zero];
	}

	objc_autoreleasePoolPop(pool);
}







|














>







>















|













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

	extendedSalt = [OFSecureData
		    dataWithCount: param.saltLength + 4
	    allowsSwappableMemory: param.allowsSwappableMemory];
	extendedSaltItems = extendedSalt.mutableItems;

	@try {
		uint32_t i = OFToBigEndian32(1);

		[param.HMAC setKey: param.password
			    length: param.passwordLength];

		memcpy(extendedSaltItems, param.salt, param.saltLength);

		while (param.keyLength > 0) {
			size_t length;

			memcpy(extendedSaltItems + param.saltLength, &i, 4);

			[param.HMAC reset];
			[param.HMAC updateWithBuffer: extendedSaltItems
					      length: param.saltLength + 4];
			[param.HMAC calculate];
			memcpy(bufferItems, param.HMAC.digest, digestSize);
			memcpy(digestItems, param.HMAC.digest, digestSize);

			for (size_t j = 1; j < param.iterations; j++) {
				[param.HMAC reset];
				[param.HMAC updateWithBuffer: digestItems
						      length: digestSize];
				[param.HMAC calculate];
				memcpy(digestItems, param.HMAC.digest,
				    digestSize);

				for (size_t k = 0; k < digestSize; k++)
					bufferItems[k] ^= digestItems[k];
			}

			length = digestSize;
			if (length > param.keyLength)
				length = param.keyLength;

			memcpy(param.key, bufferItems, length);
			param.key += length;
			param.keyLength -= length;

			i = OFToBigEndian32(OFFromBigEndian32(i) + 1);
		}
	} @catch (id e) {
		[extendedSalt zero];
		[buffer zero];
		[digest zero];

		@throw e;
	} @finally {
		[param.HMAC zero];
	}

	objc_autoreleasePoolPop(pool);
}

Modified src/OFPair.h from [b104780bae] to [96e09802be].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFPair.m from [6855a79df9] to [22c01e92c3].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, [_firstObject hash]);
	OF_HASH_ADD_HASH(hash, [_secondObject hash]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
	return [self retain];







|

|

|
|

|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, [_firstObject hash]);
	OFHashAddHash(&hash, [_secondObject hash]);

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{
	return [self retain];

Renamed and modified src/condition.h [ed39b78cbd] to src/OFPlainCondition.h [fd9b66a9ca].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#include "platform.h"

#if !defined(OF_HAVE_THREADS) || \
    (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS))
# error No conditions available!
#endif

/* For of_time_interval_t */
#import "OFObject.h"

#import "mutex.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_cond_t of_condition_t;
#elif defined(OF_WINDOWS)
# include <windows.h>
typedef struct {
	HANDLE event;
	volatile int count;
} of_condition_t;
#elif defined(OF_AMIGAOS)
# include <exec/tasks.h>
typedef struct {
	struct of_condition_waiting_task {
		struct Task *task;
		unsigned char sigBit;
		struct of_condition_waiting_task *next;
	} *waitingTasks;
} of_condition_t;
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern bool of_condition_new(of_condition_t *condition);
extern bool of_condition_signal(of_condition_t *condition);
extern bool of_condition_broadcast(of_condition_t *condition);

extern bool of_condition_wait(of_condition_t *condition, of_mutex_t *mutex);
extern bool of_condition_timed_wait(of_condition_t *condition,
    of_mutex_t *mutex, of_time_interval_t timeout);
#ifdef OF_AMIGAOS
extern bool of_condition_wait_or_signal(of_condition_t *condition,
    of_mutex_t *mutex, ULONG *signalMask);
extern bool of_condition_timed_wait_or_signal(of_condition_t *condition,
    of_mutex_t *mutex, of_time_interval_t timeout, ULONG *signalMask);
#endif
extern bool of_condition_free(of_condition_t *condition);
#ifdef __cplusplus
}
#endif







|

<
|



|





|



|


|

|





|
|
|
>
|
|
|

|
|
|
|

|



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 "platform.h"

#if !defined(OF_HAVE_THREADS) || \
    (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS))
# error No conditions available!
#endif

/* For OFTimeInterval */
#import "OFObject.h"

#import "OFPlainMutex.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_cond_t OFPlainCondition;
#elif defined(OF_WINDOWS)
# include <windows.h>
typedef struct {
	HANDLE event;
	volatile int count;
} OFPlainCondition;
#elif defined(OF_AMIGAOS)
# include <exec/tasks.h>
typedef struct {
	struct OFPlainConditionWaitingTask {
		struct Task *task;
		unsigned char sigBit;
		struct OFPlainConditionWaitingTask *next;
	} *waitingTasks;
} OFPlainCondition;
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern int OFPlainConditionNew(OFPlainCondition *condition);
extern int OFPlainConditionSignal(OFPlainCondition *condition);
extern int OFPlainConditionBroadcast(OFPlainCondition *condition);
extern int OFPlainConditionWait(OFPlainCondition *condition,
    OFPlainMutex *mutex);
extern int OFPlainConditionTimedWait(OFPlainCondition *condition,
    OFPlainMutex *mutex, OFTimeInterval timeout);
#ifdef OF_AMIGAOS
extern int OFPlainConditionWaitOrExecSignal(OFPlainCondition *condition,
    OFPlainMutex *mutex, ULONG *signalMask);
extern int OFPlainConditionTimedWaitOrExecSignal(OFPlainCondition *condition,
    OFPlainMutex *mutex, OFTimeInterval timeout, ULONG *signalMask);
#endif
extern int OFPlainConditionFree(OFPlainCondition *condition);
#ifdef __cplusplus
}
#endif

Renamed and modified src/condition.m [f3cd7e798e] to src/OFPlainCondition.m [b7456714c7].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include "platform/posix/condition.m"
#elif defined(OF_WINDOWS)
# include "platform/windows/condition.m"
#elif defined(OF_AMIGAOS)
# include "platform/amiga/condition.m"
#endif

<
<
|


















|

|

|

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


 * 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 "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include "platform/POSIX/OFPlainCondition.m"
#elif defined(OF_WINDOWS)
# include "platform/Windows/OFPlainCondition.m"
#elif defined(OF_AMIGAOS)
# include "platform/AmigaOS/OFPlainCondition.m"
#endif

Renamed and modified src/mutex.h [cbacd2f77f] to src/OFPlainMutex.h [e51d623af9].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "objfw-defs.h"



#include "platform.h"

#if !defined(OF_HAVE_THREADS) || \
    (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS))
# error No mutexes available!
#endif

#import "macros.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_mutex_t of_mutex_t;
#elif defined(OF_WINDOWS)
# include <windows.h>
typedef CRITICAL_SECTION of_mutex_t;
#elif defined(OF_AMIGAOS)
# include <exec/semaphores.h>
typedef struct SignalSemaphore of_mutex_t;
#endif

#if defined(OF_HAVE_ATOMIC_OPS)
# import "atomic.h"
typedef volatile int of_spinlock_t;
# define OF_SPINCOUNT 10
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
typedef pthread_spinlock_t of_spinlock_t;
#else
typedef of_mutex_t of_spinlock_t;
#endif

#ifdef OF_HAVE_SCHED_YIELD
# include <sched.h>
#endif

#if defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) || defined(OF_WINDOWS) || \
    defined(OF_AMIGAOS)
# define of_rmutex_t of_mutex_t
#else
# import "tlskey.h"
typedef struct {
	of_mutex_t mutex;
	of_tlskey_t count;
} of_rmutex_t;
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern bool of_mutex_new(of_mutex_t *mutex);
extern bool of_mutex_lock(of_mutex_t *mutex);
extern bool of_mutex_trylock(of_mutex_t *mutex);
extern bool of_mutex_unlock(of_mutex_t *mutex);
extern bool of_mutex_free(of_mutex_t *mutex);
extern bool of_rmutex_new(of_rmutex_t *rmutex);
extern bool of_rmutex_lock(of_rmutex_t *rmutex);
extern bool of_rmutex_trylock(of_rmutex_t *rmutex);
extern bool of_rmutex_unlock(of_rmutex_t *rmutex);
extern bool of_rmutex_free(of_rmutex_t *rmutex);
#ifdef __cplusplus
}
#endif

/* Spinlocks are inlined for performance. */

static OF_INLINE void
of_thread_yield(void)
{
#if defined(OF_HAVE_SCHED_YIELD)
	sched_yield();
#elif defined(OF_WINDOWS)
	Sleep(0);
#endif
}

static OF_INLINE bool
of_spinlock_new(of_spinlock_t *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	*spinlock = 0;
	return true;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return (pthread_spin_init(spinlock, 0) == 0);
#else
	return of_mutex_new(spinlock);
#endif
}

static OF_INLINE bool
of_spinlock_trylock(of_spinlock_t *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	if (of_atomic_int_cmpswap(spinlock, 0, 1)) {
		of_memory_barrier_acquire();
		return true;
	}

	return false;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return (pthread_spin_trylock(spinlock) == 0);
#else
	return of_mutex_trylock(spinlock);
#endif
}

static OF_INLINE bool
of_spinlock_lock(of_spinlock_t *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	size_t i;

	for (i = 0; i < OF_SPINCOUNT; i++)
		if (of_spinlock_trylock(spinlock))
			return true;

	while (!of_spinlock_trylock(spinlock))
		of_thread_yield();

	return true;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return (pthread_spin_lock(spinlock) == 0);
#else
	return of_mutex_lock(spinlock);
#endif
}

static OF_INLINE bool
of_spinlock_unlock(of_spinlock_t *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	bool ret = of_atomic_int_cmpswap(spinlock, 1, 0);

	of_memory_barrier_release();

	return ret;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return (pthread_spin_unlock(spinlock) == 0);
#else
	return of_mutex_unlock(spinlock);
#endif
}

static OF_INLINE bool
of_spinlock_free(of_spinlock_t *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	return true;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return (pthread_spin_destroy(spinlock) == 0);
#else
	return of_mutex_free(spinlock);
#endif
}

<
<
|














>
>












|


|


|



|
|
<

|

|








|

|

|
|
|





|
|
|
|
|
|
|
|
|
|







|








|
|



|

|

|



|
|


|
|
|


|

|

|



|
|




|
|
|

|
|

|

|

|



|
|


|

|

|

|

|



|
|


|

|

|


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


 * 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 "objfw-defs.h"

#include <errno.h>

#include "platform.h"

#if !defined(OF_HAVE_THREADS) || \
    (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS))
# error No mutexes available!
#endif

#import "macros.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_mutex_t OFPlainMutex;
#elif defined(OF_WINDOWS)
# include <windows.h>
typedef CRITICAL_SECTION OFPlainMutex;
#elif defined(OF_AMIGAOS)
# include <exec/semaphores.h>
typedef struct SignalSemaphore OFPlainMutex;
#endif

#if defined(OF_HAVE_ATOMIC_OPS)
# import "OFAtomic.h"
typedef volatile int OFSpinlock;

#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
typedef pthread_spinlock_t OFSpinlock;
#else
typedef OFPlainMutex OFSpinlock;
#endif

#ifdef OF_HAVE_SCHED_YIELD
# include <sched.h>
#endif

#if defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) || defined(OF_WINDOWS) || \
    defined(OF_AMIGAOS)
# define OFPlainRecursiveMutex OFPlainMutex
#else
# import "OFTLSKey.h"
typedef struct {
	OFPlainMutex mutex;
	OFTLSKey count;
} OFPlainRecursiveMutex;
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern int OFPlainMutexNew(OFPlainMutex *mutex);
extern int OFPlainMutexLock(OFPlainMutex *mutex);
extern int OFPlainMutexTryLock(OFPlainMutex *mutex);
extern int OFPlainMutexUnlock(OFPlainMutex *mutex);
extern int OFPlainMutexFree(OFPlainMutex *mutex);
extern int OFPlainRecursiveMutexNew(OFPlainRecursiveMutex *rmutex);
extern int OFPlainRecursiveMutexLock(OFPlainRecursiveMutex *rmutex);
extern int OFPlainRecursiveMutexTryLock(OFPlainRecursiveMutex *rmutex);
extern int OFPlainRecursiveMutexUnlock(OFPlainRecursiveMutex *rmutex);
extern int OFPlainRecursiveMutexFree(OFPlainRecursiveMutex *rmutex);
#ifdef __cplusplus
}
#endif

/* Spinlocks are inlined for performance. */

static OF_INLINE void
OFYieldThread(void)
{
#if defined(OF_HAVE_SCHED_YIELD)
	sched_yield();
#elif defined(OF_WINDOWS)
	Sleep(0);
#endif
}

static OF_INLINE int
OFSpinlockNew(OFSpinlock *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	*spinlock = 0;
	return 0;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return pthread_spin_init(spinlock, 0);
#else
	return OFPlainMutexNew(spinlock);
#endif
}

static OF_INLINE int
OFSpinlockTryLock(OFSpinlock *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	if (OFAtomicIntCompareAndSwap(spinlock, 0, 1)) {
		OFAcquireMemoryBarrier();
		return 0;
	}

	return EBUSY;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return pthread_spin_trylock(spinlock);
#else
	return OFPlainMutexTryLock(spinlock);
#endif
}

static OF_INLINE int
OFSpinlockLock(OFSpinlock *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	size_t i;

	for (i = 0; i < 10; i++)
		if (OFSpinlockTryLock(spinlock) == 0)
			return 0;

	while (OFSpinlockTryLock(spinlock) == EBUSY)
		OFYieldThread();

	return 0;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return pthread_spin_lock(spinlock);
#else
	return OFPlainMutexLock(spinlock);
#endif
}

static OF_INLINE int
OFSpinlockUnlock(OFSpinlock *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	bool ret = OFAtomicIntCompareAndSwap(spinlock, 1, 0);

	OFReleaseMemoryBarrier();

	return (ret ? 0 : EINVAL);
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return pthread_spin_unlock(spinlock);
#else
	return OFPlainMutexUnlock(spinlock);
#endif
}

static OF_INLINE int
OFSpinlockFree(OFSpinlock *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
	return 0;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
	return pthread_spin_destroy(spinlock);
#else
	return OFPlainMutexFree(spinlock);
#endif
}

Renamed and modified src/mutex.m [b45f1dde14] to src/OFPlainMutex.m [0367193fde].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include "platform/posix/mutex.m"
#elif defined(OF_WINDOWS)
# include "platform/windows/mutex.m"
#elif defined(OF_AMIGAOS)
# include "platform/amiga/mutex.m"
#endif

<
<
|


















|

|

|

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


 * 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 "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include "platform/POSIX/OFPlainMutex.m"
#elif defined(OF_WINDOWS)
# include "platform/Windows/OFPlainMutex.m"
#elif defined(OF_AMIGAOS)
# include "platform/AmigaOS/OFPlainMutex.m"
#endif

Renamed and modified src/thread.h [bf84b7cea6] to src/OFPlainThread.h [312286356e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
# error No threads available!
#endif

#import "macros.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_t of_thread_t;
#elif defined(OF_WINDOWS)
# include <windows.h>
typedef HANDLE of_thread_t;
#elif defined(OF_AMIGAOS)
# include <exec/tasks.h>
# include <exec/semaphores.h>
typedef struct {
	struct Task *task;
	void (*function)(id);
	id object;
	struct SignalSemaphore semaphore;
	struct Task *joinTask;
	unsigned char joinSigBit;
	bool detached, done;
} *of_thread_t;
#endif

typedef struct of_thread_attr_t {
	float priority;
	size_t stackSize;
} of_thread_attr_t;

#if defined(OF_HAVE_PTHREADS)



# define of_thread_is_current(t) pthread_equal(t, pthread_self())





# define of_thread_current() pthread_self()

#elif defined(OF_WINDOWS)



# define of_thread_is_current(t) (t == GetCurrentThread())





# define of_thread_current() GetCurrentThread()

#elif defined(OF_AMIGAOS)
# define of_thread_is_current(t) (t->thread == FindTask(NULL))
extern of_thread_t of_thread_current(void);
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern bool of_thread_attr_init(of_thread_attr_t *attr);
extern bool of_thread_new(of_thread_t *thread, const char *name,
    void (*function)(id), id object, const of_thread_attr_t *attr);
extern void of_thread_set_name(const char *name);
extern bool of_thread_join(of_thread_t thread);
extern bool of_thread_detach(of_thread_t thread);
#ifdef __cplusplus
}
#endif







|


|











|


|


|


>
>
>
|
>
>
>
>
>
|
>

>
>
>
|
>
>
>
>
>
|
>

|
|





|
|
|
|
|
|



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
# error No threads available!
#endif

#import "macros.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_t OFPlainThread;
#elif defined(OF_WINDOWS)
# include <windows.h>
typedef HANDLE OFPlainThread;
#elif defined(OF_AMIGAOS)
# include <exec/tasks.h>
# include <exec/semaphores.h>
typedef struct {
	struct Task *task;
	void (*function)(id);
	id object;
	struct SignalSemaphore semaphore;
	struct Task *joinTask;
	unsigned char joinSigBit;
	bool detached, done;
} *OFPlainThread;
#endif

typedef struct {
	float priority;
	size_t stackSize;
} OFPlainThreadAttributes;

#if defined(OF_HAVE_PTHREADS)
static OF_INLINE OFPlainThread
OFCurrentPlainThread(void)
{
	return pthread_self();
}

static OF_INLINE bool
OFPlainThreadIsCurrent(OFPlainThread thread)
{
	return pthread_equal(thread, pthread_self());
}
#elif defined(OF_WINDOWS)
static OF_INLINE OFPlainThread
OFCurrentPlainThread(void)
{
	return GetCurrentThread();
}

static OF_INLINE bool
OFPlainThreadIsCurrent(OFPlainThread thread)
{
	return (thread == GetCurrentThread());
}
#elif defined(OF_AMIGAOS)
extern OFPlainThread OFCurrentPlainThread(void);
extern bool OFPlainThreadIsCurrent(OFPlainThread);
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern int OFPlainThreadAttributesInit(OFPlainThreadAttributes *attr);
extern int OFPlainThreadNew(OFPlainThread *thread, const char *name,
    void (*function)(id), id object, const OFPlainThreadAttributes *attr);
extern void OFSetThreadName(const char *name);
extern int OFPlainThreadJoin(OFPlainThread thread);
extern int OFPlainThreadDetach(OFPlainThread thread);
#ifdef __cplusplus
}
#endif

Renamed and modified src/thread.m [5fe1003cc7] to src/OFPlainThread.m [ef8a973be8].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include "platform/posix/thread.m"
#elif defined(OF_WINDOWS)
# include "platform/windows/thread.m"
#elif defined(OF_AMIGAOS)
# include "platform/amiga/thread.m"
#endif

<
<
|


















|

|

|

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


 * 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 "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include "platform/POSIX/OFPlainThread.m"
#elif defined(OF_WINDOWS)
# include "platform/Windows/OFPlainThread.m"
#elif defined(OF_AMIGAOS)
# include "platform/AmigaOS/OFPlainThread.m"
#endif

Modified src/OFPlugin.h from [f88f934be5] to [4badab4fb7].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

@class OFString;

#ifndef OF_WINDOWS
# include <dlfcn.h>
# define OF_RTLD_LAZY RTLD_LAZY
# define OF_RTLD_NOW  RTLD_NOW
typedef void *of_plugin_handle_t;
#else
# include <windows.h>
# define OF_RTLD_LAZY 0
# define OF_RTLD_NOW  0
typedef HMODULE of_plugin_handle_t;
#endif

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFPlugin OFPlugin.h ObjFW/OFPlugin.h
 *
 * @brief Provides a system for loading plugins at runtime.

 */

@interface OFPlugin: OFObject
{
	of_plugin_handle_t _pluginHandle;
	OF_RESERVE_IVARS(OFPlugin, 4)
}

/**
 * @brief Loads a plugin from a file.
 *



 * @param path Path to the plugin file. The suffix is appended automatically.
 * @return The loaded plugin
 */
+ (OF_KINDOF(OFPlugin *))pluginFromFile: (OFString *)path;
@end



#ifdef __cplusplus


extern "C" {
#endif

extern of_plugin_handle_t of_dlopen(OFString *path, int flags);
extern void *of_dlsym(of_plugin_handle_t handle, const char *symbol);








extern OFString *_Nullable of_dlerror(void);
extern void of_dlclose(of_plugin_handle_t handle);
#ifdef __cplusplus
}







#endif

OF_ASSUME_NONNULL_END

<
<
|



















<
<
|


<
<
|







|
>

>


|
<



|

>
>
>
|
|

|
|
>
>
|
<
>
>
|
<
>
|
|
>
>
>
>
>
>
>
>
|
<
<
|
>
>
>
>
>
>
>
|


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


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

@class OFString;

#ifndef OF_WINDOWS
# include <dlfcn.h>


typedef void *OFPluginHandle;
#else
# include <windows.h>


typedef HMODULE OFPluginHandle;
#endif

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFPlugin OFPlugin.h ObjFW/OFPlugin.h
 *
 * @brief A class representing a loaded plugin (shared library).
 *
 */
OF_SUBCLASSING_RESTRICTED
@interface OFPlugin: OFObject
{
	OFPluginHandle _handle;

}

/**
 * @brief Returns the plugin path for a plugin with the specified name.
 *
 * E.g. on ELF systems, it appends .so, while on macOS and iOS, it creates the
 * appropriate plugin path. This can also be prefixed by a directory.
 *
 * @param name The name to return the plugin path for
 * @return The plugin path
 */
+ (OFString *)pathForName: (OFString *)name;

/**
 * @brief Creates a new OFPlugin by loading the plugin with the specified path.
 *

 * @param path The path to the plugin file. The suffix is appended
 *	       automatically.
 * @return An new, autoreleased OFPlugin

 */
+ (instancetype)pluginWithPath: (OFString *)path;

/**
 * @brief Initializes an already allocated OFPlugin by loading the plugin with
 *	  the specified path.
 *
 * @param path The path to the plugin file. The suffix is appended
 *	       automatically.
 * @return An initialized OFPlugin
 */
- (instancetype)initWithPath: (OFString *)path;



/**
 * @brief Returns the address for the specified symbol, or `nil` if not found.
 *
 * @param symbol The symbol to return the address for
 * @return The address for the speccified symbol, or `nil` if not found
 */
- (nullable void *)addressForSymbol: (OFString *)symbol;
@end

OF_ASSUME_NONNULL_END

Modified src/OFPlugin.m from [539230b0ba] to [dfbf2c9e77].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFLoadPluginFailedException.h"

typedef OFPlugin *(*init_plugin_t)(void);



of_plugin_handle_t
of_dlopen(OFString *path, int flags)
{
#ifndef OF_WINDOWS
	return dlopen([path cStringWithEncoding: [OFLocale encoding]], flags);

#else
	if (path == nil)
		return GetModuleHandle(NULL);

	if ([OFSystemInfo isWindowsNT])



		return LoadLibraryW(path.UTF16String);

	else
		return LoadLibraryA(
		    [path cStringWithEncoding: [OFLocale encoding]]);
#endif
}






void *
of_dlsym(of_plugin_handle_t handle, const char *symbol)
{
#ifndef OF_WINDOWS
	return dlsym(handle, symbol);
#else
	return (void *)(uintptr_t)GetProcAddress(handle, symbol);
#endif
}

void
of_dlclose(of_plugin_handle_t handle)
{
#ifndef OF_WINDOWS
	dlclose(handle);
#else
	FreeLibrary(handle);
#endif
}

OFString *
of_dlerror(void)
{
#ifndef OF_WINDOWS
	return [OFString stringWithCString: dlerror()
				  encoding: [OFLocale encoding]];
#else


	return nil;


#endif
}

@implementation OFPlugin
+ (id)pluginFromFile: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	of_plugin_handle_t handle;
	init_plugin_t initPlugin;
	OFPlugin *plugin;

#if defined(OF_MACOS)
	path = [path stringByAppendingFormat: @".bundle/Contents/MacOS/%@",
					      path.lastPathComponent];
#elif defined(OF_IOS)
	path = [path stringByAppendingFormat: @".bundle/%@",
					      path.lastPathComponent];
#else
	path = [path stringByAppendingString: @PLUGIN_SUFFIX];
#endif

	if ((handle = of_dlopen(path, OF_RTLD_LAZY)) == NULL)
		@throw [OFLoadPluginFailedException
		    exceptionWithPath: path
				error: of_dlerror()];

	objc_autoreleasePoolPop(pool);

	initPlugin = (init_plugin_t)(uintptr_t)of_dlsym(handle, "init_plugin");
	if (initPlugin == (init_plugin_t)0 || (plugin = initPlugin()) == nil) {
		of_dlclose(handle);
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
	}

	plugin->_pluginHandle = handle;
	return plugin;
}

- (instancetype)init
{
	if ([self isMemberOfClass: [OFPlugin class]]) {
		@try {
			[self doesNotRecognizeSelector: _cmd];
		} @catch (id e) {
			[self release];
			@throw e;
		}

		abort();
	}




	return [super init];





}

- (void)dealloc
{

	of_plugin_handle_t h = _pluginHandle;




	[super dealloc];

	of_dlclose(h);
}
@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
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
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFLoadPluginFailedException.h"

#ifndef RTLD_LAZY
# define RTLD_LAZY 0
#endif

@implementation OFPlugin
+ (instancetype)pluginWithPath: (OFString *)path
{

	return [[[self alloc] initWithPath: path] autorelease];
}

+ (OFString *)pathForName: (OFString *)name

{
#if defined(OF_MACOS)
	return [name stringByAppendingFormat: @".bundle/Contents/MacOS/%@",
					      name.lastPathComponent];
#elif defined(OF_IOS)
	return [name stringByAppendingFormat: @".bundle/%@",
					      name.lastPathComponent];
#else
	return [name stringByAppendingString: @PLUGIN_SUFFIX];

#endif
}

- (instancetype)initWithPath: (OFString *)path
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();


#ifndef OF_WINDOWS










		_handle = dlopen(










		    [path cStringWithEncoding: [OFLocale encoding]], RTLD_LAZY);
#else
		if ([OFSystemInfo isWindowsNT])
			_handle = LoadLibraryW(path.UTF16String);
		else
			_handle = LoadLibraryA(
			    [path cStringWithEncoding: [OFLocale encoding]]);
#endif

		if (_handle == NULL) {
#ifndef OF_WINDOWS
			OFString *error = [OFString


			    stringWithCString: dlerror()


				     encoding: [OFLocale encoding]];






#else
			OFString *error = nil;
#endif


			@throw [OFLoadPluginFailedException
			    exceptionWithPath: path
					error: error];
		}








		objc_autoreleasePoolPop(pool);









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

	return self;
}

- (void *)addressForSymbol: (OFString *)symbol
{
#ifndef OF_WINDOWS
	return dlsym(_handle,
	    [symbol cStringWithEncoding: [OFLocale encoding]]);
#else
	return (void *)(uintptr_t)GetProcAddress(_handle,
	    [symbol cStringWithEncoding: [OFLocale encoding]]);
#endif
}

- (void)dealloc
{
#ifndef OF_WINDOWS
	dlclose(_handle);
#else
	FreeLibrary(_handle);
#endif

	[super dealloc];
}


@end

Modified src/OFPointValue.h from [ef8036885e] to [42acb9db9a].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFValue.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFPointValue: OFValue
{
	of_point_t _point;
}


@end

OF_ASSUME_NONNULL_END

<
<
|



















|

>
>



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


 * 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 "OFValue.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFPointValue: OFValue
{
	OFPoint _point;
}

- (instancetype)initWithPoint: (OFPoint)point;
@end

OF_ASSUME_NONNULL_END

Modified src/OFPointValue.m from [83dd270a82] to [d80a9e47ec].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFString.h"

#import "OFOutOfRangeException.h"

@implementation OFPointValue
@synthesize pointValue = _point;

- (instancetype)initWithPoint: (of_point_t)point
{
	self = [super init];

	_point = point;

	return self;
}

- (const char *)objCType
{
	return @encode(of_point_t);
}

- (void)getValue: (void *)value
	    size: (size_t)size
{
	if (size != sizeof(_point))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_point, sizeof(_point));
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFValue: of_point_t { %f, %f }>", _point.x, _point.y];
}
@end







|










|


|
<










|


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
#import "OFString.h"

#import "OFOutOfRangeException.h"

@implementation OFPointValue
@synthesize pointValue = _point;

- (instancetype)initWithPoint: (OFPoint)point
{
	self = [super init];

	_point = point;

	return self;
}

- (const char *)objCType
{
	return @encode(OFPoint);
}

- (void)getValue: (void *)value size: (size_t)size

{
	if (size != sizeof(_point))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_point, sizeof(_point));
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFValue: OFPoint { %f, %f }>", _point.x, _point.y];
}
@end

Modified src/OFPointerValue.h from [3cdf748dae] to [9280ad762d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25


26
27
28

OF_ASSUME_NONNULL_BEGIN

@interface OFPointerValue: OFValue
{
	void *_pointer;
}


@end

OF_ASSUME_NONNULL_END







>
>



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

OF_ASSUME_NONNULL_BEGIN

@interface OFPointerValue: OFValue
{
	void *_pointer;
}

- (instancetype)initWithPointer: (const void *)pointer;
@end

OF_ASSUME_NONNULL_END

Modified src/OFPointerValue.m from [2773c818f6] to [0b6a62e87f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
}

- (const char *)objCType
{
	return @encode(void *);
}

- (void)getValue: (void *)value
	    size: (size_t)size
{
	if (size != sizeof(_pointer))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_pointer, sizeof(_pointer));
}

- (id)nonretainedObjectValue
{
	return _pointer;
}
@end







|
<












31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
}

- (const char *)objCType
{
	return @encode(void *);
}

- (void)getValue: (void *)value size: (size_t)size

{
	if (size != sizeof(_pointer))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_pointer, sizeof(_pointer));
}

- (id)nonretainedObjectValue
{
	return _pointer;
}
@end

Modified src/OFPollKernelEventObserver.h from [0dbb6114a3] to [1b31db8247].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFPollKernelEventObserver.m from [5ca69d7bfc] to [458ef336b0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#ifdef HAVE_POLL_H
# include <poll.h>
#endif

#import "OFPollKernelEventObserver.h"
#import "OFData.h"


#import "OFObserveFailedException.h"
#import "OFOutOfRangeException.h"

#import "socket_helpers.h"

#ifdef OF_WII
# define pollfd pollsd
# define fd socket
#endif

@implementation OFPollKernelEventObserver
- (instancetype)init
{
	self = [super init];

	@try {
		struct pollfd p = { _cancelFD[0], POLLIN, 0 };

		_FDs = [[OFMutableData alloc] initWithItemSize:
		    sizeof(struct pollfd)];
		[_FDs addItem: &p];

		_maxFD = _cancelFD[0];
		_FDToObject = [self allocMemoryWithSize: sizeof(id)
						  count: (size_t)_maxFD + 1];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_FDs release];


	[super dealloc];
}

- (void)of_addObject: (id)object
      fileDescriptor: (int)fd
	      events: (short)events OF_DIRECT
{
	struct pollfd *FDs;
	size_t count;
	bool found;

	if (fd < 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EBADF];

	FDs = _FDs.mutableItems;
	count = _FDs.count;
	found = false;

	for (size_t i = 0; i < count; i++) {
		if (FDs[i].fd == fd) {
			FDs[i].events |= events;
			found = true;
			break;
		}
	}

	if (!found) {
		struct pollfd p = { fd, events, 0 };

		if (fd > _maxFD) {
			_maxFD = fd;
			_FDToObject = [self resizeMemory: _FDToObject
						    size: sizeof(id)
						   count: (size_t)_maxFD + 1];
		}

		_FDToObject[fd] = object;
		[_FDs addItem: &p];
	}
}

- (void)of_removeObject: (id)object
	 fileDescriptor: (int)fd
		 events: (short)events OF_DIRECT
{
	struct pollfd *FDs;
	size_t nFDs;

	if (fd < 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EBADF];

	FDs = _FDs.mutableItems;
	nFDs = _FDs.count;

	for (size_t i = 0; i < nFDs; i++) {
		if (FDs[i].fd == fd) {
			FDs[i].events &= ~events;

			if (FDs[i].events == 0) {
				/*
				 * TODO: Remove from and resize _FDToObject,
				 *	 adjust _maxFD.
				 */
				[_FDs removeItemAtIndex: i];
			}

			break;
		}
	}
}

- (void)addObjectForReading: (id <OFReadyForReadingObserving>)object
{
	[self of_addObject: object
	    fileDescriptor: object.fileDescriptorForReading
		    events: POLLIN];

	[super addObjectForReading: object];
}

- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	[self of_addObject: object
	    fileDescriptor: object.fileDescriptorForWriting
		    events: POLLOUT];

	[super addObjectForWriting: object];
}

- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object
{
	[self of_removeObject: object
	       fileDescriptor: object.fileDescriptorForReading
		       events: POLLIN];

	[super removeObjectForReading: object];
}

- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	[self of_removeObject: object
	       fileDescriptor: object.fileDescriptorForWriting
		       events: POLLOUT];

	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (of_time_interval_t)timeInterval
{
	void *pool;
	struct pollfd *FDs;
	int events;
	size_t nFDs;

	if ([self of_processReadBuffers])







>




<
<


















<
|











>




|
<
|









|
|













|
|
|
<
|


|
|



|
<
|








|
|










|









<
|
<






<
|
<






<
|
<






<
|
<




|







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

#ifdef HAVE_POLL_H
# include <poll.h>
#endif

#import "OFPollKernelEventObserver.h"
#import "OFData.h"
#import "OFSocket+Private.h"

#import "OFObserveFailedException.h"
#import "OFOutOfRangeException.h"



#ifdef OF_WII
# define pollfd pollsd
# define fd socket
#endif

@implementation OFPollKernelEventObserver
- (instancetype)init
{
	self = [super init];

	@try {
		struct pollfd p = { _cancelFD[0], POLLIN, 0 };

		_FDs = [[OFMutableData alloc] initWithItemSize:
		    sizeof(struct pollfd)];
		[_FDs addItem: &p];

		_maxFD = _cancelFD[0];

		_FDToObject = OFAllocMemory((size_t)_maxFD + 1, sizeof(id));
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_FDs release];
	OFFreeMemory(_FDToObject);

	[super dealloc];
}

static void

addObject(OFPollKernelEventObserver *self, id object, int fd, short events)
{
	struct pollfd *FDs;
	size_t count;
	bool found;

	if (fd < 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EBADF];

	FDs = self->_FDs.mutableItems;
	count = self->_FDs.count;
	found = false;

	for (size_t i = 0; i < count; i++) {
		if (FDs[i].fd == fd) {
			FDs[i].events |= events;
			found = true;
			break;
		}
	}

	if (!found) {
		struct pollfd p = { fd, events, 0 };

		if (fd > self->_maxFD) {
			self->_maxFD = fd;
			self->_FDToObject = OFResizeMemory(self->_FDToObject,

			    (size_t)self->_maxFD + 1, sizeof(id));
		}

		self->_FDToObject[fd] = object;
		[self->_FDs addItem: &p];
	}
}

static void

removeObject(OFPollKernelEventObserver *self, id object, int fd, short events)
{
	struct pollfd *FDs;
	size_t nFDs;

	if (fd < 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EBADF];

	FDs = self->_FDs.mutableItems;
	nFDs = self->_FDs.count;

	for (size_t i = 0; i < nFDs; i++) {
		if (FDs[i].fd == fd) {
			FDs[i].events &= ~events;

			if (FDs[i].events == 0) {
				/*
				 * TODO: Remove from and resize _FDToObject,
				 *	 adjust _maxFD.
				 */
				[self->_FDs removeItemAtIndex: i];
			}

			break;
		}
	}
}

- (void)addObjectForReading: (id <OFReadyForReadingObserving>)object
{

	addObject(self, object, object.fileDescriptorForReading, POLLIN);


	[super addObjectForReading: object];
}

- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object
{

	addObject(self, object, object.fileDescriptorForWriting, POLLOUT);


	[super addObjectForWriting: object];
}

- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object
{

	removeObject(self, object, object.fileDescriptorForReading, POLLIN);


	[super removeObjectForReading: object];
}

- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object
{

	removeObject(self, object, object.fileDescriptorForWriting, POLLOUT);


	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (OFTimeInterval)timeInterval
{
	void *pool;
	struct pollfd *FDs;
	int events;
	size_t nFDs;

	if ([self of_processReadBuffers])
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
		if (FDs[i].revents & POLLIN) {
			void *pool2;

			if (FDs[i].fd == _cancelFD[0]) {
				char buffer;

#ifdef OF_HAVE_PIPE
				OF_ENSURE(read(_cancelFD[0], &buffer, 1) == 1);
#else
				OF_ENSURE(recvfrom(_cancelFD[0], &buffer, 1,
				    0, NULL, NULL) == 1);
#endif
				FDs[i].revents = 0;

				continue;
			}

			pool2 = objc_autoreleasePoolPush();







|

|
|







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
		if (FDs[i].revents & POLLIN) {
			void *pool2;

			if (FDs[i].fd == _cancelFD[0]) {
				char buffer;

#ifdef OF_HAVE_PIPE
				OFEnsure(read(_cancelFD[0], &buffer, 1) == 1);
#else
				OFEnsure(recvfrom(_cancelFD[0], &buffer, 1, 0,
				    NULL, NULL) == 1);
#endif
				FDs[i].revents = 0;

				continue;
			}

			pool2 = objc_autoreleasePoolPush();

Modified src/OFRIPEMD160Hash.h from [2ffc431b34] to [6a4a8d87d4].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCryptoHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFRIPEMD160Hash OFRIPEMD160Hash.h ObjFW/OFRIPEMD160Hash.h
 *
 * @brief A class which provides methods to create a RIPEMD-160 hash.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFRIPEMD160Hash: OFObject <OFCryptoHash>
{
	OFSecureData *_iVarsData;
	struct of_ripemd160_hash_ivars {
		uint32_t state[5];
		uint64_t bits;
		union of_ripemd160_hash_buffer {
			unsigned char bytes[64];
			uint32_t words[16];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

<
<
|













|











|


|


|











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


 * 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 "OFCryptographicHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFRIPEMD160Hash OFRIPEMD160Hash.h ObjFW/OFRIPEMD160Hash.h
 *
 * @brief A class which provides methods to create a RIPEMD-160 hash.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFRIPEMD160Hash: OFObject <OFCryptographicHash>
{
	OFSecureData *_iVarsData;
	struct {
		uint32_t state[5];
		uint64_t bits;
		union {
			unsigned char bytes[64];
			uint32_t words[16];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFRIPEMD160Hash.m from [f5c31d29f5] to [a6c68d768d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25

26
27
28
29

30
31
32
33
34
35
36

#include <string.h>

#import "OFRIPEMD160Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"

#import "OFOutOfRangeException.h"

#define DIGEST_SIZE 20
#define BLOCK_SIZE 64


OF_DIRECT_MEMBERS
@interface OFRIPEMD160Hash ()
- (void)of_resetState;
@end

#define F(a, b, c) ((a) ^ (b) ^ (c))







>


<
|
>







17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35

#include <string.h>

#import "OFRIPEMD160Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"
#import "OFHashNotCalculatedException.h"
#import "OFOutOfRangeException.h"


static const size_t digestSize = 20;
static const size_t blockSize = 64;

OF_DIRECT_MEMBERS
@interface OFRIPEMD160Hash ()
- (void)of_resetState;
@end

#define F(a, b, c) ((a) ^ (b) ^ (c))
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
};

static OF_INLINE void
byteSwapVectorIfBE(uint32_t *vector, uint_fast8_t length)
{
#ifdef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OF_BSWAP32(vector[i]);
#endif
}

static void
processBlock(uint32_t *state, uint32_t *buffer)
{
	uint32_t new[5], new2[5];
	uint_fast8_t i = 0;

	new[0] = new2[0] = state[0];
	new[1] = new2[1] = state[1];
	new[2] = new2[2] = state[2];
	new[3] = new2[3] = state[3];
	new[4] = new2[4] = state[4];

	byteSwapVectorIfBE(buffer, 16);

#define LOOP_BODY(f, g, k, k2)					\
	{							\
		uint32_t tmp;					\
								\
		tmp = new[0] + f(new[1], new[2], new[3]) +	\
		    buffer[wordOrder[i]] + k;			\
		tmp = OF_ROL(tmp, rotateBits[i]) + new[4];	\
								\
		new[0] = new[4];				\
		new[4] = new[3];				\
		new[3] = OF_ROL(new[2], 10);			\
		new[2] = new[1];				\
		new[1] = tmp;					\
								\
		tmp = new2[0] + g(new2[1], new2[2], new2[3]) +	\
		    buffer[wordOrder2[i]] + k2;			\
		tmp = OF_ROL(tmp, rotateBits2[i]) + new2[4];	\
								\
		new2[0] = new2[4];				\
		new2[4] = new2[3];				\
		new2[3] = OF_ROL(new2[2], 10);			\
		new2[2] = new2[1];				\
		new2[1] = tmp;					\
	}

	for (; i < 16; i++)
		LOOP_BODY(F, J, 0x00000000, 0x50A28BE6)
	for (; i < 32; i++)
		LOOP_BODY(G, I, 0x5A827999, 0x5C4DD124)
	for (; i < 48; i++)







|

















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







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

static OF_INLINE void
byteSwapVectorIfBE(uint32_t *vector, uint_fast8_t length)
{
#ifdef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OFByteSwap32(vector[i]);
#endif
}

static void
processBlock(uint32_t *state, uint32_t *buffer)
{
	uint32_t new[5], new2[5];
	uint_fast8_t i = 0;

	new[0] = new2[0] = state[0];
	new[1] = new2[1] = state[1];
	new[2] = new2[2] = state[2];
	new[3] = new2[3] = state[3];
	new[4] = new2[4] = state[4];

	byteSwapVectorIfBE(buffer, 16);

#define LOOP_BODY(f, g, k, k2)						\
	{								\
		uint32_t tmp;						\
									\
		tmp = new[0] + f(new[1], new[2], new[3]) +		\
		    buffer[wordOrder[i]] + k;				\
		tmp = OFRotateLeft(tmp, rotateBits[i]) + new[4];	\
									\
		new[0] = new[4];					\
		new[4] = new[3];					\
		new[3] = OFRotateLeft(new[2], 10);			\
		new[2] = new[1];					\
		new[1] = tmp;						\
									\
		tmp = new2[0] + g(new2[1], new2[2], new2[3]) +		\
		    buffer[wordOrder2[i]] + k2;				\
		tmp = OFRotateLeft(tmp, rotateBits2[i]) + new2[4];	\
									\
		new2[0] = new2[4];					\
		new2[4] = new2[3];					\
		new2[3] = OFRotateLeft(new2[2], 10);			\
		new2[2] = new2[1];					\
		new2[1] = tmp;						\
	}

	for (; i < 16; i++)
		LOOP_BODY(F, J, 0x00000000, 0x50A28BE6)
	for (; i < 32; i++)
		LOOP_BODY(G, I, 0x5A827999, 0x5C4DD124)
	for (; i < 48; i++)
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

@implementation OFRIPEMD160Hash
@synthesize calculated = _calculated;
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

+ (size_t)digestSize
{
	return DIGEST_SIZE;
}

+ (size_t)blockSize
{
	return BLOCK_SIZE;
}

+ (instancetype)cryptoHashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{







|




|


|







138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

@implementation OFRIPEMD160Hash
@synthesize calculated = _calculated;
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

+ (size_t)digestSize
{
	return digestSize;
}

+ (size_t)blockSize
{
	return blockSize;
}

+ (instancetype)hashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
	[_iVarsData release];

	[super dealloc];
}

- (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (size_t)blockSize
{
	return BLOCK_SIZE;
}

- (id)copy
{
	OFRIPEMD160Hash *copy = [[OFRIPEMD160Hash alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];







|




|







191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
	[_iVarsData release];

	[super dealloc];
}

- (size_t)digestSize
{
	return digestSize;
}

- (size_t)blockSize
{
	return blockSize;
}

- (id)copy
{
	OFRIPEMD160Hash *copy = [[OFRIPEMD160Hash alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
	_iVars->state[0] = 0x67452301;
	_iVars->state[1] = 0xEFCDAB89;
	_iVars->state[2] = 0x98BADCFE;
	_iVars->state[3] = 0x10325476;
	_iVars->state[4] = 0xC3D2E1F0;
}

- (void)updateWithBuffer: (const void *)buffer_
		  length: (size_t)length
{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];








|
<







220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
	_iVars->state[0] = 0x67452301;
	_iVars->state[1] = 0xEFCDAB89;
	_iVars->state[2] = 0x98BADCFE;
	_iVars->state[3] = 0x10325476;
	_iVars->state[4] = 0xC3D2E1F0;
}

- (void)updateWithBuffer: (const void *)buffer_ length: (size_t)length

{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (_calculated)


		return (const unsigned char *)_iVars->state;








	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	of_explicit_memset(_iVars->buffer.bytes + _iVars->bufferLength + 1, 0,
	    64 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 56) {
		processBlock(_iVars->state, _iVars->buffer.words);
		of_explicit_memset(_iVars->buffer.bytes, 0, 64);
	}

	_iVars->buffer.words[14] =
	    OF_BSWAP32_IF_BE((uint32_t)(_iVars->bits & 0xFFFFFFFF));
	_iVars->buffer.words[15] =
	    OF_BSWAP32_IF_BE((uint32_t)(_iVars->bits >> 32));

	processBlock(_iVars->state, _iVars->buffer.words);
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	byteSwapVectorIfBE(_iVars->state, 5);
	_calculated = true;

	return (const unsigned char *)_iVars->state;
}

- (void)reset
{
	[self of_resetState];
	_iVars->bits = 0;
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}
@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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (!_calculated)
		@throw [OFHashNotCalculatedException exceptionWithObject: self];

	return (const unsigned char *)_iVars->state;
}

- (void)calculate
{
	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	OFZeroMemory(_iVars->buffer.bytes + _iVars->bufferLength + 1,
	    64 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 56) {
		processBlock(_iVars->state, _iVars->buffer.words);
		OFZeroMemory(_iVars->buffer.bytes, 64);
	}

	_iVars->buffer.words[14] =
	    OFToLittleEndian32((uint32_t)(_iVars->bits & 0xFFFFFFFF));
	_iVars->buffer.words[15] =
	    OFToLittleEndian32((uint32_t)(_iVars->bits >> 32));

	processBlock(_iVars->state, _iVars->buffer.words);
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	byteSwapVectorIfBE(_iVars->state, 5);
	_calculated = true;


}

- (void)reset
{
	[self of_resetState];
	_iVars->bits = 0;
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}
@end

Modified src/OFRangeCharacterSet.h from [037c8088c6] to [c903dba907].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCharacterSet.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFRangeCharacterSet: OFCharacterSet
{
	of_range_t _range;
}
@end

OF_ASSUME_NONNULL_END

<
<
|



















|




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


 * 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 "OFCharacterSet.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFRangeCharacterSet: OFCharacterSet
{
	OFRange _range;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFRangeCharacterSet.m from [bec25adcc7] to [4870541066].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

@implementation OFRangeCharacterSet
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRange: (of_range_t)range
{
	self = [super init];

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

		_range = range;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (bool)characterIsMember: (of_unichar_t)character
{
	return (character >= _range.location &&
	    character < _range.location + _range.length);
}
@end







|
















|





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

@implementation OFRangeCharacterSet
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRange: (OFRange)range
{
	self = [super init];

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

		_range = range;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (bool)characterIsMember: (OFUnichar)character
{
	return (character >= _range.location &&
	    character < _range.location + _range.length);
}
@end

Modified src/OFRangeValue.h from [085216fa6c] to [3251ae1de3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFValue.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFRangeValue: OFValue
{
	of_range_t _range;
}


@end

OF_ASSUME_NONNULL_END

<
<
|



















|

>
>



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


 * 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 "OFValue.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFRangeValue: OFValue
{
	OFRange _range;
}

- (instancetype)initWithRange: (OFRange)range;
@end

OF_ASSUME_NONNULL_END

Modified src/OFRangeValue.m from [36416bfa67] to [72e3ffee04].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFString.h"

#import "OFOutOfRangeException.h"

@implementation OFRangeValue
@synthesize rangeValue = _range;

- (instancetype)initWithRange: (of_range_t)range
{
	self = [super init];

	_range = range;

	return self;
}

- (const char *)objCType
{
	return @encode(of_range_t);
}

- (void)getValue: (void *)value
	    size: (size_t)size
{
	if (size != sizeof(_range))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_range, sizeof(_range));
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFValue: of_range_t { %zu, %zu }>",
	    _range.location, _range.length];
}
@end







|










|


|
<










|



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
#import "OFString.h"

#import "OFOutOfRangeException.h"

@implementation OFRangeValue
@synthesize rangeValue = _range;

- (instancetype)initWithRange: (OFRange)range
{
	self = [super init];

	_range = range;

	return self;
}

- (const char *)objCType
{
	return @encode(OFRange);
}

- (void)getValue: (void *)value size: (size_t)size

{
	if (size != sizeof(_range))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_range, sizeof(_range));
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFValue: OFRange { %zu, %zu }>",
	    _range.location, _range.length];
}
@end

Renamed and modified src/OFRectangleValue.h [2ca7936a53] to src/OFRectValue.h [83e8354a4a].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFValue.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFRectangleValue: OFValue
{
	of_rectangle_t _rectangle;
}


@end

OF_ASSUME_NONNULL_END

<
<
|

















|

|

>
>



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


 * 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 "OFValue.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFRectValue: OFValue
{
	OFRect _rect;
}

- (instancetype)initWithRect: (OFRect)rect;
@end

OF_ASSUME_NONNULL_END

Renamed and modified src/OFRectangleValue.m [ed79263f8b] to src/OFRectValue.m [0064a9b262].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFRectangleValue.h"
#import "OFMethodSignature.h"
#import "OFString.h"

#import "OFOutOfRangeException.h"

@implementation OFRectangleValue
@synthesize rectangleValue = _rectangle;

- (instancetype)initWithRectangle: (of_rectangle_t)rectangle
{
	self = [super init];

	_rectangle = rectangle;

	return self;
}

- (const char *)objCType
{
	return @encode(of_rectangle_t);
}

- (void)getValue: (void *)value
	    size: (size_t)size
{
	if (size != sizeof(_rectangle))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_rectangle, sizeof(_rectangle));
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFValue: of_rectangle_t { %f, %f, %f, %f }>",
	    _rectangle.origin.x, _rectangle.origin.y,
	    _rectangle.size.width, _rectangle.size.height];
}
@end

<
<
|













|





|
|

|



|






|


|
<

|


|





|
|
|


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


 * 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 "OFRectValue.h"
#import "OFMethodSignature.h"
#import "OFString.h"

#import "OFOutOfRangeException.h"

@implementation OFRectValue
@synthesize rectValue = _rect;

- (instancetype)initWithRect: (OFRect)rect
{
	self = [super init];

	_rect = rect;

	return self;
}

- (const char *)objCType
{
	return @encode(OFRect);
}

- (void)getValue: (void *)value size: (size_t)size

{
	if (size != sizeof(_rect))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_rect, sizeof(_rect));
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFValue: OFRect { %f, %f, %f, %f }>",
	    _rect.origin.x, _rect.origin.y,
	    _rect.size.width, _rect.size.height];
}
@end

Modified src/OFRecursiveMutex.h from [ebcc3c2a34] to [5662e5ef58].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFLocking.h"

#import "mutex.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFRecursiveMutex OFRecursiveMutex.h ObjFW/OFRecursiveMutex.h
 *
 * @brief A class for creating mutual exclusions which can be entered
 *	  recursively.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFRecursiveMutex: OFObject <OFLocking>
{
	of_rmutex_t _rmutex;
	bool _initialized;
	OFString *_Nullable _name;
}

/**
 * @brief Creates a new recursive mutex.
 *
 * @return A new autoreleased recursive mutex.
 */
+ (instancetype)mutex;
@end

OF_ASSUME_NONNULL_END

<
<
|















<
|












|













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


 * 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 "OFLocking.h"

#import "OFPlainMutex.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFRecursiveMutex OFRecursiveMutex.h ObjFW/OFRecursiveMutex.h
 *
 * @brief A class for creating mutual exclusions which can be entered
 *	  recursively.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFRecursiveMutex: OFObject <OFLocking>
{
	OFPlainRecursiveMutex _rmutex;
	bool _initialized;
	OFString *_Nullable _name;
}

/**
 * @brief Creates a new recursive mutex.
 *
 * @return A new autoreleased recursive mutex.
 */
+ (instancetype)mutex;
@end

OF_ASSUME_NONNULL_END

Modified src/OFRecursiveMutex.m from [3739c89b37] to [cb58226e3c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	if (!of_rmutex_new(&_rmutex)) {
		Class c = self.class;
		[self release];
		@throw [OFInitializationFailedException exceptionWithClass: c];
	}

	_initialized = true;

	return self;
}

- (void)dealloc
{
	if (_initialized) {

		if (!of_rmutex_free(&_rmutex)) {

			OF_ENSURE(errno == EBUSY);

			@throw [OFStillLockedException exceptionWithLock: self];
		}
	}

	[_name release];

	[super dealloc];
}

- (void)lock
{

	if (!of_rmutex_lock(&_rmutex))

		@throw [OFLockFailedException exceptionWithLock: self
							  errNo: errno];
}

- (bool)tryLock
{

	if (!of_rmutex_trylock(&_rmutex)) {

		if (errno == EBUSY)
			return false;
		else
			@throw [OFLockFailedException exceptionWithLock: self
								  errNo: errno];
	}

	return true;
}

- (void)unlock
{

	if (!of_rmutex_unlock(&_rmutex))

		@throw [OFUnlockFailedException exceptionWithLock: self
							    errNo: errno];
}

- (OFString *)description
{
	if (_name == nil)
		return super.description;

	return [OFString stringWithFormat: @"<%@: %@>", self.className, _name];
}
@end







|













>
|
>
|












>
|
>

|




>
|
>
|



|







>
|
>

|










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
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	if (OFPlainRecursiveMutexNew(&_rmutex) != 0) {
		Class c = self.class;
		[self release];
		@throw [OFInitializationFailedException exceptionWithClass: c];
	}

	_initialized = true;

	return self;
}

- (void)dealloc
{
	if (_initialized) {
		int error = OFPlainRecursiveMutexFree(&_rmutex);

		if (error != 0) {
			OFEnsure(error == EBUSY);

			@throw [OFStillLockedException exceptionWithLock: self];
		}
	}

	[_name release];

	[super dealloc];
}

- (void)lock
{
	int error = OFPlainRecursiveMutexLock(&_rmutex);

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

- (bool)tryLock
{
	int error = OFPlainRecursiveMutexTryLock(&_rmutex);

	if (error != 0) {
		if (error == EBUSY)
			return false;
		else
			@throw [OFLockFailedException exceptionWithLock: self
								  errNo: error];
	}

	return true;
}

- (void)unlock
{
	int error = OFPlainRecursiveMutexUnlock(&_rmutex);

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

- (OFString *)description
{
	if (_name == nil)
		return super.description;

	return [OFString stringWithFormat: @"<%@: %@>", self.className, _name];
}
@end

Modified src/OFRunLoop+Private.h from [979d7c9f7e] to [1af8d65653].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@interface OFRunLoop ()
+ (void)of_setMainRunLoop: (OFRunLoop *)runLoop;
#ifdef OF_HAVE_SOCKETS
+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
			  length: (size_t)length
			    mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			   block: (nullable of_stream_async_read_block_t)block
# endif
			delegate: (nullable id <OFStreamDelegate>)delegate;
+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
		     exactLength: (size_t)length
			    mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			   block: (nullable of_stream_async_read_block_t)block
# endif
			delegate: (nullable id <OFStreamDelegate>)delegate;
+ (void)of_addAsyncReadLineForStream: (OFStream <OFReadyForReadingObserving> *)
					  stream
			    encoding: (of_string_encoding_t)encoding
				mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			       block: (nullable
					  of_stream_async_read_line_block_t)
					  block
# endif
			    delegate: (nullable id <OFStreamDelegate>)delegate;
+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			     data: (OFData *)data
			     mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			    block: (nullable of_stream_async_write_data_block_t)
				       block
# endif
			 delegate: (nullable id <OFStreamDelegate>)delegate;
+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			   string: (OFString *)string
			 encoding: (of_string_encoding_t)encoding
			     mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			    block: (nullable
				       of_stream_async_write_string_block_t)
				       block
# endif
			 delegate: (nullable id <OFStreamDelegate>)delegate;
# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
+ (void)of_addAsyncConnectForSocket: (id)socket
			       mode: (of_run_loop_mode_t)mode
			   delegate: (id <OFRunLoopConnectDelegate>)delegate;
# endif
+ (void)of_addAsyncAcceptForSocket: (id)socket
			      mode: (of_run_loop_mode_t)mode
			     block: (nullable id)block
			  delegate: (nullable id)delegate;
+ (void)of_addAsyncReceiveForDatagramSocket: (OFDatagramSocket *)socket
    buffer: (void *)buffer
    length: (size_t)length
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
     block: (nullable of_datagram_socket_async_receive_block_t)block
# endif
  delegate: (nullable id <OFDatagramSocketDelegate>) delegate;
+ (void)of_addAsyncSendForDatagramSocket: (OFDatagramSocket *)socket
      data: (OFData *)data
  receiver: (const of_socket_address_t *)receiver
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
     block: (nullable of_datagram_socket_async_send_data_block_t)block
# endif
  delegate: (nullable id <OFDatagramSocketDelegate>)delegate;
+ (void)of_addAsyncReceiveForSequencedPacketSocket:
					       (OFSequencedPacketSocket *)socket
    buffer: (void *)buffer
    length: (size_t)length
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
     block: (nullable of_sequenced_packet_socket_async_receive_block_t)block
# endif
  delegate: (nullable id <OFSequencedPacketSocketDelegate>) delegate;
+ (void)of_addAsyncSendForSequencedPacketSocket:
					       (OFSequencedPacketSocket *)socket
      data: (OFData *)data
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
     block: (nullable of_sequenced_packet_socket_async_send_data_block_t)block
# endif
  delegate: (nullable id <OFSequencedPacketSocketDelegate>)delegate;
+ (void)of_cancelAsyncRequestsForObject: (id)object
				   mode: (of_run_loop_mode_t)mode;
#endif
- (void)of_removeTimer: (OFTimer *)timer
	       forMode: (of_run_loop_mode_t)mode;
@end

OF_ASSUME_NONNULL_END







|

|






|

|




|
|

|
<
<





|

|
<





|
|

|
<
<




|



|





|

|




|
|

|






|

|





|

|


|
<

|
<



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
@interface OFRunLoop ()
+ (void)of_setMainRunLoop: (OFRunLoop *)runLoop;
#ifdef OF_HAVE_SOCKETS
+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
			  length: (size_t)length
			    mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			   block: (nullable OFStreamAsyncReadBlock)block
# endif
			delegate: (nullable id <OFStreamDelegate>)delegate;
+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
		     exactLength: (size_t)length
			    mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			   block: (nullable OFStreamAsyncReadBlock)block
# endif
			delegate: (nullable id <OFStreamDelegate>)delegate;
+ (void)of_addAsyncReadLineForStream: (OFStream <OFReadyForReadingObserving> *)
					  stream
			    encoding: (OFStringEncoding)encoding
				mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			       block: (nullable OFStreamAsyncReadLineBlock)block


# endif
			    delegate: (nullable id <OFStreamDelegate>)delegate;
+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			     data: (OFData *)data
			     mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			    block: (nullable OFStreamAsyncWriteDataBlock)block

# endif
			 delegate: (nullable id <OFStreamDelegate>)delegate;
+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			   string: (OFString *)string
			 encoding: (OFStringEncoding)encoding
			     mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			    block: (nullable OFStreamAsyncWriteStringBlock)block


# endif
			 delegate: (nullable id <OFStreamDelegate>)delegate;
# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
+ (void)of_addAsyncConnectForSocket: (id)socket
			       mode: (OFRunLoopMode)mode
			   delegate: (id <OFRunLoopConnectDelegate>)delegate;
# endif
+ (void)of_addAsyncAcceptForSocket: (id)socket
			      mode: (OFRunLoopMode)mode
			     block: (nullable id)block
			  delegate: (nullable id)delegate;
+ (void)of_addAsyncReceiveForDatagramSocket: (OFDatagramSocket *)socket
    buffer: (void *)buffer
    length: (size_t)length
      mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
     block: (nullable OFDatagramSocketAsyncReceiveBlock)block
# endif
  delegate: (nullable id <OFDatagramSocketDelegate>) delegate;
+ (void)of_addAsyncSendForDatagramSocket: (OFDatagramSocket *)socket
      data: (OFData *)data
  receiver: (const OFSocketAddress *)receiver
      mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
     block: (nullable OFDatagramSocketAsyncSendDataBlock)block
# endif
  delegate: (nullable id <OFDatagramSocketDelegate>)delegate;
+ (void)of_addAsyncReceiveForSequencedPacketSocket:
					       (OFSequencedPacketSocket *)socket
    buffer: (void *)buffer
    length: (size_t)length
      mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
     block: (nullable OFSequencedPacketSocketAsyncReceiveBlock)block
# endif
  delegate: (nullable id <OFSequencedPacketSocketDelegate>) delegate;
+ (void)of_addAsyncSendForSequencedPacketSocket:
					       (OFSequencedPacketSocket *)socket
      data: (OFData *)data
      mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
     block: (nullable OFSequencedPacketSocketAsyncSendDataBlock)block
# endif
  delegate: (nullable id <OFSequencedPacketSocketDelegate>)delegate;
+ (void)of_cancelAsyncRequestsForObject: (id)object mode: (OFRunLoopMode)mode;

#endif
- (void)of_removeTimer: (OFTimer *)timer forMode: (OFRunLoopMode)mode;

@end

OF_ASSUME_NONNULL_END

Modified src/OFRunLoop.h from [0cb286823e] to [e058e5f18b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFTimer;
@class OFDate;

/**
 * @brief A mode for an OFRunLoop.
 */
typedef OFConstantString *of_run_loop_mode_t;

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief The default mode for an OFRunLoop.
 */
extern const of_run_loop_mode_t of_run_loop_mode_default;
#ifdef __cplusplus
}
#endif

/**
 * @class OFRunLoop OFRunLoop.h ObjFW/OFRunLoop.h
 *
 * @brief A class providing a run loop for the application and its processes.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFRunLoop: OFObject
{
	OFMutableDictionary *_states;
#ifdef OF_HAVE_THREADS
	OFMutex *_statesMutex;
#endif
	of_run_loop_mode_t _Nullable _currentMode;
	volatile bool _stop;
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nullable, nonatomic) OFRunLoop *mainRunLoop;
@property (class, readonly, nullable, nonatomic) OFRunLoop *currentRunLoop;
#endif
@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
    of_run_loop_mode_t currentMode;

/**
 * @brief Returns the run loop for the main thread.
 *
 * @return The run loop for the main thread
 */
+ (nullable OFRunLoop *)mainRunLoop;







|







|
















|







|
<







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
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFTimer;
@class OFDate;

/**
 * @brief A mode for an OFRunLoop.
 */
typedef OFConstantString *OFRunLoopMode;

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief The default mode for an OFRunLoop.
 */
extern const OFRunLoopMode OFDefaultRunLoopMode;
#ifdef __cplusplus
}
#endif

/**
 * @class OFRunLoop OFRunLoop.h ObjFW/OFRunLoop.h
 *
 * @brief A class providing a run loop for the application and its processes.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFRunLoop: OFObject
{
	OFMutableDictionary *_states;
#ifdef OF_HAVE_THREADS
	OFMutex *_statesMutex;
#endif
	OFRunLoopMode _Nullable _currentMode;
	volatile bool _stop;
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nullable, nonatomic) OFRunLoop *mainRunLoop;
@property (class, readonly, nullable, nonatomic) OFRunLoop *currentRunLoop;
#endif
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFRunLoopMode currentMode;


/**
 * @brief Returns the run loop for the main thread.
 *
 * @return The run loop for the main thread
 */
+ (nullable OFRunLoop *)mainRunLoop;
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

/**
 * @brief Adds an OFTimer to the run loop for the specified mode.
 *
 * @param timer The timer to add
 * @param mode The run loop mode in which to run the timer
 */
- (void)addTimer: (OFTimer *)timer
	 forMode: (of_run_loop_mode_t)mode;

#ifdef OF_AMIGAOS
/**
 * @brief Adds an Exec Signal to the run loop.
 *
 * If a signal is added multiple times, the specified methods will be performed
 * in the order added.
 *
 * @note This is only available on AmigaOS!
 *
 * @param signal The signal to add
 * @param target The target to call when the signal was received
 * @param selector The selector to call on the target when the signal was
 *		   received. The selector must have one parameter for the ULONG
 *		   of the signal that was received.
 */
- (void)addExecSignal: (ULONG)signal
	       target: (id)target
	     selector: (SEL)selector;

/**
 * @brief Adds an Exec Signal to the run loop for the specified mode.
 *
 * If a signal is added multiple times, the specified methods will be performed
 * in the order added.
 *
 * @note This is only available on AmigaOS!
 *
 * @param signal The signal to add
 * @param mode The run loop mode in which to handle the signal
 * @param target The target to call when the signal was received
 * @param selector The selector to call on the target when the signal was
 *		   received. The selector must have one parameter for the ULONG
 *		   of the signal that was received.
 */
- (void)addExecSignal: (ULONG)signal
	      forMode: (of_run_loop_mode_t)mode
	       target: (id)target
	     selector: (SEL)selector;

/**
 * @brief Removes the specified Exec Signal with the specified target and
 *	  selector.
 *







|
<
















|
<
<

















|







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

/**
 * @brief Adds an OFTimer to the run loop for the specified mode.
 *
 * @param timer The timer to add
 * @param mode The run loop mode in which to run the timer
 */
- (void)addTimer: (OFTimer *)timer forMode: (OFRunLoopMode)mode;


#ifdef OF_AMIGAOS
/**
 * @brief Adds an Exec Signal to the run loop.
 *
 * If a signal is added multiple times, the specified methods will be performed
 * in the order added.
 *
 * @note This is only available on AmigaOS!
 *
 * @param signal The signal to add
 * @param target The target to call when the signal was received
 * @param selector The selector to call on the target when the signal was
 *		   received. The selector must have one parameter for the ULONG
 *		   of the signal that was received.
 */
- (void)addExecSignal: (ULONG)signal target: (id)target selector: (SEL)selector;



/**
 * @brief Adds an Exec Signal to the run loop for the specified mode.
 *
 * If a signal is added multiple times, the specified methods will be performed
 * in the order added.
 *
 * @note This is only available on AmigaOS!
 *
 * @param signal The signal to add
 * @param mode The run loop mode in which to handle the signal
 * @param target The target to call when the signal was received
 * @param selector The selector to call on the target when the signal was
 *		   received. The selector must have one parameter for the ULONG
 *		   of the signal that was received.
 */
- (void)addExecSignal: (ULONG)signal
	      forMode: (OFRunLoopMode)mode
	       target: (id)target
	     selector: (SEL)selector;

/**
 * @brief Removes the specified Exec Signal with the specified target and
 *	  selector.
 *
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
 *
 * @param signal The signal to remove
 * @param mode The run loop mode to which the signal was added
 * @param target The target which was specified when adding the signal
 * @param selector The selector which was specified when adding the signal
 */
- (void)removeExecSignal: (ULONG)signal
		 forMode: (of_run_loop_mode_t)mode
		  target: (id)target
		selector: (SEL)selector;
#endif

/**
 * @brief Starts the run loop.
 */







|







158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
 *
 * @param signal The signal to remove
 * @param mode The run loop mode to which the signal was added
 * @param target The target which was specified when adding the signal
 * @param selector The selector which was specified when adding the signal
 */
- (void)removeExecSignal: (ULONG)signal
		 forMode: (OFRunLoopMode)mode
		  target: (id)target
		selector: (SEL)selector;
#endif

/**
 * @brief Starts the run loop.
 */
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/**
 * @brief Run the run loop until an event or timer occurs or the specified
 *	  deadline is reached.
 *
 * @param mode The mode in which to run the run loop
 * @param deadline The date until which the run loop should run at the longest
 */
- (void)runMode: (of_run_loop_mode_t)mode
     beforeDate: (nullable OFDate *)deadline;

/**
 * @brief Stops the run loop. If there is still an operation being executed, it
 *	  is finished before the run loop stops.
 */
- (void)stop;
@end

OF_ASSUME_NONNULL_END







<
|









182
183
184
185
186
187
188

189
190
191
192
193
194
195
196
197
198
/**
 * @brief Run the run loop until an event or timer occurs or the specified
 *	  deadline is reached.
 *
 * @param mode The mode in which to run the run loop
 * @param deadline The date until which the run loop should run at the longest
 */

- (void)runMode: (OFRunLoopMode)mode beforeDate: (nullable OFDate *)deadline;

/**
 * @brief Stops the run loop. If there is still an operation being executed, it
 *	  is finished before the run loop stops.
 */
- (void)stop;
@end

OF_ASSUME_NONNULL_END

Modified src/OFRunLoop.m from [ae4dcb6d52] to [e671b17f7b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
40
41
42
43
44
45
46

47

48
49
50
51
52
53
54
55
#endif
#import "OFSortedList.h"
#import "OFTimer.h"
#import "OFTimer+Private.h"
#import "OFDate.h"

#import "OFObserveFailedException.h"



const of_run_loop_mode_t of_run_loop_mode_default = @"of_run_loop_mode_default";
static OFRunLoop *mainRunLoop = nil;

@interface OFRunLoopState: OFObject
#ifdef OF_HAVE_SOCKETS
    <OFKernelEventObserverDelegate>
#endif
{







>

>
|







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#endif
#import "OFSortedList.h"
#import "OFTimer.h"
#import "OFTimer+Private.h"
#import "OFDate.h"

#import "OFObserveFailedException.h"
#import "OFWriteFailedException.h"

#include "OFRunLoopConstants.inc"

static OFRunLoop *mainRunLoop = nil;

@interface OFRunLoopState: OFObject
#ifdef OF_HAVE_SOCKETS
    <OFKernelEventObserverDelegate>
#endif
{
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
# ifdef OF_HAVE_THREADS
	OFMutex *_execSignalsMutex;
# endif
#endif
}
@end

OF_DIRECT_MEMBERS
@interface OFRunLoop ()
- (OFRunLoopState *)of_stateForMode: (of_run_loop_mode_t)mode
			     create: (bool)create;
@end

#ifdef OF_HAVE_SOCKETS
@interface OFRunLoopQueueItem: OFObject
{
@public
	id _delegate;
}

- (bool)handleObject: (id)object;
@end

@interface OFRunLoopReadQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_stream_async_read_block_t _block;
# endif
	void *_buffer;
	size_t _length;
}
@end

@interface OFRunLoopExactReadQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_stream_async_read_block_t _block;
# endif
	void *_buffer;
	size_t _exactLength, _readLength;
}
@end

@interface OFRunLoopReadLineQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_stream_async_read_line_block_t _block;
# endif
	of_string_encoding_t _encoding;
}
@end

@interface OFRunLoopWriteDataQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_stream_async_write_data_block_t _block;
# endif
	OFData *_data;
	size_t _writtenLength;
}
@end

@interface OFRunLoopWriteStringQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_stream_async_write_string_block_t _block;
# endif
	OFString *_string;
	of_string_encoding_t _encoding;
	size_t _writtenLength;
}
@end

# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
@interface OFRunLoopConnectQueueItem: OFRunLoopQueueItem
@end







<
<
<
<
<
<














|










|










|

|







|










|


|







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
# ifdef OF_HAVE_THREADS
	OFMutex *_execSignalsMutex;
# endif
#endif
}
@end







#ifdef OF_HAVE_SOCKETS
@interface OFRunLoopQueueItem: OFObject
{
@public
	id _delegate;
}

- (bool)handleObject: (id)object;
@end

@interface OFRunLoopReadQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFStreamAsyncReadBlock _block;
# endif
	void *_buffer;
	size_t _length;
}
@end

@interface OFRunLoopExactReadQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFStreamAsyncReadBlock _block;
# endif
	void *_buffer;
	size_t _exactLength, _readLength;
}
@end

@interface OFRunLoopReadLineQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFStreamAsyncReadLineBlock _block;
# endif
	OFStringEncoding _encoding;
}
@end

@interface OFRunLoopWriteDataQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFStreamAsyncWriteDataBlock _block;
# endif
	OFData *_data;
	size_t _writtenLength;
}
@end

@interface OFRunLoopWriteStringQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFStreamAsyncWriteStringBlock _block;
# endif
	OFString *_string;
	OFStringEncoding _encoding;
	size_t _writtenLength;
}
@end

# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
@interface OFRunLoopConnectQueueItem: OFRunLoopQueueItem
@end
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
}
@end

@interface OFRunLoopDatagramReceiveQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_datagram_socket_async_receive_block_t _block;
# endif
	void *_buffer;
	size_t _length;
}
@end

@interface OFRunLoopDatagramSendQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_datagram_socket_async_send_data_block_t _block;
# endif
	OFData *_data;
	of_socket_address_t _receiver;
}
@end

@interface OFRunLoopPacketReceiveQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_sequenced_packet_socket_async_receive_block_t _block;
# endif
	void *_buffer;
	size_t _length;
}
@end

@interface OFRunLoopPacketSendQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_sequenced_packet_socket_async_send_data_block_t _block;
# endif
	OFData *_data;
}
@end
#endif

@implementation OFRunLoopState







|










|


|







|










|







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
}
@end

@interface OFRunLoopDatagramReceiveQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFDatagramSocketAsyncReceiveBlock _block;
# endif
	void *_buffer;
	size_t _length;
}
@end

@interface OFRunLoopDatagramSendQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFDatagramSocketAsyncSendDataBlock _block;
# endif
	OFData *_data;
	OFSocketAddress _receiver;
}
@end

@interface OFRunLoopPacketReceiveQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFSequencedPacketSocketAsyncReceiveBlock _block;
# endif
	void *_buffer;
	size_t _length;
}
@end

@interface OFRunLoopPacketSendQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	OFSequencedPacketSocketAsyncSendDataBlock _block;
# endif
	OFData *_data;
}
@end
#endif

@implementation OFRunLoopState
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
	OFList OF_GENERIC(OF_KINDOF(OFRunLoopReadQueueItem *)) *queue =
	    [[_readQueues objectForKey: object] retain];

	assert(queue != nil);

	@try {
		if (![queue.firstObject handleObject: object]) {
			of_list_object_t *listObject = queue.firstListObject;

			/*
			 * The handler might have called -[cancelAsyncRequests]
			 * so that our queue is now empty, in which case we
			 * should do nothing.
			 */
			if (listObject != NULL) {
				/*
				 * Make sure we keep the target until after we
				 * are done removing the object. The reason for
				 * this is that the target might call
				 * -[cancelAsyncRequests] in its dealloc.
				 */

				[[listObject->object retain] autorelease];

				[queue removeListObject: listObject];

				if (queue.count == 0) {
					[_kernelEventObserver
					    removeObjectForReading: object];
					[_readQueues
					    removeObjectForKey: object];
				}







|






|






>
|

|







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
	OFList OF_GENERIC(OF_KINDOF(OFRunLoopReadQueueItem *)) *queue =
	    [[_readQueues objectForKey: object] retain];

	assert(queue != nil);

	@try {
		if (![queue.firstObject handleObject: object]) {
			OFListItem listItem = queue.firstListItem;

			/*
			 * The handler might have called -[cancelAsyncRequests]
			 * so that our queue is now empty, in which case we
			 * should do nothing.
			 */
			if (listItem != NULL) {
				/*
				 * Make sure we keep the target until after we
				 * are done removing the object. The reason for
				 * this is that the target might call
				 * -[cancelAsyncRequests] in its dealloc.
				 */
				[[OFListItemObject(listItem) retain]
				    autorelease];

				[queue removeListItem: listItem];

				if (queue.count == 0) {
					[_kernelEventObserver
					    removeObjectForReading: object];
					[_readQueues
					    removeObjectForKey: object];
				}
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
	 */
	OFList *queue = [[_writeQueues objectForKey: object] retain];

	assert(queue != nil);

	@try {
		if (![queue.firstObject handleObject: object]) {
			of_list_object_t *listObject = queue.firstListObject;

			/*
			 * The handler might have called -[cancelAsyncRequests]
			 * so that our queue is now empty, in which case we
			 * should do nothing.
			 */
			if (listObject != NULL) {
				/*
				 * Make sure we keep the target until after we
				 * are done removing the object. The reason for
				 * this is that the target might call
				 * -[cancelAsyncRequests] in its dealloc.
				 */

				[[listObject->object retain] autorelease];

				[queue removeListObject: listObject];

				if (queue.count == 0) {
					[_kernelEventObserver
					    removeObjectForWriting: object];
					[_writeQueues
					    removeObjectForKey: object];
				}







|






|






>
|

|







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
	 */
	OFList *queue = [[_writeQueues objectForKey: object] retain];

	assert(queue != nil);

	@try {
		if (![queue.firstObject handleObject: object]) {
			OFListItem listItem = queue.firstListItem;

			/*
			 * The handler might have called -[cancelAsyncRequests]
			 * so that our queue is now empty, in which case we
			 * should do nothing.
			 */
			if (listItem != NULL) {
				/*
				 * Make sure we keep the target until after we
				 * are done removing the object. The reason for
				 * this is that the target might call
				 * -[cancelAsyncRequests] in its dealloc.
				 */
				[[OFListItemObject(listItem) retain]
				    autorelease];

				[queue removeListItem: listItem];

				if (queue.count == 0) {
					[_kernelEventObserver
					    removeObjectForWriting: object];
					[_writeQueues
					    removeObjectForKey: object];
				}
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
@implementation OFRunLoopReadQueueItem
- (bool)handleObject: (id)object
{
	size_t length;
	id exception = nil;

	@try {
		length = [object readIntoBuffer: _buffer
					 length: _length];
	} @catch (id e) {
		length = 0;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)







|
<







423
424
425
426
427
428
429
430

431
432
433
434
435
436
437
@implementation OFRunLoopReadQueueItem
- (bool)handleObject: (id)object
{
	size_t length;
	id exception = nil;

	@try {
		length = [object readIntoBuffer: _buffer length: _length];

	} @catch (id e) {
		length = 0;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
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
	size_t length;
	id exception = nil;
	size_t dataLength = _data.count * _data.itemSize;
	OFData *newData, *oldData;

	@try {
		const char *dataItems = _data.items;

		length = [object writeBuffer: dataItems + _writtenLength


				      length: dataLength - _writtenLength];


	} @catch (id e) {
		length = 0;
		exception = e;
	}

	_writtenLength += length;


	if (_writtenLength != dataLength && exception == nil)
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newData = _block(_data, _writtenLength, exception);

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];







|
|
>
>
|
>
>






>






|







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
	size_t length;
	id exception = nil;
	size_t dataLength = _data.count * _data.itemSize;
	OFData *newData, *oldData;

	@try {
		const char *dataItems = _data.items;
		length = dataLength - _writtenLength;
		[object writeBuffer: dataItems + _writtenLength length: length];
	} @catch (OFWriteFailedException *e) {
		length = e.bytesWritten;

		if (e.errNo != EWOULDBLOCK && e.errNo != EAGAIN)
			exception = e;
	} @catch (id e) {
		length = 0;
		exception = e;
	}

	_writtenLength += length;
	OFEnsure(_writtenLength <= dataLength);

	if (_writtenLength != dataLength && exception == nil)
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newData = _block(_writtenLength, exception);

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];
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
	size_t length;
	id exception = nil;
	size_t cStringLength = [_string cStringLengthWithEncoding: _encoding];
	OFString *newString, *oldString;

	@try {
		const char *cString = [_string cStringWithEncoding: _encoding];

		length = [object writeBuffer: cString + _writtenLength


				      length: cStringLength - _writtenLength];


	} @catch (id e) {
		length = 0;
		exception = e;
	}

	_writtenLength += length;


	if (_writtenLength != cStringLength && exception == nil)
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newString = _block(_string, _writtenLength, exception);

		if (newString == nil)
			return false;

		oldString = _string;
		_string = [newString copy];
		[oldString release];







|
|
>
>
|
>
>






>






|







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
	size_t length;
	id exception = nil;
	size_t cStringLength = [_string cStringLengthWithEncoding: _encoding];
	OFString *newString, *oldString;

	@try {
		const char *cString = [_string cStringWithEncoding: _encoding];
		length = cStringLength - _writtenLength;
		[object writeBuffer: cString + _writtenLength length: length];
	} @catch (OFWriteFailedException *e) {
		length = e.bytesWritten;

		if (e.errNo != EWOULDBLOCK && e.errNo != EAGAIN)
			exception = e;
	} @catch (id e) {
		length = 0;
		exception = e;
	}

	_writtenLength += length;
	OFEnsure(_writtenLength <= cStringLength);

	if (_writtenLength != cStringLength && exception == nil)
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newString = _block(_writtenLength, exception);

		if (newString == nil)
			return false;

		oldString = _string;
		_string = [newString copy];
		[oldString release];
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
		    timerWithTimeInterval: 0
				   target: _delegate
				 selector: @selector(of_socketDidConnect:
					       exception:)
				   object: object
				   object: exception
				  repeats: false];

		[runLoop addTimer: timer
			  forMode: runLoop.currentMode];
	}

	return false;
}
@end
# endif








<
|
<







741
742
743
744
745
746
747

748

749
750
751
752
753
754
755
		    timerWithTimeInterval: 0
				   target: _delegate
				 selector: @selector(of_socketDidConnect:
					       exception:)
				   object: object
				   object: exception
				  repeats: false];

		[runLoop addTimer: timer forMode: runLoop.currentMode];

	}

	return false;
}
@end
# endif

761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
		acceptedSocket = nil;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		if ([object isKindOfClass: [OFStreamSocket class]])
			return ((of_stream_socket_async_accept_block_t)
			    _block)(acceptedSocket, exception);
		else if ([object isKindOfClass:
		    [OFSequencedPacketSocket class]])
			return
			    ((of_sequenced_packet_socket_async_accept_block_t)
			    _block)(acceptedSocket, exception);
		else
			OF_ENSURE(0);
	} else {
# endif
		if (![_delegate respondsToSelector:
		    @selector(socket:didAcceptSocket:exception:)])
			return false;

		return [_delegate socket: object







|
|


|
<


|







764
765
766
767
768
769
770
771
772
773
774
775

776
777
778
779
780
781
782
783
784
785
		acceptedSocket = nil;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		if ([object isKindOfClass: [OFStreamSocket class]])
			return ((OFStreamSocketAsyncAcceptBlock)_block)(
			    acceptedSocket, exception);
		else if ([object isKindOfClass:
		    [OFSequencedPacketSocket class]])
			return ((OFSequencedPacketSocketAsyncAcceptBlock)

			    _block)(acceptedSocket, exception);
		else
			OFEnsure(0);
	} else {
# endif
		if (![_delegate respondsToSelector:
		    @selector(socket:didAcceptSocket:exception:)])
			return false;

		return [_delegate socket: object
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
# endif
@end

@implementation OFRunLoopDatagramReceiveQueueItem
- (bool)handleObject: (id)object
{
	size_t length;
	of_socket_address_t address;
	id exception = nil;

	@try {
		length = [object receiveIntoBuffer: _buffer
					    length: _length
					    sender: &address];
	} @catch (id e) {







|







800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
# endif
@end

@implementation OFRunLoopDatagramReceiveQueueItem
- (bool)handleObject: (id)object
{
	size_t length;
	OFSocketAddress address;
	id exception = nil;

	@try {
		length = [object receiveIntoBuffer: _buffer
					    length: _length
					    sender: &address];
	} @catch (id e) {
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
			  receiver: &_receiver];
	} @catch (id e) {
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newData = _block(_data, &_receiver, exception);

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];







|







857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
			  receiver: &_receiver];
	} @catch (id e) {
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newData = _block(exception);

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
@implementation OFRunLoopPacketReceiveQueueItem
- (bool)handleObject: (id)object
{
	size_t length;
	id exception = nil;

	@try {
		length = [object receiveIntoBuffer: _buffer
					    length: _length];
	} @catch (id e) {
		length = 0;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)







|
<







909
910
911
912
913
914
915
916

917
918
919
920
921
922
923
@implementation OFRunLoopPacketReceiveQueueItem
- (bool)handleObject: (id)object
{
	size_t length;
	id exception = nil;

	@try {
		length = [object receiveIntoBuffer: _buffer length: _length];

	} @catch (id e) {
		length = 0;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
			    length: _data.count * _data.itemSize];
	} @catch (id e) {
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newData = _block(_data, exception);

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];







|







958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
			    length: _data.count * _data.itemSize];
	} @catch (id e) {
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newData = _block(exception);

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
#endif
}

+ (void)of_setMainRunLoop: (OFRunLoop *)runLoop
{
	mainRunLoop = [runLoop retain];
}





























#ifdef OF_HAVE_SOCKETS
# define NEW_READ(type, object, mode)					\
	void *pool = objc_autoreleasePoolPush();			\
	OFRunLoop *runLoop = [self currentRunLoop];			\
	OFRunLoopState *state = [runLoop of_stateForMode: mode		\
						  create: true];	\
	OFList *queue = [state->_readQueues objectForKey: object];	\
	type *queueItem;						\
									\
	if (queue == nil) {						\
		queue = [OFList list];					\
		[state->_readQueues setObject: queue			\
				       forKey: object];			\
	}								\
									\
	if (queue.count == 0)						\
		[state->_kernelEventObserver				\
		    addObjectForReading: object];			\
									\
	queueItem = [[[type alloc] init] autorelease];
# define NEW_WRITE(type, object, mode)					\
	void *pool = objc_autoreleasePoolPush();			\
	OFRunLoop *runLoop = [self currentRunLoop];			\
	OFRunLoopState *state = [runLoop of_stateForMode: mode		\
						  create: true];	\
	OFList *queue = [state->_writeQueues objectForKey: object];	\
	type *queueItem;						\
									\
	if (queue == nil) {						\
		queue = [OFList list];					\
		[state->_writeQueues setObject: queue			\
					  forKey: object];		\
	}								\
									\
	if (queue.count == 0)						\
		[state->_kernelEventObserver				\
		    addObjectForWriting: object];			\
									\
	queueItem = [[[type alloc] init] autorelease];
#define QUEUE_ITEM							\
	[queue appendObject: queueItem];				\
									\
	objc_autoreleasePoolPop(pool);

+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
			  length: (size_t)length
			    mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			   block: (of_stream_async_read_block_t)block
# endif
			delegate: (id <OFStreamDelegate>)delegate
{
	NEW_READ(OFRunLoopReadQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_buffer = buffer;
	queueItem->_length = length;

	QUEUE_ITEM
}

+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
		     exactLength: (size_t)exactLength
			    mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			   block: (of_stream_async_read_block_t)block
# endif
			delegate: (id <OFStreamDelegate>)delegate
{
	NEW_READ(OFRunLoopExactReadQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_buffer = buffer;
	queueItem->_exactLength = exactLength;

	QUEUE_ITEM
}

+ (void)of_addAsyncReadLineForStream: (OFStream <OFReadyForReadingObserving> *)
					  stream
			    encoding: (of_string_encoding_t)encoding
				mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			       block: (of_stream_async_read_line_block_t)block
# endif
			    delegate: (id <OFStreamDelegate>)delegate
{
	NEW_READ(OFRunLoopReadLineQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_encoding = encoding;

	QUEUE_ITEM
}

+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			     data: (OFData *)data
			     mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			    block: (of_stream_async_write_data_block_t)block
# endif
			 delegate: (id <OFStreamDelegate>)delegate
{
	NEW_WRITE(OFRunLoopWriteDataQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_data = [data copy];

	QUEUE_ITEM
}

+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			   string: (OFString *)string
			 encoding: (of_string_encoding_t)encoding
			     mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			    block: (of_stream_async_write_string_block_t)block
# endif
			 delegate: (id <OFStreamDelegate>)delegate
{
	NEW_WRITE(OFRunLoopWriteStringQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_string = [string copy];
	queueItem->_encoding = encoding;

	QUEUE_ITEM
}

# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
+ (void)of_addAsyncConnectForSocket: (id)sock
			       mode: (of_run_loop_mode_t)mode
			   delegate: (id <OFRunLoopConnectDelegate>)delegate
{
	NEW_WRITE(OFRunLoopConnectQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];

	QUEUE_ITEM
}
# endif

+ (void)of_addAsyncAcceptForSocket: (id)sock
			      mode: (of_run_loop_mode_t)mode
			     block: (id)block
			  delegate: (id)delegate
{
	NEW_READ(OFRunLoopAcceptQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif

	QUEUE_ITEM
}

+ (void)of_addAsyncReceiveForDatagramSocket: (OFDatagramSocket *)sock
    buffer: (void *)buffer
    length: (size_t)length
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
     block: (of_datagram_socket_async_receive_block_t)block
# endif
  delegate: (id <OFDatagramSocketDelegate>)delegate
{
	NEW_READ(OFRunLoopDatagramReceiveQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_buffer = buffer;
	queueItem->_length = length;

	QUEUE_ITEM
}

+ (void)of_addAsyncSendForDatagramSocket: (OFDatagramSocket *)sock
      data: (OFData *)data
  receiver: (const of_socket_address_t *)receiver
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
     block: (of_datagram_socket_async_send_data_block_t)block
# endif
  delegate: (id <OFDatagramSocketDelegate>)delegate
{
	NEW_WRITE(OFRunLoopDatagramSendQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_data = [data copy];
	queueItem->_receiver = *receiver;

	QUEUE_ITEM
}

+ (void)of_addAsyncReceiveForSequencedPacketSocket: (OFSequencedPacketSocket *)
							sock
    buffer: (void *)buffer
    length: (size_t)length
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
     block: (of_sequenced_packet_socket_async_receive_block_t)block
# endif
  delegate: (id <OFSequencedPacketSocketDelegate>)delegate
{
	NEW_READ(OFRunLoopPacketReceiveQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_buffer = buffer;
	queueItem->_length = length;

	QUEUE_ITEM
}

+ (void)of_addAsyncSendForSequencedPacketSocket: (OFSequencedPacketSocket *)sock
      data: (OFData *)data
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
     block: (of_sequenced_packet_socket_async_send_data_block_t)block
# endif
  delegate: (id <OFSequencedPacketSocketDelegate>)delegate
{
	NEW_WRITE(OFRunLoopPacketSendQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_data = [data copy];

	QUEUE_ITEM
}
# undef NEW_READ
# undef NEW_WRITE
# undef QUEUE_ITEM

+ (void)of_cancelAsyncRequestsForObject: (id)object
				   mode: (of_run_loop_mode_t)mode
{
	void *pool = objc_autoreleasePoolPush();
	OFRunLoop *runLoop = [self currentRunLoop];
	OFRunLoopState *state = [runLoop of_stateForMode: mode
						  create: false];
	OFList *queue;

	if (state == nil)
		return;

	if ((queue = [state->_writeQueues objectForKey: object]) != nil) {
		assert(queue.count > 0);







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





|
<





|
<










|
<





|
<
















|

|



















|

|

















|
|

|

















|

|

















|
|

|

















|











|
















|

|

















|
|

|



















|

|

















|

|

















|
<



|
<







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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
#endif
}

+ (void)of_setMainRunLoop: (OFRunLoop *)runLoop
{
	mainRunLoop = [runLoop retain];
}

static OFRunLoopState *
stateForMode(OFRunLoop *self, OFRunLoopMode mode, bool create)
{
	OFRunLoopState *state;

#ifdef OF_HAVE_THREADS
	[self->_statesMutex lock];
	@try {
#endif
		state = [self->_states objectForKey: mode];

		if (create && state == nil) {
			state = [[OFRunLoopState alloc] init];
			@try {
				[self->_states setObject: state forKey: mode];
			} @finally {
				[state release];
			}
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		[self->_statesMutex unlock];
	}
#endif

	return state;
}

#ifdef OF_HAVE_SOCKETS
# define NEW_READ(type, object, mode)					\
	void *pool = objc_autoreleasePoolPush();			\
	OFRunLoop *runLoop = [self currentRunLoop];			\
	OFRunLoopState *state = stateForMode(runLoop, mode, true);	\

	OFList *queue = [state->_readQueues objectForKey: object];	\
	type *queueItem;						\
									\
	if (queue == nil) {						\
		queue = [OFList list];					\
		[state->_readQueues setObject: queue forKey: object];	\

	}								\
									\
	if (queue.count == 0)						\
		[state->_kernelEventObserver				\
		    addObjectForReading: object];			\
									\
	queueItem = [[[type alloc] init] autorelease];
# define NEW_WRITE(type, object, mode)					\
	void *pool = objc_autoreleasePoolPush();			\
	OFRunLoop *runLoop = [self currentRunLoop];			\
	OFRunLoopState *state = stateForMode(runLoop, mode, true);	\

	OFList *queue = [state->_writeQueues objectForKey: object];	\
	type *queueItem;						\
									\
	if (queue == nil) {						\
		queue = [OFList list];					\
		[state->_writeQueues setObject: queue forKey: object];	\

	}								\
									\
	if (queue.count == 0)						\
		[state->_kernelEventObserver				\
		    addObjectForWriting: object];			\
									\
	queueItem = [[[type alloc] init] autorelease];
#define QUEUE_ITEM							\
	[queue appendObject: queueItem];				\
									\
	objc_autoreleasePoolPop(pool);

+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
			  length: (size_t)length
			    mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			   block: (OFStreamAsyncReadBlock)block
# endif
			delegate: (id <OFStreamDelegate>)delegate
{
	NEW_READ(OFRunLoopReadQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_buffer = buffer;
	queueItem->_length = length;

	QUEUE_ITEM
}

+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
		     exactLength: (size_t)exactLength
			    mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			   block: (OFStreamAsyncReadBlock)block
# endif
			delegate: (id <OFStreamDelegate>)delegate
{
	NEW_READ(OFRunLoopExactReadQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_buffer = buffer;
	queueItem->_exactLength = exactLength;

	QUEUE_ITEM
}

+ (void)of_addAsyncReadLineForStream: (OFStream <OFReadyForReadingObserving> *)
					  stream
			    encoding: (OFStringEncoding)encoding
				mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			       block: (OFStreamAsyncReadLineBlock)block
# endif
			    delegate: (id <OFStreamDelegate>)delegate
{
	NEW_READ(OFRunLoopReadLineQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_encoding = encoding;

	QUEUE_ITEM
}

+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			     data: (OFData *)data
			     mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			    block: (OFStreamAsyncWriteDataBlock)block
# endif
			 delegate: (id <OFStreamDelegate>)delegate
{
	NEW_WRITE(OFRunLoopWriteDataQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_data = [data copy];

	QUEUE_ITEM
}

+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			   string: (OFString *)string
			 encoding: (OFStringEncoding)encoding
			     mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
			    block: (OFStreamAsyncWriteStringBlock)block
# endif
			 delegate: (id <OFStreamDelegate>)delegate
{
	NEW_WRITE(OFRunLoopWriteStringQueueItem, stream, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_string = [string copy];
	queueItem->_encoding = encoding;

	QUEUE_ITEM
}

# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
+ (void)of_addAsyncConnectForSocket: (id)sock
			       mode: (OFRunLoopMode)mode
			   delegate: (id <OFRunLoopConnectDelegate>)delegate
{
	NEW_WRITE(OFRunLoopConnectQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];

	QUEUE_ITEM
}
# endif

+ (void)of_addAsyncAcceptForSocket: (id)sock
			      mode: (OFRunLoopMode)mode
			     block: (id)block
			  delegate: (id)delegate
{
	NEW_READ(OFRunLoopAcceptQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif

	QUEUE_ITEM
}

+ (void)of_addAsyncReceiveForDatagramSocket: (OFDatagramSocket *)sock
    buffer: (void *)buffer
    length: (size_t)length
      mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
     block: (OFDatagramSocketAsyncReceiveBlock)block
# endif
  delegate: (id <OFDatagramSocketDelegate>)delegate
{
	NEW_READ(OFRunLoopDatagramReceiveQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_buffer = buffer;
	queueItem->_length = length;

	QUEUE_ITEM
}

+ (void)of_addAsyncSendForDatagramSocket: (OFDatagramSocket *)sock
      data: (OFData *)data
  receiver: (const OFSocketAddress *)receiver
      mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
     block: (OFDatagramSocketAsyncSendDataBlock)block
# endif
  delegate: (id <OFDatagramSocketDelegate>)delegate
{
	NEW_WRITE(OFRunLoopDatagramSendQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_data = [data copy];
	queueItem->_receiver = *receiver;

	QUEUE_ITEM
}

+ (void)of_addAsyncReceiveForSequencedPacketSocket: (OFSequencedPacketSocket *)
							sock
    buffer: (void *)buffer
    length: (size_t)length
      mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
     block: (OFSequencedPacketSocketAsyncReceiveBlock)block
# endif
  delegate: (id <OFSequencedPacketSocketDelegate>)delegate
{
	NEW_READ(OFRunLoopPacketReceiveQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_buffer = buffer;
	queueItem->_length = length;

	QUEUE_ITEM
}

+ (void)of_addAsyncSendForSequencedPacketSocket: (OFSequencedPacketSocket *)sock
      data: (OFData *)data
      mode: (OFRunLoopMode)mode
# ifdef OF_HAVE_BLOCKS
     block: (OFSequencedPacketSocketAsyncSendDataBlock)block
# endif
  delegate: (id <OFSequencedPacketSocketDelegate>)delegate
{
	NEW_WRITE(OFRunLoopPacketSendQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_data = [data copy];

	QUEUE_ITEM
}
# undef NEW_READ
# undef NEW_WRITE
# undef QUEUE_ITEM

+ (void)of_cancelAsyncRequestsForObject: (id)object mode: (OFRunLoopMode)mode

{
	void *pool = objc_autoreleasePoolPush();
	OFRunLoop *runLoop = [self currentRunLoop];
	OFRunLoopState *state = stateForMode(runLoop, mode, false);

	OFList *queue;

	if (state == nil)
		return;

	if ((queue = [state->_writeQueues objectForKey: object]) != nil) {
		assert(queue.count > 0);
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
	@try {
		OFRunLoopState *state;

		_states = [[OFMutableDictionary alloc] init];

		state = [[OFRunLoopState alloc] init];
		@try {
			[_states setObject: state
				    forKey: of_run_loop_mode_default];
		} @finally {
			[state release];
		}

#ifdef OF_HAVE_THREADS
		_statesMutex = [[OFMutex alloc] init];
#endif







|
<







1364
1365
1366
1367
1368
1369
1370
1371

1372
1373
1374
1375
1376
1377
1378
	@try {
		OFRunLoopState *state;

		_states = [[OFMutableDictionary alloc] init];

		state = [[OFRunLoopState alloc] init];
		@try {
			[_states setObject: state forKey: OFDefaultRunLoopMode];

		} @finally {
			[state release];
		}

#ifdef OF_HAVE_THREADS
		_statesMutex = [[OFMutex alloc] init];
#endif
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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
#ifdef OF_HAVE_THREADS
	[_statesMutex release];
#endif

	[super dealloc];
}

- (OFRunLoopState *)of_stateForMode: (of_run_loop_mode_t)mode
			     create: (bool)create
{
	OFRunLoopState *state;

#ifdef OF_HAVE_THREADS
	[_statesMutex lock];
	@try {
#endif
		state = [_states objectForKey: mode];

		if (create && state == nil) {
			state = [[OFRunLoopState alloc] init];
			@try {
				[_states setObject: state
					    forKey: mode];
			} @finally {
				[state release];
			}
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		[_statesMutex unlock];
	}
#endif

	return state;
}

- (void)addTimer: (OFTimer *)timer
{
	[self addTimer: timer
	       forMode: of_run_loop_mode_default];
}

- (void)addTimer: (OFTimer *)timer
	 forMode: (of_run_loop_mode_t)mode
{
	OFRunLoopState *state = [self of_stateForMode: mode
					       create: true];

#ifdef OF_HAVE_THREADS
	[state->_timersQueueMutex lock];
	@try {
#endif
		[state->_timersQueue insertObject: timer];
#ifdef OF_HAVE_THREADS
	} @finally {
		[state->_timersQueueMutex unlock];
	}
#endif

	[timer of_setInRunLoop: self
			  mode: mode];

#if defined(OF_HAVE_SOCKETS)
	[state->_kernelEventObserver cancel];
#elif defined(OF_HAVE_THREADS)
	[state->_condition signal];
#endif
}

- (void)of_removeTimer: (OFTimer *)timer
	       forMode: (of_run_loop_mode_t)mode
{
	OFRunLoopState *state = [self of_stateForMode: mode
					       create: false];


	if (state == nil)
		return;


#ifdef OF_HAVE_THREADS
	[state->_timersQueueMutex lock];
	@try {
#endif
		of_list_object_t *iter;

		for (iter = state->_timersQueue.firstListObject; iter != NULL;
		    iter = iter->next) {
			if ([iter->object isEqual: timer]) {
				[state->_timersQueue removeListObject: iter];
				break;
			}
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		[state->_timersQueueMutex unlock];
	}
#endif
}

#ifdef OF_AMIGAOS
- (void)addExecSignal: (ULONG)signal
	       target: (id)target
	     selector: (SEL)selector
{
	[self addExecSignal: signal
		    forMode: of_run_loop_mode_default
		     target: target
		   selector: selector];
}

- (void)addExecSignal: (ULONG)signal
	      forMode: (of_run_loop_mode_t)mode
	       target: (id)target
	     selector: (SEL)selector
{
	OFRunLoopState *state = [self of_stateForMode: mode
					       create: true];

# ifdef OF_HAVE_THREADS
	[state->_execSignalsMutex lock];
	@try {
# endif
		[state->_execSignals addItem: &signal];
		[state->_execSignalsTargets addObject: target];







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


|
<


|
<

|
<












|
<








|
<

|
<

>
|

>





<
<
|
|
|
|











|
<
<


|





|



|
<







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
1453
1454


1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467

1468
1469
1470
1471
1472
1473
1474
#ifdef OF_HAVE_THREADS
	[_statesMutex release];
#endif

	[super dealloc];
}






























- (void)addTimer: (OFTimer *)timer
{
	[self addTimer: timer forMode: OFDefaultRunLoopMode];

}

- (void)addTimer: (OFTimer *)timer forMode: (OFRunLoopMode)mode

{
	OFRunLoopState *state = stateForMode(self, mode, true);


#ifdef OF_HAVE_THREADS
	[state->_timersQueueMutex lock];
	@try {
#endif
		[state->_timersQueue insertObject: timer];
#ifdef OF_HAVE_THREADS
	} @finally {
		[state->_timersQueueMutex unlock];
	}
#endif

	[timer of_setInRunLoop: self mode: mode];


#if defined(OF_HAVE_SOCKETS)
	[state->_kernelEventObserver cancel];
#elif defined(OF_HAVE_THREADS)
	[state->_condition signal];
#endif
}

- (void)of_removeTimer: (OFTimer *)timer forMode: (OFRunLoopMode)mode

{
	OFRunLoopState *state = stateForMode(self, mode, false);


	/* {} required to avoid -Wmisleading-indentation false positive. */
	if (state == nil) {
		return;
	}

#ifdef OF_HAVE_THREADS
	[state->_timersQueueMutex lock];
	@try {
#endif


		for (OFListItem iter = state->_timersQueue.firstListItem;
		    iter != NULL; iter = OFListItemNext(iter)) {
			if ([OFListItemObject(iter) isEqual: timer]) {
				[state->_timersQueue removeListItem: iter];
				break;
			}
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		[state->_timersQueueMutex unlock];
	}
#endif
}

#ifdef OF_AMIGAOS
- (void)addExecSignal: (ULONG)signal target: (id)target selector: (SEL)selector


{
	[self addExecSignal: signal
		    forMode: OFDefaultRunLoopMode
		     target: target
		   selector: selector];
}

- (void)addExecSignal: (ULONG)signal
	      forMode: (OFRunLoopMode)mode
	       target: (id)target
	     selector: (SEL)selector
{
	OFRunLoopState *state = stateForMode(self, mode, true);


# ifdef OF_HAVE_THREADS
	[state->_execSignalsMutex lock];
	@try {
# endif
		[state->_execSignals addItem: &signal];
		[state->_execSignalsTargets addObject: target];
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
}

- (void)removeExecSignal: (ULONG)signal
		  target: (id)target
		selector: (SEL)selector
{
	[self removeExecSignal: signal
		       forMode: of_run_loop_mode_default
			target: target
		      selector: selector];
}

- (void)removeExecSignal: (ULONG)signal
		 forMode: (of_run_loop_mode_t)mode
		  target: (id)target
		selector: (SEL)selector
{
	OFRunLoopState *state = [self of_stateForMode: mode
					       create: false];

	if (state == nil)
		return;

# ifdef OF_HAVE_THREADS
	[state->_execSignalsMutex lock];
	@try {







|





|



|
<







1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510

1511
1512
1513
1514
1515
1516
1517
}

- (void)removeExecSignal: (ULONG)signal
		  target: (id)target
		selector: (SEL)selector
{
	[self removeExecSignal: signal
		       forMode: OFDefaultRunLoopMode
			target: target
		      selector: selector];
}

- (void)removeExecSignal: (ULONG)signal
		 forMode: (OFRunLoopMode)mode
		  target: (id)target
		selector: (SEL)selector
{
	OFRunLoopState *state = stateForMode(self, mode, false);


	if (state == nil)
		return;

# ifdef OF_HAVE_THREADS
	[state->_execSignalsMutex lock];
	@try {
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

- (void)runUntilDate: (OFDate *)deadline
{
	_stop = false;

	while (!_stop &&
	    (deadline == nil || deadline.timeIntervalSinceNow >= 0))
		[self runMode: of_run_loop_mode_default
		   beforeDate: deadline];
}

- (void)runMode: (of_run_loop_mode_t)mode
     beforeDate: (OFDate *)deadline
{
	void *pool = objc_autoreleasePoolPush();
	of_run_loop_mode_t previousMode = _currentMode;
	OFRunLoopState *state = [self of_stateForMode: mode
					       create: false];

	if (state == nil)
		return;

	_currentMode = mode;
	@try {
		OFDate *nextTimer;
#if defined(OF_AMIGAOS) && !defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
		ULONG signalMask;
#endif

		for (;;) {
			OFTimer *timer;

#ifdef OF_HAVE_THREADS
			[state->_timersQueueMutex lock];
			@try {
#endif
				of_list_object_t *listObject =
				    state->_timersQueue.firstListObject;

				if (listObject != NULL && [listObject->object

				    fireDate].timeIntervalSinceNow <= 0) {
					timer = [[listObject->object
					    retain] autorelease];

					[state->_timersQueue
					    removeListObject: listObject];

					[timer of_setInRunLoop: nil
							  mode: nil];
				} else
					break;
#ifdef OF_HAVE_THREADS
			} @finally {
				[state->_timersQueueMutex unlock];
			}
#endif







<
|


<
|


|
|
<


















|
|

|
>
|
|



|

|
<







1563
1564
1565
1566
1567
1568
1569

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

- (void)runUntilDate: (OFDate *)deadline
{
	_stop = false;

	while (!_stop &&
	    (deadline == nil || deadline.timeIntervalSinceNow >= 0))

		[self runMode: OFDefaultRunLoopMode beforeDate: deadline];
}


- (void)runMode: (OFRunLoopMode)mode beforeDate: (OFDate *)deadline
{
	void *pool = objc_autoreleasePoolPush();
	OFRunLoopMode previousMode = _currentMode;
	OFRunLoopState *state = stateForMode(self, mode, false);


	if (state == nil)
		return;

	_currentMode = mode;
	@try {
		OFDate *nextTimer;
#if defined(OF_AMIGAOS) && !defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
		ULONG signalMask;
#endif

		for (;;) {
			OFTimer *timer;

#ifdef OF_HAVE_THREADS
			[state->_timersQueueMutex lock];
			@try {
#endif
				OFListItem listItem =
				    state->_timersQueue.firstListItem;

				if (listItem != NULL &&
				    [OFListItemObject(listItem) fireDate]
				    .timeIntervalSinceNow <= 0) {
					timer = [[OFListItemObject(listItem)
					    retain] autorelease];

					[state->_timersQueue
					    removeListItem: listItem];

					[timer of_setInRunLoop: nil mode: nil];

				} else
					break;
#ifdef OF_HAVE_THREADS
			} @finally {
				[state->_timersQueueMutex unlock];
			}
#endif
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
		} @finally {
			[state->_timersQueueMutex unlock];
		}
#endif

		/* Watch for I/O events until the next timer is due */
		if (nextTimer != nil || deadline != nil) {
			of_time_interval_t timeout;

			if (nextTimer != nil && deadline == nil)
				timeout = nextTimer.timeIntervalSinceNow;
			else if (nextTimer == nil && deadline != nil)
				timeout = deadline.timeIntervalSinceNow;
			else
				timeout = [nextTimer earlierDate: deadline]







|







1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
		} @finally {
			[state->_timersQueueMutex unlock];
		}
#endif

		/* Watch for I/O events until the next timer is due */
		if (nextTimer != nil || deadline != nil) {
			OFTimeInterval timeout;

			if (nextTimer != nil && deadline == nil)
				timeout = nextTimer.timeIntervalSinceNow;
			else if (nextTimer == nil && deadline != nil)
				timeout = deadline.timeIntervalSinceNow;
			else
				timeout = [nextTimer earlierDate: deadline]
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
	} @finally {
		_currentMode = previousMode;
	}
}

- (void)stop
{
	OFRunLoopState *state = [self of_stateForMode: of_run_loop_mode_default
					       create: false];

	_stop = true;

	if (state == nil)
		return;

#if defined(OF_HAVE_SOCKETS)
	[state->_kernelEventObserver cancel];
#elif defined(OF_HAVE_THREADS)
	[state->_condition signal];
#endif
}
@end







|
<













1704
1705
1706
1707
1708
1709
1710
1711

1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
	} @finally {
		_currentMode = previousMode;
	}
}

- (void)stop
{
	OFRunLoopState *state = stateForMode(self, OFDefaultRunLoopMode, false);


	_stop = true;

	if (state == nil)
		return;

#if defined(OF_HAVE_SOCKETS)
	[state->_kernelEventObserver cancel];
#elif defined(OF_HAVE_THREADS)
	[state->_condition signal];
#endif
}
@end

Added src/OFRunLoopConstants.inc version [04d2ca961c].

































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
 * 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.
 */

const OFRunLoopMode OFDefaultRunLoopMode = @"OFDefaultRunLoopMode";

Deleted src/OFSCTPSocket.h version [5b42b1dc48].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFSequencedPacketSocket.h"
#import "OFRunLoop.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFSCTPSocket;
@class OFString;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket connected.
 *
 * @param exception An exception which occurred while connecting the socket or
 *		    `nil` on success
 */
typedef void (^of_sctp_socket_async_connect_block_t)(id _Nullable exception);
#endif

/**
 * @protocol OFSCTPSocketDelegate OFSCTPSocket.h ObjFW/OFSCTPSocket.h
 *
 * A delegate for OFSCTPSocket.
 */
@protocol OFSCTPSocketDelegate <OFSequencedPacketSocketDelegate>
@optional
/**
 * @brief A method which is called when a socket connected.
 *
 * @param socket The socket which connected
 * @param host The host connected to
 * @param port The port on the host connected to
 * @param exception An exception that occurred while connecting, or nil on
 *		    success
 */
-     (void)socket: (OFSCTPSocket *)socket
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (nullable id)exception;
@end

/**
 * @class OFSCTPSocket OFSCTPSocket.h ObjFW/OFSCTPSocket.h
 *
 * @brief A class which provides methods to create and use SCTP sockets in
 *	  one-to-one mode.
 *
 * To connect to a server, create a socket and connect it.
 * To create a server, create a socket, bind it and listen on it.
 */
@interface OFSCTPSocket: OFSequencedPacketSocket
{
	OF_RESERVE_IVARS(OFSCTPSocket, 4)
}

/**
 * @brief Whether sending packets can be delayed. Setting this to NO sets
 *        SCTP_NODELAY on the socket.
 */
@property (nonatomic) bool canDelaySendingPackets;

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

/**
 * @brief Connect the OFSCTPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 */
- (void)connectToHost: (OFString *)host
		 port: (uint16_t)port;

/**
 * @brief Asynchronously connect the OFSCTPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port;

/**
 * @brief Asynchronously connect the OFSCTPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously connect the OFSCTPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
		     block: (of_sctp_socket_async_connect_block_t)block;

/**
 * @brief Asynchronously connect the OFSCTPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_sctp_socket_async_connect_block_t)block;
#endif

/**
 * @brief Bind the socket to the specified host and port.
 *
 * @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for
 *	       IPv6 to bind to all.
 * @param port The port to bind to. If the port is 0, an unused port will be
 *	       chosen, which can be obtained using the return value.
 * @return The port the socket was bound to
 */
- (uint16_t)bindToHost: (OFString *)host
		  port: (uint16_t)port;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































































































Deleted src/OFSCTPSocket.m version [f509519c08].

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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFSCTPSocket.h"
#import "OFDNSResolver.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFIPSocketAsyncConnector.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFString.h"
#import "OFThread.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFGetOptionFailedException.h"
#import "OFNotOpenException.h"
#import "OFSetOptionFailedException.h"

#import "socket.h"
#import "socket_helpers.h"

static const of_run_loop_mode_t connectRunLoopMode =
    @"of_sctp_socket_connect_mode";

@interface OFSCTPSocket () <OFIPSocketAsyncConnecting>
@end

@interface OFSCTPSocketConnectDelegate: OFObject <OFSCTPSocketDelegate>
{
@public
	bool _done;
	id _exception;
}
@end

@implementation OFSCTPSocketConnectDelegate
- (void)dealloc
{
	[_exception release];

	[super dealloc];
}

-     (void)socket: (OFSCTPSocket *)sock
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (id)exception
{
	_done = true;
	_exception = [exception retain];
}
@end

@implementation OFSCTPSocket
@dynamic delegate;

- (bool)of_createSocketForAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo
{
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if ((_socket = socket(address->sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_SCTP)) == INVALID_SOCKET) {
		*errNo = of_socket_errno();
		return false;
	}

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	return true;
}

- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (connect(_socket, &address->sockaddr.sockaddr,
	    address->length) != 0) {
		*errNo = of_socket_errno();
		return false;
	}

	return true;
}

- (void)of_closeSocket
{
	closesocket(_socket);
	_socket = INVALID_SOCKET;
}

- (void)connectToHost: (OFString *)host
		 port: (uint16_t)port
{
	void *pool = objc_autoreleasePoolPush();
	id <OFSCTPSocketDelegate> delegate = _delegate;
	OFSCTPSocketConnectDelegate *connectDelegate =
	    [[[OFSCTPSocketConnectDelegate alloc] init] autorelease];
	OFRunLoop *runLoop = [OFRunLoop currentRunLoop];

	self.delegate = connectDelegate;
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: connectRunLoopMode];

	while (!connectDelegate->_done)
		[runLoop runMode: connectRunLoopMode
		      beforeDate: nil];

	/* Cleanup */
	[runLoop runMode: connectRunLoopMode
	      beforeDate: [OFDate date]];

	if (connectDelegate->_exception != nil)
		@throw connectDelegate->_exception;

	self.delegate = delegate;

	objc_autoreleasePoolPop(pool);
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
{
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: of_run_loop_mode_default];
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	void *pool = objc_autoreleasePoolPush();

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	[[[[OFIPSocketAsyncConnector alloc]
		  initWithSocket: self
			    host: host
			    port: port
			delegate: _delegate
			   block: NULL
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
		     block: (of_sctp_socket_async_connect_block_t)block
{
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: of_run_loop_mode_default
			   block: block];
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_sctp_socket_async_connect_block_t)block
{
	void *pool = objc_autoreleasePoolPush();

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	[[[[OFIPSocketAsyncConnector alloc]
		  initWithSocket: self
			    host: host
			    port: port
			delegate: nil
			   block: block] autorelease]
	    startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}
#endif

- (uint16_t)bindToHost: (OFString *)host
		  port: (uint16_t)port
{
	const int one = 1;
	void *pool = objc_autoreleasePoolPush();
	OFData *socketAddresses;
	of_socket_address_t address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	socketAddresses = [[OFThread DNSResolver]
	    resolveAddressesForHost: host
		      addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY];

	address = *(of_socket_address_t *)[socketAddresses itemAtIndex: 0];
	of_socket_address_set_port(&address, port);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_SCTP)) == INVALID_SOCKET)
		@throw [OFBindFailedException
		    exceptionWithHost: host
				 port: port
			       socket: self
				errNo: of_socket_errno()];

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR,
	    (char *)&one, (socklen_t)sizeof(one));

	if (bind(_socket, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithHost: host
							   port: port
							 socket: self
							  errNo: errNo];
	}

	objc_autoreleasePoolPop(pool);

	if (port > 0)
		return port;

	memset(&address, 0, sizeof(address));

	address.length = (socklen_t)sizeof(address.sockaddr);
	if (of_getsockname(_socket, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithHost: host
							   port: port
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_family == AF_INET)
		return OF_BSWAP16_IF_LE(address.sockaddr.in.sin_port);
# ifdef OF_HAVE_IPV6
	else if (address.sockaddr.sockaddr.sa_family == AF_INET6)
		return OF_BSWAP16_IF_LE(address.sockaddr.in6.sin6_port);
# endif
	else {
		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithHost: host
							   port: port
							 socket: self
							  errNo: EAFNOSUPPORT];
	}
}

- (void)setCanDelaySendingPackets: (bool)canDelaySendingPackets
{
	int v = !canDelaySendingPackets;

	if (setsockopt(_socket, IPPROTO_SCTP, SCTP_NODELAY,
	    (char *)&v, (socklen_t)sizeof(v)) != 0)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];
}

- (bool)canDelaySendingPackets
{
	int v;
	socklen_t len = sizeof(v);

	if (getsockopt(_socket, IPPROTO_SCTP, SCTP_NODELAY,
	    (char *)&v, &len) != 0 || len != sizeof(v))
		@throw [OFGetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	return !v;
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































































































































































































































































































































































































































































































Modified src/OFSHA1Hash.h from [588a3c6fde] to [c7a73ceb16].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCryptoHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFSHA1Hash OFSHA1Hash.h ObjFW/OFSHA1Hash.h
 *
 * @brief A class which provides methods to create an SHA-1 hash.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFSHA1Hash: OFObject <OFCryptoHash>
{
	OFSecureData *_iVarsData;
	struct of_sha1_hash_ivars {
		uint32_t state[5];
		uint64_t bits;
		union of_sha1_hash_buffer {
			unsigned char bytes[64];
			uint32_t words[80];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

<
<
|













|











|


|


|











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


 * 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 "OFCryptographicHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFSHA1Hash OFSHA1Hash.h ObjFW/OFSHA1Hash.h
 *
 * @brief A class which provides methods to create an SHA-1 hash.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFSHA1Hash: OFObject <OFCryptographicHash>
{
	OFSecureData *_iVarsData;
	struct {
		uint32_t state[5];
		uint64_t bits;
		union {
			unsigned char bytes[64];
			uint32_t words[80];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFSHA1Hash.m from [cfb97974cb] to [6da836d4ff].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#include <string.h>

#import "OFSHA1Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"

#import "OFOutOfRangeException.h"

#define DIGEST_SIZE 20
#define BLOCK_SIZE 64


OF_DIRECT_MEMBERS
@interface OFSHA1Hash ()
- (void)of_resetState;
@end

#define F(a, b, c, d) ((d) ^ ((b) & ((c) ^ (d))))
#define G(a, b, c, d) ((b) ^ (c) ^ (d))
#define H(a, b, c, d) (((b) & (c)) | ((d) & ((b) | (c))))
#define I(a, b, c, d) ((b) ^ (c) ^ (d))

static OF_INLINE void
byteSwapVectorIfLE(uint32_t *vector, uint_fast8_t length)
{
#ifndef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OF_BSWAP32(vector[i]);
#endif
}

static void
processBlock(uint32_t *state, uint32_t *buffer)
{
	uint32_t new[5];
	uint_fast8_t i;

	new[0] = state[0];
	new[1] = state[1];
	new[2] = state[2];
	new[3] = state[3];
	new[4] = state[4];

	byteSwapVectorIfLE(buffer, 16);

	for (i = 16; i < 80; i++) {
		uint32_t tmp = buffer[i - 3] ^ buffer[i - 8] ^
		    buffer[i - 14] ^ buffer[i - 16];
		buffer[i] = OF_ROL(tmp, 1);
	}

#define LOOP_BODY(f, k)							\
	{								\
		uint32_t tmp = OF_ROL(new[0], 5) +			\
		    f(new[0], new[1], new[2], new[3]) +			\
		    new[4] + k + buffer[i];				\
		new[4] = new[3];					\
		new[3] = new[2];					\
		new[2] = OF_ROL(new[1], 30);				\
		new[1] = new[0];					\
		new[0] = tmp;						\
	}

	for (i = 0; i < 20; i++)
		LOOP_BODY(F, 0x5A827999)
	for (; i < 40; i++)







>


<
|
>
















|




















|




|




|







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

#include <string.h>

#import "OFSHA1Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"
#import "OFHashNotCalculatedException.h"
#import "OFOutOfRangeException.h"


static const size_t digestSize = 20;
static const size_t blockSize = 64;

OF_DIRECT_MEMBERS
@interface OFSHA1Hash ()
- (void)of_resetState;
@end

#define F(a, b, c, d) ((d) ^ ((b) & ((c) ^ (d))))
#define G(a, b, c, d) ((b) ^ (c) ^ (d))
#define H(a, b, c, d) (((b) & (c)) | ((d) & ((b) | (c))))
#define I(a, b, c, d) ((b) ^ (c) ^ (d))

static OF_INLINE void
byteSwapVectorIfLE(uint32_t *vector, uint_fast8_t length)
{
#ifndef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OFByteSwap32(vector[i]);
#endif
}

static void
processBlock(uint32_t *state, uint32_t *buffer)
{
	uint32_t new[5];
	uint_fast8_t i;

	new[0] = state[0];
	new[1] = state[1];
	new[2] = state[2];
	new[3] = state[3];
	new[4] = state[4];

	byteSwapVectorIfLE(buffer, 16);

	for (i = 16; i < 80; i++) {
		uint32_t tmp = buffer[i - 3] ^ buffer[i - 8] ^
		    buffer[i - 14] ^ buffer[i - 16];
		buffer[i] = OFRotateLeft(tmp, 1);
	}

#define LOOP_BODY(f, k)							\
	{								\
		uint32_t tmp = OFRotateLeft(new[0], 5) +		\
		    f(new[0], new[1], new[2], new[3]) +			\
		    new[4] + k + buffer[i];				\
		new[4] = new[3];					\
		new[3] = new[2];					\
		new[2] = OFRotateLeft(new[1], 30);			\
		new[1] = new[0];					\
		new[0] = tmp;						\
	}

	for (i = 0; i < 20; i++)
		LOOP_BODY(F, 0x5A827999)
	for (; i < 40; i++)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

@implementation OFSHA1Hash
@synthesize calculated = _calculated;
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

+ (size_t)digestSize
{
	return DIGEST_SIZE;
}

+ (size_t)blockSize
{
	return BLOCK_SIZE;
}

+ (instancetype)cryptoHashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{







|




|


|







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

@implementation OFSHA1Hash
@synthesize calculated = _calculated;
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

+ (size_t)digestSize
{
	return digestSize;
}

+ (size_t)blockSize
{
	return blockSize;
}

+ (instancetype)hashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
	[_iVarsData release];

	[super dealloc];
}

- (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (size_t)blockSize
{
	return BLOCK_SIZE;
}

- (id)copy
{
	OFSHA1Hash *copy = [[OFSHA1Hash alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];







|




|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
	[_iVarsData release];

	[super dealloc];
}

- (size_t)digestSize
{
	return digestSize;
}

- (size_t)blockSize
{
	return blockSize;
}

- (id)copy
{
	OFSHA1Hash *copy = [[OFSHA1Hash alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
	_iVars->state[0] = 0x67452301;
	_iVars->state[1] = 0xEFCDAB89;
	_iVars->state[2] = 0x98BADCFE;
	_iVars->state[3] = 0x10325476;
	_iVars->state[4] = 0xC3D2E1F0;
}

- (void)updateWithBuffer: (const void *)buffer_
		  length: (size_t)length
{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];








|
<







180
181
182
183
184
185
186
187

188
189
190
191
192
193
194
	_iVars->state[0] = 0x67452301;
	_iVars->state[1] = 0xEFCDAB89;
	_iVars->state[2] = 0x98BADCFE;
	_iVars->state[3] = 0x10325476;
	_iVars->state[4] = 0xC3D2E1F0;
}

- (void)updateWithBuffer: (const void *)buffer_ length: (size_t)length

{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (_calculated)


		return (const unsigned char *)_iVars->state;








	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	of_explicit_memset(_iVars->buffer.bytes + _iVars->bufferLength + 1, 0,
	    64 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 56) {
		processBlock(_iVars->state, _iVars->buffer.words);
		of_explicit_memset(_iVars->buffer.bytes, 0, 64);
	}

	_iVars->buffer.words[14] =
	    OF_BSWAP32_IF_LE((uint32_t)(_iVars->bits >> 32));
	_iVars->buffer.words[15] =
	    OF_BSWAP32_IF_LE((uint32_t)(_iVars->bits & 0xFFFFFFFF));

	processBlock(_iVars->state, _iVars->buffer.words);
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	byteSwapVectorIfLE(_iVars->state, 5);
	_calculated = true;

	return (const unsigned char *)_iVars->state;
}

- (void)reset
{
	[self of_resetState];
	_iVars->bits = 0;
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}
@end







|
>
>
|
|
>
>
>
>
>
>
>

|




|



|

|


|


<
<






|




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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (!_calculated)
		@throw [OFHashNotCalculatedException exceptionWithObject: self];

	return (const unsigned char *)_iVars->state;
}

- (void)calculate
{
	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	OFZeroMemory(_iVars->buffer.bytes + _iVars->bufferLength + 1,
	    64 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 56) {
		processBlock(_iVars->state, _iVars->buffer.words);
		OFZeroMemory(_iVars->buffer.bytes, 64);
	}

	_iVars->buffer.words[14] =
	    OFToBigEndian32((uint32_t)(_iVars->bits >> 32));
	_iVars->buffer.words[15] =
	    OFToBigEndian32((uint32_t)(_iVars->bits & 0xFFFFFFFF));

	processBlock(_iVars->state, _iVars->buffer.words);
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	byteSwapVectorIfLE(_iVars->state, 5);
	_calculated = true;


}

- (void)reset
{
	[self of_resetState];
	_iVars->bits = 0;
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}
@end

Modified src/OFSHA224Hash.h from [c733920226] to [ecc593c441].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFSHA224Hash.m from [cb55b029f3] to [5e5ae70827].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFSHA224Hash.h"

#define DIGEST_SIZE 28

@implementation OFSHA224Hash
+ (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (void)of_resetState
{
	_iVars->state[0] = 0xC1059ED8;
	_iVars->state[1] = 0x367CD507;
	_iVars->state[2] = 0x3070DD17;

<
<
|

















|




|




|







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


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

#import "OFSHA224Hash.h"

static const size_t digestSize = 28;

@implementation OFSHA224Hash
+ (size_t)digestSize
{
	return digestSize;
}

- (size_t)digestSize
{
	return digestSize;
}

- (void)of_resetState
{
	_iVars->state[0] = 0xC1059ED8;
	_iVars->state[1] = 0x367CD507;
	_iVars->state[2] = 0x3070DD17;

Modified src/OFSHA224Or256Hash.h from [0f791ad851] to [9235d0cf7a].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCryptoHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFSHA224Or256Hash OFSHA224Or256Hash.h ObjFW/OFSHA224Or256Hash.h
 *
 * @brief A base class for SHA-224 and SHA-256.
 */
@interface OFSHA224Or256Hash: OFObject <OFCryptoHash>
{
@private
	OFSecureData *_iVarsData;
@protected
	struct of_sha224_or_256_hash_ivars {
		uint32_t state[8];
		uint64_t bits;
		union of_sha224_or_256_hash_buffer {
			unsigned char bytes[64];
			uint32_t words[64];
		} buffer;
		size_t bufferLength;
	} *_iVars;
@private
	bool _allowsSwappableMemory;
	bool _calculated;
	OF_RESERVE_IVARS(OFSHA224Or256Hash, 4)
}
@end

OF_ASSUME_NONNULL_END

<
<
|













|










|




|


|













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


 * 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 "OFCryptographicHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFSHA224Or256Hash OFSHA224Or256Hash.h ObjFW/OFSHA224Or256Hash.h
 *
 * @brief A base class for SHA-224 and SHA-256.
 */
@interface OFSHA224Or256Hash: OFObject <OFCryptographicHash>
{
@private
	OFSecureData *_iVarsData;
@protected
	struct {
		uint32_t state[8];
		uint64_t bits;
		union {
			unsigned char bytes[64];
			uint32_t words[64];
		} buffer;
		size_t bufferLength;
	} *_iVars;
@private
	bool _allowsSwappableMemory;
	bool _calculated;
	OF_RESERVE_IVARS(OFSHA224Or256Hash, 4)
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFSHA224Or256Hash.m from [4e2317a38c] to [de7aa6c8f4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
20
21
22
23
24
25
26

27
28
29

30
31
32
33
34
35
36
#include <stdlib.h>
#include <string.h>

#import "OFSHA224Or256Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"

#import "OFOutOfRangeException.h"

#define BLOCK_SIZE 64


@interface OFSHA224Or256Hash ()
- (void)of_resetState;
@end

static const uint32_t table[] = {
	0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,







>


<
>







18
19
20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
#include <stdlib.h>
#include <string.h>

#import "OFSHA224Or256Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"
#import "OFHashNotCalculatedException.h"
#import "OFOutOfRangeException.h"


static const size_t blockSize = 64;

@interface OFSHA224Or256Hash ()
- (void)of_resetState;
@end

static const uint32_t table[] = {
	0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
};

static OF_INLINE void
byteSwapVectorIfLE(uint32_t *vector, uint_fast8_t length)
{
#ifndef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OF_BSWAP32(vector[i]);
#endif
}

static void
processBlock(uint32_t *state, uint32_t *buffer)
{
	uint32_t new[8];







|







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
};

static OF_INLINE void
byteSwapVectorIfLE(uint32_t *vector, uint_fast8_t length)
{
#ifndef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OFByteSwap32(vector[i]);
#endif
}

static void
processBlock(uint32_t *state, uint32_t *buffer)
{
	uint32_t new[8];
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

	byteSwapVectorIfLE(buffer, 16);

	for (i = 16; i < 64; i++) {
		uint32_t tmp;

		tmp = buffer[i - 2];
		buffer[i] = (OF_ROR(tmp, 17) ^ OF_ROR(tmp, 19) ^ (tmp >> 10)) +
		    buffer[i - 7];
		tmp = buffer[i - 15];
		buffer[i] += (OF_ROR(tmp, 7) ^ OF_ROR(tmp, 18) ^ (tmp >> 3)) +
		    buffer[i - 16];
	}

	for (i = 0; i < 64; i++) {
		uint32_t tmp1 = new[7] + (OF_ROR(new[4], 6) ^
		    OF_ROR(new[4], 11) ^ OF_ROR(new[4], 25)) +
		    ((new[4] & (new[5] ^ new[6])) ^ new[6]) +
		    table[i] + buffer[i];
		uint32_t tmp2 = (OF_ROR(new[0], 2) ^ OF_ROR(new[0], 13) ^
		    OF_ROR(new[0], 22)) +
		    ((new[0] & (new[1] | new[2])) | (new[1] & new[2]));

		new[7] = new[6];
		new[6] = new[5];
		new[5] = new[4];
		new[4] = new[3] + tmp1;
		new[3] = new[2];







|
|

|
|



|
|


|
|







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

	byteSwapVectorIfLE(buffer, 16);

	for (i = 16; i < 64; i++) {
		uint32_t tmp;

		tmp = buffer[i - 2];
		buffer[i] = (OFRotateRight(tmp, 17) ^ OFRotateRight(tmp, 19) ^
		    (tmp >> 10)) + buffer[i - 7];
		tmp = buffer[i - 15];
		buffer[i] += (OFRotateRight(tmp, 7) ^ OFRotateRight(tmp, 18) ^
		    (tmp >> 3)) + buffer[i - 16];
	}

	for (i = 0; i < 64; i++) {
		uint32_t tmp1 = new[7] + (OFRotateRight(new[4], 6) ^
		    OFRotateRight(new[4], 11) ^ OFRotateRight(new[4], 25)) +
		    ((new[4] & (new[5] ^ new[6])) ^ new[6]) +
		    table[i] + buffer[i];
		uint32_t tmp2 = (OFRotateRight(new[0], 2) ^
		    OFRotateRight(new[0], 13) ^ OFRotateRight(new[0], 22)) +
		    ((new[0] & (new[1] | new[2])) | (new[1] & new[2]));

		new[7] = new[6];
		new[6] = new[5];
		new[5] = new[4];
		new[4] = new[3] + tmp1;
		new[3] = new[2];
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
+ (size_t)digestSize
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (size_t)blockSize
{
	return BLOCK_SIZE;
}

+ (instancetype)cryptoHashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{







|


|







123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
+ (size_t)digestSize
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (size_t)blockSize
{
	return blockSize;
}

+ (instancetype)hashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
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
- (size_t)digestSize
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)blockSize
{
	return BLOCK_SIZE;
}

- (id)copy
{
	OFSHA224Or256Hash *copy = [[[self class] alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];
	copy->_iVars = copy->_iVarsData.mutableItems;
	copy->_allowsSwappableMemory = _allowsSwappableMemory;
	copy->_calculated = _calculated;

	return copy;
}

- (void)updateWithBuffer: (const void *)buffer_
		  length: (size_t)length
{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];








|














|
<







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
- (size_t)digestSize
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)blockSize
{
	return blockSize;
}

- (id)copy
{
	OFSHA224Or256Hash *copy = [[[self class] alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];
	copy->_iVars = copy->_iVarsData.mutableItems;
	copy->_allowsSwappableMemory = _allowsSwappableMemory;
	copy->_calculated = _calculated;

	return copy;
}

- (void)updateWithBuffer: (const void *)buffer_ length: (size_t)length

{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (_calculated)


		return (const unsigned char *)_iVars->state;








	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	of_explicit_memset(_iVars->buffer.bytes + _iVars->bufferLength + 1, 0,
	    64 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 56) {
		processBlock(_iVars->state, _iVars->buffer.words);
		of_explicit_memset(_iVars->buffer.bytes, 0, 64);
	}

	_iVars->buffer.words[14] =
	    OF_BSWAP32_IF_LE((uint32_t)(_iVars->bits >> 32));
	_iVars->buffer.words[15] =
	    OF_BSWAP32_IF_LE((uint32_t)(_iVars->bits & 0xFFFFFFFF));

	processBlock(_iVars->state, _iVars->buffer.words);
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	byteSwapVectorIfLE(_iVars->state, 8);
	_calculated = true;

	return (const unsigned char *)_iVars->state;
}

- (void)reset
{
	[self of_resetState];
	_iVars->bits = 0;
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}

- (void)of_resetState
{
	OF_UNRECOGNIZED_SELECTOR
}
@end







|
>
>
|
|
>
>
>
>
>
>
>

|




|



|

|


|


<
<






|









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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (!_calculated)
		@throw [OFHashNotCalculatedException exceptionWithObject: self];

	return (const unsigned char *)_iVars->state;
}

- (void)calculate
{
	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	OFZeroMemory(_iVars->buffer.bytes + _iVars->bufferLength + 1,
	    64 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 56) {
		processBlock(_iVars->state, _iVars->buffer.words);
		OFZeroMemory(_iVars->buffer.bytes, 64);
	}

	_iVars->buffer.words[14] =
	    OFToBigEndian32((uint32_t)(_iVars->bits >> 32));
	_iVars->buffer.words[15] =
	    OFToBigEndian32((uint32_t)(_iVars->bits & 0xFFFFFFFF));

	processBlock(_iVars->state, _iVars->buffer.words);
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	byteSwapVectorIfLE(_iVars->state, 8);
	_calculated = true;


}

- (void)reset
{
	[self of_resetState];
	_iVars->bits = 0;
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}

- (void)of_resetState
{
	OF_UNRECOGNIZED_SELECTOR
}
@end

Modified src/OFSHA256Hash.h from [1c291a6b9d] to [29c7ad23a9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFSHA256Hash.m from [d6d4b0d05c] to [d082eb2586].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFSHA256Hash.h"

#define DIGEST_SIZE 32

@implementation OFSHA256Hash
+ (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (void)of_resetState
{
	_iVars->state[0] = 0x6A09E667;
	_iVars->state[1] = 0xBB67AE85;
	_iVars->state[2] = 0x3C6EF372;

<
<
|

















|




|




|







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


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

#import "OFSHA256Hash.h"

static const size_t digestSize = 32;

@implementation OFSHA256Hash
+ (size_t)digestSize
{
	return digestSize;
}

- (size_t)digestSize
{
	return digestSize;
}

- (void)of_resetState
{
	_iVars->state[0] = 0x6A09E667;
	_iVars->state[1] = 0xBB67AE85;
	_iVars->state[2] = 0x3C6EF372;

Modified src/OFSHA384Hash.h from [617b436074] to [7c87550526].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFSHA384Hash.m from [f950be9c9b] to [30d7ff4a68].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFSHA384Hash.h"

#define DIGEST_SIZE 48

@implementation OFSHA384Hash
+ (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (void)of_resetState
{
	_iVars->state[0] = 0xCBBB9D5DC1059ED8;
	_iVars->state[1] = 0x629A292A367CD507;
	_iVars->state[2] = 0x9159015A3070DD17;

<
<
|

















|




|




|







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


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

#import "OFSHA384Hash.h"

static const size_t digestSize = 48;

@implementation OFSHA384Hash
+ (size_t)digestSize
{
	return digestSize;
}

- (size_t)digestSize
{
	return digestSize;
}

- (void)of_resetState
{
	_iVars->state[0] = 0xCBBB9D5DC1059ED8;
	_iVars->state[1] = 0x629A292A367CD507;
	_iVars->state[2] = 0x9159015A3070DD17;

Modified src/OFSHA384Or512Hash.h from [2b8816dd44] to [889dffbb99].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFCryptoHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFSHA384Or512Hash OFSHA384Or512Hash.h ObjFW/OFSHA384Or512Hash.h
 *
 * @brief A base class for SHA-384 and SHA-512.
 */
@interface OFSHA384Or512Hash: OFObject <OFCryptoHash>
{
@private
	OFSecureData *_iVarsData;
@protected
	struct of_sha384_or_512_hash_ivars {
		uint64_t state[8];
		uint64_t bits[2];
		union of_sha384_or_512_hash_buffer {
			unsigned char bytes[128];
			uint64_t words[80];
		} buffer;
		size_t bufferLength;
	} *_iVars;
@private
	bool _allowsSwappableMemory;
	bool _calculated;
	OF_RESERVE_IVARS(OFSHA384Or512Hash, 4)
}
@end

OF_ASSUME_NONNULL_END

<
<
|













|










|




|


|













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


 * 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 "OFCryptographicHash.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSecureData;

/**
 * @class OFSHA384Or512Hash OFSHA384Or512Hash.h ObjFW/OFSHA384Or512Hash.h
 *
 * @brief A base class for SHA-384 and SHA-512.
 */
@interface OFSHA384Or512Hash: OFObject <OFCryptographicHash>
{
@private
	OFSecureData *_iVarsData;
@protected
	struct {
		uint64_t state[8];
		uint64_t bits[2];
		union {
			unsigned char bytes[128];
			uint64_t words[80];
		} buffer;
		size_t bufferLength;
	} *_iVars;
@private
	bool _allowsSwappableMemory;
	bool _calculated;
	OF_RESERVE_IVARS(OFSHA384Or512Hash, 4)
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFSHA384Or512Hash.m from [2e85a85b06] to [f0d2e79fad].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
#include <stdlib.h>
#include <string.h>

#import "OFSHA384Or512Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"

#import "OFOutOfRangeException.h"

#define BLOCK_SIZE 128

@interface OFSHA384Or512Hash ()
- (void)of_resetState;
@end

static const uint64_t table[] = {
	0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F,







>


|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdlib.h>
#include <string.h>

#import "OFSHA384Or512Hash.h"
#import "OFSecureData.h"

#import "OFHashAlreadyCalculatedException.h"
#import "OFHashNotCalculatedException.h"
#import "OFOutOfRangeException.h"

static const size_t blockSize = 128;

@interface OFSHA384Or512Hash ()
- (void)of_resetState;
@end

static const uint64_t table[] = {
	0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F,
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
};

static OF_INLINE void
byteSwapVectorIfLE(uint64_t *vector, uint_fast8_t length)
{
#ifndef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OF_BSWAP64(vector[i]);
#endif
}

static void
processBlock(uint64_t *state, uint64_t *buffer)
{
	uint64_t new[8];







|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
};

static OF_INLINE void
byteSwapVectorIfLE(uint64_t *vector, uint_fast8_t length)
{
#ifndef OF_BIG_ENDIAN
	for (uint_fast8_t i = 0; i < length; i++)
		vector[i] = OFByteSwap64(vector[i]);
#endif
}

static void
processBlock(uint64_t *state, uint64_t *buffer)
{
	uint64_t new[8];
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

	byteSwapVectorIfLE(buffer, 16);

	for (i = 16; i < 80; i++) {
		uint64_t tmp;

		tmp = buffer[i - 2];
		buffer[i] = (OF_ROR(tmp, 19) ^ OF_ROR(tmp, 61) ^ (tmp >> 6)) +
		    buffer[i - 7];
		tmp = buffer[i - 15];
		buffer[i] += (OF_ROR(tmp, 1) ^ OF_ROR(tmp, 8) ^ (tmp >> 7)) +
		    buffer[i - 16];
	}

	for (i = 0; i < 80; i++) {
		uint64_t tmp1 = new[7] + (OF_ROR(new[4], 14) ^
		    OF_ROR(new[4], 18) ^ OF_ROR(new[4], 41)) +
		    ((new[4] & (new[5] ^ new[6])) ^ new[6]) +
		    table[i] + buffer[i];
		uint64_t tmp2 = (OF_ROR(new[0], 28) ^ OF_ROR(new[0], 34) ^
		    OF_ROR(new[0], 39)) +
		    ((new[0] & (new[1] | new[2])) | (new[1] & new[2]));

		new[7] = new[6];
		new[6] = new[5];
		new[5] = new[4];
		new[4] = new[3] + tmp1;
		new[3] = new[2];







|
|

|
|



|
|


|
|







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

	byteSwapVectorIfLE(buffer, 16);

	for (i = 16; i < 80; i++) {
		uint64_t tmp;

		tmp = buffer[i - 2];
		buffer[i] = (OFRotateRight(tmp, 19) ^ OFRotateRight(tmp, 61) ^
		    (tmp >> 6)) + buffer[i - 7];
		tmp = buffer[i - 15];
		buffer[i] += (OFRotateRight(tmp, 1) ^ OFRotateRight(tmp, 8) ^
		    (tmp >> 7)) + buffer[i - 16];
	}

	for (i = 0; i < 80; i++) {
		uint64_t tmp1 = new[7] + (OFRotateRight(new[4], 14) ^
		    OFRotateRight(new[4], 18) ^ OFRotateRight(new[4], 41)) +
		    ((new[4] & (new[5] ^ new[6])) ^ new[6]) +
		    table[i] + buffer[i];
		uint64_t tmp2 = (OFRotateRight(new[0], 28) ^
		    OFRotateRight(new[0], 34) ^ OFRotateRight(new[0], 39)) +
		    ((new[0] & (new[1] | new[2])) | (new[1] & new[2]));

		new[7] = new[6];
		new[6] = new[5];
		new[5] = new[4];
		new[4] = new[3] + tmp1;
		new[3] = new[2];
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
+ (size_t)digestSize
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (size_t)blockSize
{
	return BLOCK_SIZE;
}

+ (instancetype)cryptoHashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{







|


|







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
+ (size_t)digestSize
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (size_t)blockSize
{
	return blockSize;
}

+ (instancetype)hashWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithAllowsSwappableMemory:
	    allowsSwappableMemory] autorelease];
}

- (instancetype)initWithAllowsSwappableMemory: (bool)allowsSwappableMemory
{
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
- (size_t)digestSize
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)blockSize
{
	return BLOCK_SIZE;
}

- (id)copy
{
	OFSHA384Or512Hash *copy = [[[self class] alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];
	copy->_iVars = copy->_iVarsData.mutableItems;
	copy->_allowsSwappableMemory = _allowsSwappableMemory;
	copy->_calculated = _calculated;

	return copy;
}

- (void)updateWithBuffer: (const void *)buffer_
		  length: (size_t)length
{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];








|














|
<







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
- (size_t)digestSize
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)blockSize
{
	return blockSize;
}

- (id)copy
{
	OFSHA384Or512Hash *copy = [[[self class] alloc] of_init];

	copy->_iVarsData = [_iVarsData copy];
	copy->_iVars = copy->_iVarsData.mutableItems;
	copy->_allowsSwappableMemory = _allowsSwappableMemory;
	copy->_calculated = _calculated;

	return copy;
}

- (void)updateWithBuffer: (const void *)buffer_ length: (size_t)length

{
	const unsigned char *buffer = buffer_;

	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (_calculated)


		return (const unsigned char *)_iVars->state;








	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	of_explicit_memset(_iVars->buffer.bytes + _iVars->bufferLength + 1, 0,
	    128 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 112) {
		processBlock(_iVars->state, _iVars->buffer.words);
		of_explicit_memset(_iVars->buffer.bytes, 0, 128);
	}

	_iVars->buffer.words[14] = OF_BSWAP64_IF_LE(_iVars->bits[1]);
	_iVars->buffer.words[15] = OF_BSWAP64_IF_LE(_iVars->bits[0]);

	processBlock(_iVars->state, _iVars->buffer.words);
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	byteSwapVectorIfLE(_iVars->state, 8);
	_calculated = true;

	return (const unsigned char *)_iVars->state;
}

- (void)reset
{
	[self of_resetState];
	of_explicit_memset(_iVars->bits, 0, sizeof(_iVars->bits));
	of_explicit_memset(&_iVars->buffer, 0, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}

- (void)of_resetState
{
	OF_UNRECOGNIZED_SELECTOR
}
@end







|
>
>
|
|
>
>
>
>
>
>
>

|




|


|
|


|


<
<





|
|









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
			_iVars->bufferLength = 0;
		}
	}
}

- (const unsigned char *)digest
{
	if (!_calculated)
		@throw [OFHashNotCalculatedException exceptionWithObject: self];

	return (const unsigned char *)_iVars->state;
}

- (void)calculate
{
	if (_calculated)
		@throw [OFHashAlreadyCalculatedException
		    exceptionWithObject: self];

	_iVars->buffer.bytes[_iVars->bufferLength] = 0x80;
	OFZeroMemory(_iVars->buffer.bytes + _iVars->bufferLength + 1,
	    128 - _iVars->bufferLength - 1);

	if (_iVars->bufferLength >= 112) {
		processBlock(_iVars->state, _iVars->buffer.words);
		OFZeroMemory(_iVars->buffer.bytes, 128);
	}

	_iVars->buffer.words[14] = OFToBigEndian64(_iVars->bits[1]);
	_iVars->buffer.words[15] = OFToBigEndian64(_iVars->bits[0]);

	processBlock(_iVars->state, _iVars->buffer.words);
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	byteSwapVectorIfLE(_iVars->state, 8);
	_calculated = true;


}

- (void)reset
{
	[self of_resetState];
	OFZeroMemory(_iVars->bits, sizeof(_iVars->bits));
	OFZeroMemory(&_iVars->buffer, sizeof(_iVars->buffer));
	_iVars->bufferLength = 0;
	_calculated = false;
}

- (void)of_resetState
{
	OF_UNRECOGNIZED_SELECTOR
}
@end

Modified src/OFSHA512Hash.h from [8253dbc793] to [b25fc86287].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFSHA512Hash.m from [96269c9e3c] to [b08066342d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFSHA512Hash.h"

#define DIGEST_SIZE 64

@implementation OFSHA512Hash
+ (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (size_t)digestSize
{
	return DIGEST_SIZE;
}

- (void)of_resetState
{
	_iVars->state[0] = 0x6A09E667F3BCC908;
	_iVars->state[1] = 0xBB67AE8584CAA73B;
	_iVars->state[2] = 0x3C6EF372FE94F82B;

<
<
|

















|




|




|







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


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

#import "OFSHA512Hash.h"

static const size_t digestSize = 64;

@implementation OFSHA512Hash
+ (size_t)digestSize
{
	return digestSize;
}

- (size_t)digestSize
{
	return digestSize;
}

- (void)of_resetState
{
	_iVars->state[0] = 0x6A09E667F3BCC908;
	_iVars->state[1] = 0xBB67AE8584CAA73B;
	_iVars->state[2] = 0x3C6EF372FE94F82B;

Modified src/OFSPXSocket.h from [66a0b3680e] to [0544ae262f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket connected.
 *
 * @param exception An exception which occurred while connecting the socket or
 *		    `nil` on success
 */
typedef void (^of_spx_socket_async_connect_block_t)(id _Nullable exception);
#endif

/**
 * @protocol OFSPXSocketDelegate OFSPXSocket.h ObjFW/OFSPXSocket.h
 *
 * A delegate for OFSPXSocket.
 */







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket connected.
 *
 * @param exception An exception which occurred while connecting the socket or
 *		    `nil` on success
 */
typedef void (^OFSPXSocketAsyncConnectBlock)(id _Nullable exception);
#endif

/**
 * @protocol OFSPXSocketDelegate OFSPXSocket.h ObjFW/OFSPXSocket.h
 *
 * A delegate for OFSPXSocket.
 */
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
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously connect the OFSPXSocket to the specified destination.
 *
 * @param node The node to connect to
 * @param network The network on which the node to connect to is
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
		     block: (of_spx_socket_async_connect_block_t)block;

/**
 * @brief Asynchronously connect the OFSPXSocket to the specified destination.
 *
 * @param node The node to connect to
 * @param network The network on which the node to connect to is
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_spx_socket_async_connect_block_t)block;
#endif

/**
 * @brief Bind the socket to the specified network, node and port.
 *
 * @param port The port (sometimes called socket number) to bind to. 0 means to
 *	       pick one and return it.
 * @return The address on which this socket can be reached
 */
- (of_socket_address_t)bindToPort: (uint16_t)port;
@end

OF_ASSUME_NONNULL_END







|














|














|
|









|



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
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously connect the OFSPXSocket to the specified destination.
 *
 * @param node The node to connect to
 * @param network The network on which the node to connect to is
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
		     block: (OFSPXSocketAsyncConnectBlock)block;

/**
 * @brief Asynchronously connect the OFSPXSocket to the specified destination.
 *
 * @param node The node to connect to
 * @param network The network on which the node to connect to is
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
		     block: (OFSPXSocketAsyncConnectBlock)block;
#endif

/**
 * @brief Bind the socket to the specified network, node and port.
 *
 * @param port The port (sometimes called socket number) to bind to. 0 means to
 *	       pick one and return it.
 * @return The address on which this socket can be reached
 */
- (OFSocketAddress)bindToPort: (uint16_t)port;
@end

OF_ASSUME_NONNULL_END

Modified src/OFSPXSocket.m from [c3e334ddfc] to [772214332c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFSPXSocket.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"



#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFConnectionFailedException.h"
#import "OFNotOpenException.h"

#import "socket.h"
#import "socket_helpers.h"

#ifndef NSPROTO_SPX
# define NSPROTO_SPX 0
#endif

#define SPX_PACKET_TYPE 5

@interface OFSPXSocket ()
- (int)of_createSocketForAddress: (const of_socket_address_t *)address
			   errNo: (int *)errNo;
- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo;
- (void)of_closeSocket;
@end

OF_DIRECT_MEMBERS
@interface OFSPXSocketAsyncConnectDelegate: OFObject <OFRunLoopConnectDelegate>
{
	OFSPXSocket *_socket;
	unsigned char _node[IPX_NODE_LEN];
	uint32_t _network;
	uint16_t _port;
#ifdef OF_HAVE_BLOCKS
	of_spx_socket_async_connect_block_t _block;
#endif
}

- (instancetype)initWithSocket: (OFSPXSocket *)socket
			  node: (unsigned char [IPX_NODE_LEN])node
		       network: (uint32_t)network
			  port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
			 block: (of_spx_socket_async_connect_block_t)block
#endif
;
- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
@end

@implementation OFSPXSocketAsyncConnectDelegate
- (instancetype)initWithSocket: (OFSPXSocket *)sock
			  node: (unsigned char [IPX_NODE_LEN])node
		       network: (uint32_t)network
			  port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
			 block: (of_spx_socket_async_connect_block_t)block
#endif
{
	self = [super init];

	@try {
		_socket = [sock retain];
		memcpy(_node, node, IPX_NODE_LEN);

<
<
|




















>
>






<
<
<




|


|

|












|








|


|








|







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


 * 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 "OFSPXSocket.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFConnectionFailedException.h"
#import "OFNotOpenException.h"




#ifndef NSPROTO_SPX
# define NSPROTO_SPX 0
#endif

static const uint8_t SPXPacketType = 5;

@interface OFSPXSocket ()
- (int)of_createSocketForAddress: (const OFSocketAddress *)address
			   errNo: (int *)errNo;
- (bool)of_connectSocketToAddress: (const OFSocketAddress *)address
			    errNo: (int *)errNo;
- (void)of_closeSocket;
@end

OF_DIRECT_MEMBERS
@interface OFSPXSocketAsyncConnectDelegate: OFObject <OFRunLoopConnectDelegate>
{
	OFSPXSocket *_socket;
	unsigned char _node[IPX_NODE_LEN];
	uint32_t _network;
	uint16_t _port;
#ifdef OF_HAVE_BLOCKS
	OFSPXSocketAsyncConnectBlock _block;
#endif
}

- (instancetype)initWithSocket: (OFSPXSocket *)socket
			  node: (unsigned char [IPX_NODE_LEN])node
		       network: (uint32_t)network
			  port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
			 block: (OFSPXSocketAsyncConnectBlock)block
#endif
;
- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode;
@end

@implementation OFSPXSocketAsyncConnectDelegate
- (instancetype)initWithSocket: (OFSPXSocket *)sock
			  node: (unsigned char [IPX_NODE_LEN])node
		       network: (uint32_t)network
			  port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
			 block: (OFSPXSocketAsyncConnectBlock)block
#endif
{
	self = [super init];

	@try {
		_socket = [sock retain];
		memcpy(_node, node, IPX_NODE_LEN);
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
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif

	[super dealloc];
}

- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
{
	of_socket_address_t address =
	    of_socket_address_ipx(_node, _network, _port);
	id exception = nil;
	int errNo;

	if (![_socket of_createSocketForAddress: &address
					  errNo: &errNo]) {
		exception = [self of_connectionFailedExceptionForErrNo: errNo];
		goto inform_delegate;
	}

	_socket.canBlock = false;

	if (![_socket of_connectSocketToAddress: &address

					  errNo: &errNo]) {

		if (errNo == EINPROGRESS) {

			[OFRunLoop of_addAsyncConnectForSocket: _socket
							  mode: runLoopMode
						      delegate: self];
			return;
		}

		[_socket of_closeSocket];

		exception = [self of_connectionFailedExceptionForErrNo: errNo];
	}

inform_delegate:
	[self performSelector: @selector(of_socketDidConnect:exception:)
		   withObject: _socket
		   withObject: exception
		   afterDelay: 0];
}

- (void)of_socketDidConnect: (id)sock
		  exception: (id)exception
{
	id <OFSPXSocketDelegate> delegate = ((OFSPXSocket *)sock).delegate;

	if (exception == nil)
		((OFSPXSocket *)sock).canBlock = true;

#ifdef OF_HAVE_BLOCKS







|

|
|



|
<






|
>
|
>

>


















|
<







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
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif

	[super dealloc];
}

- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode
{
	OFSocketAddress address =
	    OFSocketAddressMakeIPX(_node, _network, _port);
	id exception = nil;
	int errNo;

	if (![_socket of_createSocketForAddress: &address errNo: &errNo]) {

		exception = [self of_connectionFailedExceptionForErrNo: errNo];
		goto inform_delegate;
	}

	_socket.canBlock = false;

	if (![_socket of_connectSocketToAddress: &address errNo: &errNo]) {
#ifdef OF_WINDOWS
		if (errNo == EINPROGRESS || errNo == EWOULDBLOCK) {
#else
		if (errNo == EINPROGRESS) {
#endif
			[OFRunLoop of_addAsyncConnectForSocket: _socket
							  mode: runLoopMode
						      delegate: self];
			return;
		}

		[_socket of_closeSocket];

		exception = [self of_connectionFailedExceptionForErrNo: errNo];
	}

inform_delegate:
	[self performSelector: @selector(of_socketDidConnect:exception:)
		   withObject: _socket
		   withObject: exception
		   afterDelay: 0];
}

- (void)of_socketDidConnect: (id)sock exception: (id)exception

{
	id <OFSPXSocketDelegate> delegate = ((OFSPXSocket *)sock).delegate;

	if (exception == nil)
		((OFSPXSocket *)sock).canBlock = true;

#ifdef OF_HAVE_BLOCKS
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
							errNo: errNo];
}
@end

@implementation OFSPXSocket
@dynamic delegate;

- (int)of_createSocketForAddress: (const of_socket_address_t *)address
			   errNo: (int *)errNo
{
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if ((_socket = socket(address->sockaddr.ipx.sipx_family,
	    SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) == INVALID_SOCKET) {

		*errNo = of_socket_errno();
		return false;
	}

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	return true;
}

- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (connect(_socket, &address->sockaddr.sockaddr,
	    address->length) != 0) {
		*errNo = of_socket_errno();
		return false;
	}

	return true;
}

- (void)of_closeSocket
{
	closesocket(_socket);
	_socket = INVALID_SOCKET;
}

- (void)connectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
	      network: (uint32_t)network
		 port: (uint16_t)port
{
	of_socket_address_t address =
	    of_socket_address_ipx(node, network, port);
	int errNo;

	if (![self of_createSocketForAddress: &address
				       errNo: &errNo])
		@throw [OFConnectionFailedException
		    exceptionWithNode: node
			      network: network
				 port: port
			       socket: self
				errNo: errNo];

	if (![self of_connectSocketToAddress: &address
				       errNo: &errNo]) {
		[self of_closeSocket];

		@throw [OFConnectionFailedException
		    exceptionWithNode: node
			      network: network
				 port: port
			       socket: self
				errNo: errNo];
	}
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
{
	[self asyncConnectToNode: node
			 network: network
			    port: port
		     runLoopMode: of_run_loop_mode_default];
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	void *pool = objc_autoreleasePoolPush();

	[[[[OFSPXSocketAsyncConnectDelegate alloc]
	    initWithSocket: self
		      node: node
		   network: network
		      port: port
#ifdef OF_HAVE_BLOCKS
		     block: NULL
#endif
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
		     block: (of_spx_socket_async_connect_block_t)block
{
	[self asyncConnectToNode: node
			 network: network
			    port: port
		     runLoopMode: of_run_loop_mode_default
			   block: block];
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_spx_socket_async_connect_block_t)block
{
	void *pool = objc_autoreleasePoolPush();

	[[[[OFSPXSocketAsyncConnectDelegate alloc]
	    initWithSocket: self
		      node: node
		   network: network
		      port: port
		     block: block
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}
#endif

- (of_socket_address_t)bindToPort: (uint16_t)port
{
	const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
	of_socket_address_t address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = of_socket_address_ipx(zeroNode, 0, port);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) == INVALID_SOCKET)

		@throw [OFBindFailedException
		    exceptionWithPort: port
			   packetType: SPX_PACKET_TYPE
			       socket: self
				errNo: of_socket_errno()];

	_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, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPX_PACKET_TYPE
							 socket: self
							  errNo: errNo];
	}

	memset(&address, 0, sizeof(address));
	address.family = OF_SOCKET_ADDRESS_FAMILY_IPX;
	address.length = (socklen_t)sizeof(address.sockaddr);

	if (of_getsockname(_socket, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPX_PACKET_TYPE
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_family != AF_IPX) {
		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPX_PACKET_TYPE
							 socket: self
							  errNo: EAFNOSUPPORT];
	}

	return address;
}
@end







|






|



|
>
|











|


|




|









|






<
|


|
<







|
<


















|





|




















|




|






|
|















|


|




|


|


|
>


|

|









|


|


|





|


|

|


|


|






|


|







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
							errNo: errNo];
}
@end

@implementation OFSPXSocket
@dynamic delegate;

- (int)of_createSocketForAddress: (const OFSocketAddress *)address
			   errNo: (int *)errNo
{
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if ((_socket = socket(address->sockaddr.ipx.sipx_family,
	    SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) ==
	    OFInvalidSocketHandle) {
		*errNo = OFSocketErrNo();
		return false;
	}

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	return true;
}

- (bool)of_connectSocketToAddress: (const OFSocketAddress *)address
			    errNo: (int *)errNo
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (connect(_socket, &address->sockaddr.sockaddr,
	    address->length) != 0) {
		*errNo = OFSocketErrNo();
		return false;
	}

	return true;
}

- (void)of_closeSocket
{
	closesocket(_socket);
	_socket = OFInvalidSocketHandle;
}

- (void)connectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
	      network: (uint32_t)network
		 port: (uint16_t)port
{

	OFSocketAddress address = OFSocketAddressMakeIPX(node, network, port);
	int errNo;

	if (![self of_createSocketForAddress: &address errNo: &errNo])

		@throw [OFConnectionFailedException
		    exceptionWithNode: node
			      network: network
				 port: port
			       socket: self
				errNo: errNo];

	if (![self of_connectSocketToAddress: &address errNo: &errNo]) {

		[self of_closeSocket];

		@throw [OFConnectionFailedException
		    exceptionWithNode: node
			      network: network
				 port: port
			       socket: self
				errNo: errNo];
	}
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
{
	[self asyncConnectToNode: node
			 network: network
			    port: port
		     runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
{
	void *pool = objc_autoreleasePoolPush();

	[[[[OFSPXSocketAsyncConnectDelegate alloc]
	    initWithSocket: self
		      node: node
		   network: network
		      port: port
#ifdef OF_HAVE_BLOCKS
		     block: NULL
#endif
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
		     block: (OFSPXSocketAsyncConnectBlock)block
{
	[self asyncConnectToNode: node
			 network: network
			    port: port
		     runLoopMode: OFDefaultRunLoopMode
			   block: block];
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
		     block: (OFSPXSocketAsyncConnectBlock)block
{
	void *pool = objc_autoreleasePoolPush();

	[[[[OFSPXSocketAsyncConnectDelegate alloc]
	    initWithSocket: self
		      node: node
		   network: network
		      port: port
		     block: block
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}
#endif

- (OFSocketAddress)bindToPort: (uint16_t)port
{
	const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
	OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = OFSocketAddressMakeIPX(zeroNode, 0, port);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) ==
	    OFInvalidSocketHandle)
		@throw [OFBindFailedException
		    exceptionWithPort: port
			   packetType: SPXPacketType
			       socket: self
				errNo: OFSocketErrNo()];

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	if (bind(_socket, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPXPacketType
							 socket: self
							  errNo: errNo];
	}

	memset(&address, 0, sizeof(address));
	address.family = OFSocketAddressFamilyIPX;
	address.length = (socklen_t)sizeof(address.sockaddr);

	if (OFGetSockName(_socket, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPXPacketType
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_family != AF_IPX) {
		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPXPacketType
							 socket: self
							  errNo: EAFNOSUPPORT];
	}

	return address;
}
@end

Modified src/OFSPXStreamSocket.h from [809200c97d] to [ccec1c33a6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket connected.
 *
 * @param exception An exception which occurred while connecting the socket or
 *		    `nil` on success
 */
typedef void (^of_spx_stream_socket_async_connect_block_t)(
    id _Nullable exception);
#endif

/**
 * @protocol OFSPXStreamSocketDelegate OFSPXStreamSocket.h \
 *	     ObjFW/OFSPXStreamSocket.h
 *
 * A delegate for OFSPXStreamSocket.







<
|







26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket connected.
 *
 * @param exception An exception which occurred while connecting the socket or
 *		    `nil` on success
 */

typedef void (^OFSPXStreamSocketAsyncConnectBlock)(id _Nullable exception);
#endif

/**
 * @protocol OFSPXStreamSocketDelegate OFSPXStreamSocket.h \
 *	     ObjFW/OFSPXStreamSocket.h
 *
 * A delegate for OFSPXStreamSocket.
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
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously connect the OFSPXStreamSocket to the specified
 *	  destination.
 *
 * @param node The node to connect to
 * @param network The network on which the node to connect to is
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
		     block: (of_spx_stream_socket_async_connect_block_t)block;

/**
 * @brief Asynchronously connect the OFSPXStreamSocket to the specified
 *	  destination.
 *
 * @param node The node to connect to
 * @param network The network on which the node to connect to is
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_spx_stream_socket_async_connect_block_t)block;
#endif

/**
 * @brief Bind the socket to the specified network, node and port.
 *
 * @param port The port (sometimes called socket number) to bind to. 0 means to
 *	       pick one and return it.
 * @return The address on which this socket can be reached
 */
- (of_socket_address_t)bindToPort: (uint16_t)port;
@end

OF_ASSUME_NONNULL_END







|















|















|
|









|



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
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously connect the OFSPXStreamSocket to the specified
 *	  destination.
 *
 * @param node The node to connect to
 * @param network The network on which the node to connect to is
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
		     block: (OFSPXStreamSocketAsyncConnectBlock)block;

/**
 * @brief Asynchronously connect the OFSPXStreamSocket to the specified
 *	  destination.
 *
 * @param node The node to connect to
 * @param network The network on which the node to connect to is
 * @param port The port (sometimes also called socket number) on the node to
 *	       connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
		     block: (OFSPXStreamSocketAsyncConnectBlock)block;
#endif

/**
 * @brief Bind the socket to the specified network, node and port.
 *
 * @param port The port (sometimes called socket number) to bind to. 0 means to
 *	       pick one and return it.
 * @return The address on which this socket can be reached
 */
- (OFSocketAddress)bindToPort: (uint16_t)port;
@end

OF_ASSUME_NONNULL_END

Modified src/OFSPXStreamSocket.m from [fcd6269f1f] to [f7ce692f2d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFSPXStreamSocket.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"



#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFConnectionFailedException.h"
#import "OFNotOpenException.h"

#import "socket.h"
#import "socket_helpers.h"

#ifndef NSPROTO_SPX
# define NSPROTO_SPX 0
#endif

#define SPX_PACKET_TYPE 5

@interface OFSPXStreamSocket ()
- (int)of_createSocketForAddress: (const of_socket_address_t *)address
			   errNo: (int *)errNo;
- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo;
- (void)of_closeSocket;
@end

OF_DIRECT_MEMBERS
@interface OFSPXStreamSocketAsyncConnectDelegate: OFObject
    <OFRunLoopConnectDelegate>
{
	OFSPXStreamSocket *_socket;
	unsigned char _node[IPX_NODE_LEN];
	uint32_t _network;
	uint16_t _port;
#ifdef OF_HAVE_BLOCKS
	of_spx_stream_socket_async_connect_block_t _block;
#endif
}

- (instancetype)initWithSocket: (OFSPXStreamSocket *)socket
			  node: (unsigned char [IPX_NODE_LEN])node
		       network: (uint32_t)network
			  port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
			 block: (of_spx_stream_socket_async_connect_block_t)
				    block
#endif
;
- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
@end

@implementation OFSPXStreamSocketAsyncConnectDelegate
- (instancetype)initWithSocket: (OFSPXStreamSocket *)sock
			  node: (unsigned char [IPX_NODE_LEN])node
		       network: (uint32_t)network
			  port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS
			 block: (of_spx_stream_socket_async_connect_block_t)
				    block
#endif
{
	self = [super init];

	@try {
		_socket = [sock retain];
		memcpy(_node, node, IPX_NODE_LEN);

<
<
|




















>
>






<
<
<




|


|

|













|








<
|


|








<
|







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


 * 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 "OFSPXStreamSocket.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFConnectionFailedException.h"
#import "OFNotOpenException.h"




#ifndef NSPROTO_SPX
# define NSPROTO_SPX 0
#endif

static const uint8_t SPXPacketType = 5;

@interface OFSPXStreamSocket ()
- (int)of_createSocketForAddress: (const OFSocketAddress *)address
			   errNo: (int *)errNo;
- (bool)of_connectSocketToAddress: (const OFSocketAddress *)address
			    errNo: (int *)errNo;
- (void)of_closeSocket;
@end

OF_DIRECT_MEMBERS
@interface OFSPXStreamSocketAsyncConnectDelegate: OFObject
    <OFRunLoopConnectDelegate>
{
	OFSPXStreamSocket *_socket;
	unsigned char _node[IPX_NODE_LEN];
	uint32_t _network;
	uint16_t _port;
#ifdef OF_HAVE_BLOCKS
	OFSPXStreamSocketAsyncConnectBlock _block;
#endif
}

- (instancetype)initWithSocket: (OFSPXStreamSocket *)socket
			  node: (unsigned char [IPX_NODE_LEN])node
		       network: (uint32_t)network
			  port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS

			 block: (OFSPXStreamSocketAsyncConnectBlock)block
#endif
;
- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode;
@end

@implementation OFSPXStreamSocketAsyncConnectDelegate
- (instancetype)initWithSocket: (OFSPXStreamSocket *)sock
			  node: (unsigned char [IPX_NODE_LEN])node
		       network: (uint32_t)network
			  port: (uint16_t)port
#ifdef OF_HAVE_BLOCKS

			 block: (OFSPXStreamSocketAsyncConnectBlock)block
#endif
{
	self = [super init];

	@try {
		_socket = [sock retain];
		memcpy(_node, node, IPX_NODE_LEN);
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
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif

	[super dealloc];
}

- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
{
	of_socket_address_t address =
	    of_socket_address_ipx(_node, _network, _port);
	id exception = nil;
	int errNo;

	if (![_socket of_createSocketForAddress: &address
					  errNo: &errNo]) {
		exception = [self of_connectionFailedExceptionForErrNo: errNo];
		goto inform_delegate;
	}

	_socket.canBlock = false;

	if (![_socket of_connectSocketToAddress: &address

					  errNo: &errNo]) {

		if (errNo == EINPROGRESS) {

			[OFRunLoop of_addAsyncConnectForSocket: _socket
							  mode: runLoopMode
						      delegate: self];
			return;
		}

		[_socket of_closeSocket];

		exception = [self of_connectionFailedExceptionForErrNo: errNo];
	}

inform_delegate:
	[self performSelector: @selector(of_socketDidConnect:exception:)
		   withObject: _socket
		   withObject: exception
		   afterDelay: 0];
}

- (void)of_socketDidConnect: (id)sock
		  exception: (id)exception
{
	id <OFSPXStreamSocketDelegate> delegate =
	    ((OFSPXStreamSocket *)sock).delegate;

	if (exception == nil)
		((OFSPXStreamSocket *)sock).canBlock = true;








|

|
|



|
<






|
>
|
>

>


















|
<







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
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif

	[super dealloc];
}

- (void)startWithRunLoopMode: (OFRunLoopMode)runLoopMode
{
	OFSocketAddress address =
	    OFSocketAddressMakeIPX(_node, _network, _port);
	id exception = nil;
	int errNo;

	if (![_socket of_createSocketForAddress: &address errNo: &errNo]) {

		exception = [self of_connectionFailedExceptionForErrNo: errNo];
		goto inform_delegate;
	}

	_socket.canBlock = false;

	if (![_socket of_connectSocketToAddress: &address errNo: &errNo]) {
#ifdef OF_WINDOWS
		if (errNo == EINPROGRESS || errNo == EWOULDBLOCK) {
#else
		if (errNo == EINPROGRESS) {
#endif
			[OFRunLoop of_addAsyncConnectForSocket: _socket
							  mode: runLoopMode
						      delegate: self];
			return;
		}

		[_socket of_closeSocket];

		exception = [self of_connectionFailedExceptionForErrNo: errNo];
	}

inform_delegate:
	[self performSelector: @selector(of_socketDidConnect:exception:)
		   withObject: _socket
		   withObject: exception
		   afterDelay: 0];
}

- (void)of_socketDidConnect: (id)sock exception: (id)exception

{
	id <OFSPXStreamSocketDelegate> delegate =
	    ((OFSPXStreamSocket *)sock).delegate;

	if (exception == nil)
		((OFSPXStreamSocket *)sock).canBlock = true;

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
							errNo: errNo];
}
@end

@implementation OFSPXStreamSocket
@dynamic delegate;

- (int)of_createSocketForAddress: (const of_socket_address_t *)address
			   errNo: (int *)errNo
{
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if ((_socket = socket(address->sockaddr.ipx.sipx_family,
	    SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) == INVALID_SOCKET) {

		*errNo = of_socket_errno();
		return false;
	}

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	return true;
}

- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (connect(_socket, &address->sockaddr.sockaddr,
	    address->length) != 0) {
		*errNo = of_socket_errno();
		return false;
	}

	return true;
}

- (void)of_closeSocket
{
	closesocket(_socket);
	_socket = INVALID_SOCKET;
}

- (void)connectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
	      network: (uint32_t)network
		 port: (uint16_t)port
{
	of_socket_address_t address =
	    of_socket_address_ipx(node, network, port);
	int errNo;

	if (![self of_createSocketForAddress: &address
				       errNo: &errNo])
		@throw [OFConnectionFailedException
		    exceptionWithNode: node
			      network: network
				 port: port
			       socket: self
				errNo: errNo];

	if (![self of_connectSocketToAddress: &address
				       errNo: &errNo]) {
		[self of_closeSocket];

		@throw [OFConnectionFailedException
		    exceptionWithNode: node
			      network: network
				 port: port
			       socket: self
				errNo: errNo];
	}
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
{
	[self asyncConnectToNode: node
			 network: network
			    port: port
		     runLoopMode: of_run_loop_mode_default];
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	void *pool = objc_autoreleasePoolPush();

	[[[[OFSPXStreamSocketAsyncConnectDelegate alloc]
	    initWithSocket: self
		      node: node
		   network: network
		      port: port
#ifdef OF_HAVE_BLOCKS
		     block: NULL
#endif
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
		     block: (of_spx_stream_socket_async_connect_block_t)block
{
	[self asyncConnectToNode: node
			 network: network
			    port: port
		     runLoopMode: of_run_loop_mode_default
			   block: block];
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_spx_stream_socket_async_connect_block_t)block
{
	void *pool = objc_autoreleasePoolPush();

	[[[[OFSPXStreamSocketAsyncConnectDelegate alloc]
	    initWithSocket: self
		      node: node
		   network: network
		      port: port
		     block: block
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}
#endif

- (of_socket_address_t)bindToPort: (uint16_t)port
{
	const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
	of_socket_address_t address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = of_socket_address_ipx(zeroNode, 0, port);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, NSPROTO_SPX)) == INVALID_SOCKET)
		@throw [OFBindFailedException
		    exceptionWithPort: port
			   packetType: SPX_PACKET_TYPE
			       socket: self
				errNo: of_socket_errno()];

	_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, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPX_PACKET_TYPE
							 socket: self
							  errNo: errNo];
	}

	memset(&address, 0, sizeof(address));
	address.family = OF_SOCKET_ADDRESS_FAMILY_IPX;
	address.length = (socklen_t)sizeof(address.sockaddr);

	if (of_getsockname(_socket, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPX_PACKET_TYPE
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_family != AF_IPX) {
		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPX_PACKET_TYPE
							 socket: self
							  errNo: EAFNOSUPPORT];
	}

	return address;
}
@end







|






|



|
>
|











|


|




|









|






<
|


|
<







|
<


















|





|




















|




|






|
|















|


|




|


|


|


|

|









|


|


|





|


|

|


|


|






|


|







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
							errNo: errNo];
}
@end

@implementation OFSPXStreamSocket
@dynamic delegate;

- (int)of_createSocketForAddress: (const OFSocketAddress *)address
			   errNo: (int *)errNo
{
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if ((_socket = socket(address->sockaddr.ipx.sipx_family,
	    SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) ==
	    OFInvalidSocketHandle) {
		*errNo = OFSocketErrNo();
		return false;
	}

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	return true;
}

- (bool)of_connectSocketToAddress: (const OFSocketAddress *)address
			    errNo: (int *)errNo
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (connect(_socket, &address->sockaddr.sockaddr,
	    address->length) != 0) {
		*errNo = OFSocketErrNo();
		return false;
	}

	return true;
}

- (void)of_closeSocket
{
	closesocket(_socket);
	_socket = OFInvalidSocketHandle;
}

- (void)connectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
	      network: (uint32_t)network
		 port: (uint16_t)port
{

	OFSocketAddress address = OFSocketAddressMakeIPX(node, network, port);
	int errNo;

	if (![self of_createSocketForAddress: &address errNo: &errNo])

		@throw [OFConnectionFailedException
		    exceptionWithNode: node
			      network: network
				 port: port
			       socket: self
				errNo: errNo];

	if (![self of_connectSocketToAddress: &address errNo: &errNo]) {

		[self of_closeSocket];

		@throw [OFConnectionFailedException
		    exceptionWithNode: node
			      network: network
				 port: port
			       socket: self
				errNo: errNo];
	}
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
{
	[self asyncConnectToNode: node
			 network: network
			    port: port
		     runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
{
	void *pool = objc_autoreleasePoolPush();

	[[[[OFSPXStreamSocketAsyncConnectDelegate alloc]
	    initWithSocket: self
		      node: node
		   network: network
		      port: port
#ifdef OF_HAVE_BLOCKS
		     block: NULL
#endif
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
		     block: (OFSPXStreamSocketAsyncConnectBlock)block
{
	[self asyncConnectToNode: node
			 network: network
			    port: port
		     runLoopMode: OFDefaultRunLoopMode
			   block: block];
}

- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
		     block: (OFSPXStreamSocketAsyncConnectBlock)block
{
	void *pool = objc_autoreleasePoolPush();

	[[[[OFSPXStreamSocketAsyncConnectDelegate alloc]
	    initWithSocket: self
		      node: node
		   network: network
		      port: port
		     block: block
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}
#endif

- (OFSocketAddress)bindToPort: (uint16_t)port
{
	const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
	OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = OFSocketAddressMakeIPX(zeroNode, 0, port);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, NSPROTO_SPX)) == OFInvalidSocketHandle)
		@throw [OFBindFailedException
		    exceptionWithPort: port
			   packetType: SPXPacketType
			       socket: self
				errNo: OFSocketErrNo()];

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	if (bind(_socket, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPXPacketType
							 socket: self
							  errNo: errNo];
	}

	memset(&address, 0, sizeof(address));
	address.family = OFSocketAddressFamilyIPX;
	address.length = (socklen_t)sizeof(address.sockaddr);

	if (OFGetSockName(_socket, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPXPacketType
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_family != AF_IPX) {
		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPort: port
						     packetType: SPXPacketType
							 socket: self
							  errNo: EAFNOSUPPORT];
	}

	return address;
}
@end

Modified src/OFSandbox.h from [e483b091af] to [8f1c6b186c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFPair OF_GENERIC(FirstType, SecondType);

/**
 * @brief An @ref OFPair for a path to unveil, with the first string being the
 *	  path and the second the permissions.
 */
typedef OFPair OF_GENERIC(OFString *, OFString *) *of_sandbox_unveil_path_t;

/**
 * @class OFSandbox OFSandbox.h ObjFW/OFSandbox.h
 *
 * @brief A class which describes a sandbox for the application.
 */
@interface OFSandbox: OFObject <OFCopying>
{
	unsigned int _allowsStdIO: 1;
	unsigned int _allowsReadingFiles: 1;
	unsigned int _allowsWritingFiles: 1;
	unsigned int _allowsCreatingFiles: 1;
	unsigned int _allowsCreatingSpecialFiles: 1;

<
<
|

















<
<




<
<
<
<
|

<
<
<
<
<







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 "OFObject.h"

OF_ASSUME_NONNULL_BEGIN



@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFPair OF_GENERIC(FirstType, SecondType);





typedef OFPair OF_GENERIC(OFString *, OFString *) *OFSandboxUnveilPath;






@interface OFSandbox: OFObject <OFCopying>
{
	unsigned int _allowsStdIO: 1;
	unsigned int _allowsReadingFiles: 1;
	unsigned int _allowsWritingFiles: 1;
	unsigned int _allowsCreatingFiles: 1;
	unsigned int _allowsCreatingSpecialFiles: 1;
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
268
269
	unsigned int _allowsVMInfo: 1;
	unsigned int _allowsChangingProcessRights: 1;
	unsigned int _allowsPF: 1;
	unsigned int _allowsAudio: 1;
	unsigned int _allowsBPF: 1;
	unsigned int _allowsUnveil: 1;
	unsigned int _returnsErrors: 1;
	OFMutableArray OF_GENERIC(of_sandbox_unveil_path_t) *_unveiledPaths;
@public
	size_t _unveiledPathsIndex;
	OF_RESERVE_IVARS(OFSandbox, 4)
}

/**
 * @brief Allows IO operations on previously allocated file descriptors.
 */
@property (nonatomic) bool allowsStdIO;

/**
 * @brief Allows read access to the file system.
 */
@property (nonatomic) bool allowsReadingFiles;

/**
 * @brief Allows write access to the file system.
 */
@property (nonatomic) bool allowsWritingFiles;

/**
 * @brief Allows creating files in the file system.
 */
@property (nonatomic) bool allowsCreatingFiles;

/**
 * @brief Allows creating special files in the file system.
 */
@property (nonatomic) bool allowsCreatingSpecialFiles;

/**
 * @brief Allows creating, reading and writing temporary files in `/tmp`.
 */
@property (nonatomic) bool allowsTemporaryFiles;

/**
 * @brief Allows using IP sockets.
 */
@property (nonatomic) bool allowsIPSockets;

/**
 * @brief Allows multicast sockets.
 */
@property (nonatomic) bool allowsMulticastSockets;

/**
 * @brief Allows explicit changes to file attributes.
 */
@property (nonatomic) bool allowsChangingFileAttributes;

/**
 * @brief Allows changing ownership of files.
 */
@property (nonatomic) bool allowsFileOwnerChanges;

/**
 * @brief Allows file locks.
 */
@property (nonatomic) bool allowsFileLocks;

/**
 * @brief Allows UNIX sockets.
 */
@property (nonatomic) bool allowsUNIXSockets;

/**
 * @brief Allows syscalls necessary for DNS lookups.
 */
@property (nonatomic) bool allowsDNS;

/**
 * @brief Allows to look up users and groups.
 */
@property (nonatomic) bool allowsUserDatabaseReading;

/**
 * @brief Allows sending file descriptors via sendmsg().
 */
@property (nonatomic) bool allowsFileDescriptorSending;

/**
 * @brief Allows receiving file descriptors via recvmsg().
 */
@property (nonatomic) bool allowsFileDescriptorReceiving;

/**
 * @brief Allows MTIOCGET and MTIOCTOP operations on tape devices.
 */
@property (nonatomic) bool allowsTape;

/**
 * @brief Allows read-write operations and ioctls on the TTY.
 */
@property (nonatomic) bool allowsTTY;

/**
 * @brief Allows various process relationshop operations.
 */
@property (nonatomic) bool allowsProcessOperations;

/**
 * @brief Allows execve().
 */
@property (nonatomic) bool allowsExec;

/**
 * @brief Allows PROT_EXEC for `mmap()` and `mprotect()`.
 */
@property (nonatomic) bool allowsProtExec;

/**
 * @brief Allows `settime()`.
 */
@property (nonatomic) bool allowsSetTime;

/**
 * @brief Allows introspection of processes on the system.
 */
@property (nonatomic) bool allowsPS;

/**
 * @brief Allows introspection of the system's virtual memory.
 */
@property (nonatomic) bool allowsVMInfo;

/**
 * @brief Allows changing the rights of process, for example the UID.
 */
@property (nonatomic) bool allowsChangingProcessRights;

/**
 * @brief Allows certain ioctls on the PF device.
 */
@property (nonatomic) bool allowsPF;

/**
 * @brief Allows certain ioctls on audio devices.
 */
@property (nonatomic) bool allowsAudio;

/**
 * @brief Allows BIOCGSTATS to collect statistics from a BPF device.
 */
@property (nonatomic) bool allowsBPF;

/**
 * @brief Allows unveiling more paths.
 */
@property (nonatomic) bool allowsUnveil;

/**
 * @brief Returns errors instead of killing the process.
 */
@property (nonatomic) bool returnsErrors;

#ifdef OF_HAVE_PLEDGE
/**
 * The string for OpenBSD's pledge() call.
 *
 * @warning Only available on systems with the pledge() call!
 */
@property (readonly, nonatomic) OFString *pledgeString;
#endif

/**
 * @brief A list of unveiled paths.
 */
@property (readonly, nonatomic)
    OFArray OF_GENERIC(of_sandbox_unveil_path_t) *unveiledPaths;

/**
 * @brief Create a new, autorelease OFSandbox.
 */
+ (instancetype)sandbox;

/**
 * @brief "Unveils" the specified path, meaning that it becomes visible from
 *	  the sandbox with the specified permissions.
 *
 * @param path The path to unveil
 * @param permissions The permissions for the path. The following permissions
 *		      can be combined:
 *		      Permission | Description
 *		      -----------|--------------------
 *		      r          | Make the path available for reading, like
 *		                 | @ref allowsReadingFiles
 *		      w          | Make the path available for writing, like
 *		                 | @ref allowsWritingFiles
 *		      x          | Make the path available for executing, like
 *		                 | @ref allowsExec
 *		      c          | Make the path available for creation and
 *		                 | deletion, like @ref allowsCreatingFiles
 */
- (void)unveilPath: (OFString *)path
       permissions: (OFString *)permissions;
@end

OF_ASSUME_NONNULL_END







|





<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<
<
<
<

<

<
<
<
<
<


<
<
<
<

|

<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<



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
	unsigned int _allowsVMInfo: 1;
	unsigned int _allowsChangingProcessRights: 1;
	unsigned int _allowsPF: 1;
	unsigned int _allowsAudio: 1;
	unsigned int _allowsBPF: 1;
	unsigned int _allowsUnveil: 1;
	unsigned int _returnsErrors: 1;
	OFMutableArray OF_GENERIC(OFSandboxUnveilPath) *_unveiledPaths;
@public
	size_t _unveiledPathsIndex;
	OF_RESERVE_IVARS(OFSandbox, 4)
}




@property (nonatomic) bool allowsStdIO;




@property (nonatomic) bool allowsReadingFiles;




@property (nonatomic) bool allowsWritingFiles;




@property (nonatomic) bool allowsCreatingFiles;




@property (nonatomic) bool allowsCreatingSpecialFiles;




@property (nonatomic) bool allowsTemporaryFiles;




@property (nonatomic) bool allowsIPSockets;




@property (nonatomic) bool allowsMulticastSockets;




@property (nonatomic) bool allowsChangingFileAttributes;




@property (nonatomic) bool allowsFileOwnerChanges;




@property (nonatomic) bool allowsFileLocks;




@property (nonatomic) bool allowsUNIXSockets;




@property (nonatomic) bool allowsDNS;




@property (nonatomic) bool allowsUserDatabaseReading;




@property (nonatomic) bool allowsFileDescriptorSending;




@property (nonatomic) bool allowsFileDescriptorReceiving;




@property (nonatomic) bool allowsTape;




@property (nonatomic) bool allowsTTY;




@property (nonatomic) bool allowsProcessOperations;




@property (nonatomic) bool allowsExec;




@property (nonatomic) bool allowsProtExec;




@property (nonatomic) bool allowsSetTime;




@property (nonatomic) bool allowsPS;




@property (nonatomic) bool allowsVMInfo;




@property (nonatomic) bool allowsChangingProcessRights;




@property (nonatomic) bool allowsPF;




@property (nonatomic) bool allowsAudio;




@property (nonatomic) bool allowsBPF;




@property (nonatomic) bool allowsUnveil;




@property (nonatomic) bool returnsErrors;

#ifdef OF_HAVE_PLEDGE





@property (readonly, nonatomic) OFString *pledgeString;
#endif




@property (readonly, nonatomic)
    OFArray OF_GENERIC(OFSandboxUnveilPath) *unveiledPaths;




+ (instancetype)sandbox;



















- (void)unveilPath: (OFString *)path permissions: (OFString *)permissions;

@end

OF_ASSUME_NONNULL_END

Modified src/OFSandbox.m from [103c5612bc] to [a04c417381].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD(hash, _allowsStdIO);
	OF_HASH_ADD(hash, _allowsReadingFiles);
	OF_HASH_ADD(hash, _allowsWritingFiles);
	OF_HASH_ADD(hash, _allowsCreatingFiles);
	OF_HASH_ADD(hash, _allowsCreatingSpecialFiles);
	OF_HASH_ADD(hash, _allowsTemporaryFiles);
	OF_HASH_ADD(hash, _allowsIPSockets);
	OF_HASH_ADD(hash, _allowsMulticastSockets);
	OF_HASH_ADD(hash, _allowsChangingFileAttributes);
	OF_HASH_ADD(hash, _allowsFileOwnerChanges);
	OF_HASH_ADD(hash, _allowsFileLocks);
	OF_HASH_ADD(hash, _allowsUNIXSockets);
	OF_HASH_ADD(hash, _allowsDNS);
	OF_HASH_ADD(hash, _allowsUserDatabaseReading);
	OF_HASH_ADD(hash, _allowsFileDescriptorSending);
	OF_HASH_ADD(hash, _allowsFileDescriptorReceiving);
	OF_HASH_ADD(hash, _allowsTape);
	OF_HASH_ADD(hash, _allowsTTY);
	OF_HASH_ADD(hash, _allowsProcessOperations);
	OF_HASH_ADD(hash, _allowsExec);
	OF_HASH_ADD(hash, _allowsProtExec);
	OF_HASH_ADD(hash, _allowsSetTime);
	OF_HASH_ADD(hash, _allowsPS);
	OF_HASH_ADD(hash, _allowsVMInfo);
	OF_HASH_ADD(hash, _allowsChangingProcessRights);
	OF_HASH_ADD(hash, _allowsPF);
	OF_HASH_ADD(hash, _allowsAudio);
	OF_HASH_ADD(hash, _allowsBPF);
	OF_HASH_ADD(hash, _allowsUnveil);
	OF_HASH_ADD(hash, _returnsErrors);

	OF_HASH_FINALIZE(hash);

	return hash;
}

#ifdef OF_HAVE_PLEDGE
- (OFString *)pledgeString
{







|

|

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

|







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
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAdd(&hash, _allowsStdIO);
	OFHashAdd(&hash, _allowsReadingFiles);
	OFHashAdd(&hash, _allowsWritingFiles);
	OFHashAdd(&hash, _allowsCreatingFiles);
	OFHashAdd(&hash, _allowsCreatingSpecialFiles);
	OFHashAdd(&hash, _allowsTemporaryFiles);
	OFHashAdd(&hash, _allowsIPSockets);
	OFHashAdd(&hash, _allowsMulticastSockets);
	OFHashAdd(&hash, _allowsChangingFileAttributes);
	OFHashAdd(&hash, _allowsFileOwnerChanges);
	OFHashAdd(&hash, _allowsFileLocks);
	OFHashAdd(&hash, _allowsUNIXSockets);
	OFHashAdd(&hash, _allowsDNS);
	OFHashAdd(&hash, _allowsUserDatabaseReading);
	OFHashAdd(&hash, _allowsFileDescriptorSending);
	OFHashAdd(&hash, _allowsFileDescriptorReceiving);
	OFHashAdd(&hash, _allowsTape);
	OFHashAdd(&hash, _allowsTTY);
	OFHashAdd(&hash, _allowsProcessOperations);
	OFHashAdd(&hash, _allowsExec);
	OFHashAdd(&hash, _allowsProtExec);
	OFHashAdd(&hash, _allowsSetTime);
	OFHashAdd(&hash, _allowsPS);
	OFHashAdd(&hash, _allowsVMInfo);
	OFHashAdd(&hash, _allowsChangingProcessRights);
	OFHashAdd(&hash, _allowsPF);
	OFHashAdd(&hash, _allowsAudio);
	OFHashAdd(&hash, _allowsBPF);
	OFHashAdd(&hash, _allowsUnveil);
	OFHashAdd(&hash, _returnsErrors);

	OFHashFinalize(&hash);

	return hash;
}

#ifdef OF_HAVE_PLEDGE
- (OFString *)pledgeString
{
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
#endif

- (void)unveilPath: (OFString *)path
       permissions: (OFString *)permissions
{
	void *pool = objc_autoreleasePoolPush();

	[_unveiledPaths addObject: [OFPair pairWithFirstObject: path
						  secondObject: permissions]];

	objc_autoreleasePoolPop(pool);
}

- (OFArray OF_GENERIC(of_sandbox_unveil_path_t) *)unveiledPaths
{
	return [[_unveiledPaths copy] autorelease];
}
@end







|
<









|




583
584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
599
600
601
602
603
604

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
#endif

- (void)unveilPath: (OFString *)path permissions: (OFString *)permissions

{
	void *pool = objc_autoreleasePoolPush();

	[_unveiledPaths addObject: [OFPair pairWithFirstObject: path
						  secondObject: permissions]];

	objc_autoreleasePoolPop(pool);
}

- (OFArray OF_GENERIC(OFSandboxUnveilPath) *)unveiledPaths
{
	return [[_unveiledPaths copy] autorelease];
}
@end

Renamed and modified src/scrypt.h [75597e6dd2] to src/OFScrypt.h [7e18036f48].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFHMAC;

/**
 * @brief The parameters for @ref of_scrypt.
 */
typedef struct of_scrypt_parameters_t {
	/** @brief The block size to use. */
	size_t blockSize;
	/** @brief The CPU/memory cost factor to use. */
	size_t costFactor;
	/** @brief The parallelization to use. */
	size_t parallelization;
	/** @brief The salt to derive a key with. */







|

|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFHMAC;

/**
 * @brief The parameters for @ref OFScrypt.
 */
typedef struct {
	/** @brief The block size to use. */
	size_t blockSize;
	/** @brief The CPU/memory cost factor to use. */
	size_t costFactor;
	/** @brief The parallelization to use. */
	size_t parallelization;
	/** @brief The salt to derive a key with. */
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
	 * @brief The desired length for the derived key.
	 *
	 * @ref key needs to have enough storage.
	 */
	size_t keyLength;
	/** @brief Whether data may be stored in swappable memory. */
	bool allowsSwappableMemory;
} of_scrypt_parameters_t;

#ifdef __cplusplus
extern "C" {
#endif
extern void of_salsa20_8_core(uint32_t buffer[_Nonnull 16]);
extern void of_scrypt_block_mix(uint32_t *output, const uint32_t *input,
    size_t blockSize);
extern void of_scrypt_romix(uint32_t *buffer, size_t blockSize,
    size_t costFactor, uint32_t *tmp);

/**
 * @brief Derives a key from a password and a salt using scrypt.
 *
 * @param param The parameters to use
 */
extern void of_scrypt(of_scrypt_parameters_t param);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|




|
|

|







|





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
	 * @brief The desired length for the derived key.
	 *
	 * @ref key needs to have enough storage.
	 */
	size_t keyLength;
	/** @brief Whether data may be stored in swappable memory. */
	bool allowsSwappableMemory;
} OFScryptParameters;

#ifdef __cplusplus
extern "C" {
#endif
extern void OFSalsa20_8Core(uint32_t buffer[_Nonnull 16]);
extern void OFScryptBlockMix(uint32_t *output, const uint32_t *input,
    size_t blockSize);
extern void OFScryptROMix(uint32_t *buffer, size_t blockSize,
    size_t costFactor, uint32_t *tmp);

/**
 * @brief Derives a key from a password and a salt using scrypt.
 *
 * @param param The parameters to use
 */
extern void OFScrypt(OFScryptParameters param);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Renamed and modified src/scrypt.m [163ddcc8f1] to src/OFScrypt.m [730aa203eb].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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 "OFSHA256Hash.h"
#import "OFSecureData.h"

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

#import "scrypt.h"
#import "pbkdf2.h"

void
of_salsa20_8_core(uint32_t buffer[16])
{
	uint32_t tmp[16];

	for (uint_fast8_t i = 0; i < 16; i++)
		tmp[i] = OF_BSWAP32_IF_BE(buffer[i]);

	for (uint_fast8_t i = 0; i < 8; i += 2) {
		tmp[ 4] ^= OF_ROL(tmp[ 0] + tmp[12],  7);
		tmp[ 8] ^= OF_ROL(tmp[ 4] + tmp[ 0],  9);
		tmp[12] ^= OF_ROL(tmp[ 8] + tmp[ 4], 13);
		tmp[ 0] ^= OF_ROL(tmp[12] + tmp[ 8], 18);
		tmp[ 9] ^= OF_ROL(tmp[ 5] + tmp[ 1],  7);
		tmp[13] ^= OF_ROL(tmp[ 9] + tmp[ 5],  9);
		tmp[ 1] ^= OF_ROL(tmp[13] + tmp[ 9], 13);
		tmp[ 5] ^= OF_ROL(tmp[ 1] + tmp[13], 18);
		tmp[14] ^= OF_ROL(tmp[10] + tmp[ 6],  7);
		tmp[ 2] ^= OF_ROL(tmp[14] + tmp[10],  9);
		tmp[ 6] ^= OF_ROL(tmp[ 2] + tmp[14], 13);
		tmp[10] ^= OF_ROL(tmp[ 6] + tmp[ 2], 18);
		tmp[ 3] ^= OF_ROL(tmp[15] + tmp[11],  7);
		tmp[ 7] ^= OF_ROL(tmp[ 3] + tmp[15],  9);
		tmp[11] ^= OF_ROL(tmp[ 7] + tmp[ 3], 13);
		tmp[15] ^= OF_ROL(tmp[11] + tmp[ 7], 18);
		tmp[ 1] ^= OF_ROL(tmp[ 0] + tmp[ 3],  7);
		tmp[ 2] ^= OF_ROL(tmp[ 1] + tmp[ 0],  9);
		tmp[ 3] ^= OF_ROL(tmp[ 2] + tmp[ 1], 13);
		tmp[ 0] ^= OF_ROL(tmp[ 3] + tmp[ 2], 18);
		tmp[ 6] ^= OF_ROL(tmp[ 5] + tmp[ 4],  7);
		tmp[ 7] ^= OF_ROL(tmp[ 6] + tmp[ 5],  9);
		tmp[ 4] ^= OF_ROL(tmp[ 7] + tmp[ 6], 13);
		tmp[ 5] ^= OF_ROL(tmp[ 4] + tmp[ 7], 18);
		tmp[11] ^= OF_ROL(tmp[10] + tmp[ 9],  7);
		tmp[ 8] ^= OF_ROL(tmp[11] + tmp[10],  9);
		tmp[ 9] ^= OF_ROL(tmp[ 8] + tmp[11], 13);
		tmp[10] ^= OF_ROL(tmp[ 9] + tmp[ 8], 18);
		tmp[12] ^= OF_ROL(tmp[15] + tmp[14],  7);
		tmp[13] ^= OF_ROL(tmp[12] + tmp[15],  9);
		tmp[14] ^= OF_ROL(tmp[13] + tmp[12], 13);
		tmp[15] ^= OF_ROL(tmp[14] + tmp[13], 18);
	}

	for (uint_fast8_t i = 0; i < 16; i++)
		buffer[i] = OF_BSWAP32_IF_BE(OF_BSWAP32_IF_BE(buffer[i]) +
		    tmp[i]);

	of_explicit_memset(tmp, 0, sizeof(tmp));
}

void
of_scrypt_block_mix(uint32_t *output, const uint32_t *input, size_t blockSize)
{
	uint32_t tmp[16];

	/* Check defined here and executed in of_scrypt() */
#define OVERFLOW_CHECK_1					\
	if (param.blockSize > SIZE_MAX / 2 ||			\
	    2 * param.blockSize - 1 > SIZE_MAX / 16)		\
		@throw [OFOutOfRangeException exception];

	memcpy(tmp, input + (2 * blockSize - 1) * 16, 64);

	for (size_t i = 0; i < 2 * blockSize; i++) {
		for (size_t j = 0; j < 16; j++)
			tmp[j] ^= input[i * 16 + j];

		of_salsa20_8_core(tmp);

		/*
		 * Even indices are stored in the first half and odd ones in
		 * the second.
		 */
		memcpy(output + ((i / 2) + (i & 1) * blockSize) * 16, tmp, 64);
	}

	of_explicit_memset(tmp, 0, sizeof(tmp));
}

void
of_scrypt_romix(uint32_t *buffer, size_t blockSize, size_t costFactor,
    uint32_t *tmp)
{
	/* Check defined here and executed in of_scrypt() */
#define OVERFLOW_CHECK_2						\
	if (param.blockSize > SIZE_MAX / 128 / param.costFactor)	\
		@throw [OFOutOfRangeException exception];

	uint32_t *tmp2 = tmp + 32 * blockSize;

	memcpy(tmp, buffer, 128 * blockSize);

	for (size_t i = 0; i < costFactor; i++) {
		memcpy(tmp2 + i * 32 * blockSize, tmp, 128 * blockSize);
		of_scrypt_block_mix(tmp, tmp2 + i * 32 * blockSize, blockSize);
	}

	for (size_t i = 0; i < costFactor; i++) {
		uint32_t j = OF_BSWAP32_IF_BE(tmp[(2 * blockSize - 1) * 16]) &
		    (costFactor - 1);

		for (size_t k = 0; k < 32 * blockSize; k++)
			tmp[k] ^= tmp2[j * 32 * blockSize + k];

		of_scrypt_block_mix(buffer, tmp, blockSize);

		if (i < costFactor - 1)
			memcpy(tmp, buffer, 128 * blockSize);
	}
}

void
of_scrypt(of_scrypt_parameters_t param)
{
	OFSecureData *tmp = nil, *buffer = nil;
	OFHMAC *HMAC = nil;

	if (param.blockSize == 0 || param.costFactor <= 1 ||
	    (param.costFactor & (param.costFactor - 1)) != 0 ||
	    param.parallelization == 0)







|
|


|




|


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



|


|



|



|











|








|



|


|










|



|
|




|







|







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
#import "OFSHA256Hash.h"
#import "OFSecureData.h"

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

#import "OFScrypt.h"
#import "OFPBKDF2.h"

void
OFSalsa20_8Core(uint32_t buffer[16])
{
	uint32_t tmp[16];

	for (uint_fast8_t i = 0; i < 16; i++)
		tmp[i] = OFToLittleEndian32(buffer[i]);

	for (uint_fast8_t i = 0; i < 8; i += 2) {
		tmp[ 4] ^= OFRotateLeft(tmp[ 0] + tmp[12],  7);
		tmp[ 8] ^= OFRotateLeft(tmp[ 4] + tmp[ 0],  9);
		tmp[12] ^= OFRotateLeft(tmp[ 8] + tmp[ 4], 13);
		tmp[ 0] ^= OFRotateLeft(tmp[12] + tmp[ 8], 18);
		tmp[ 9] ^= OFRotateLeft(tmp[ 5] + tmp[ 1],  7);
		tmp[13] ^= OFRotateLeft(tmp[ 9] + tmp[ 5],  9);
		tmp[ 1] ^= OFRotateLeft(tmp[13] + tmp[ 9], 13);
		tmp[ 5] ^= OFRotateLeft(tmp[ 1] + tmp[13], 18);
		tmp[14] ^= OFRotateLeft(tmp[10] + tmp[ 6],  7);
		tmp[ 2] ^= OFRotateLeft(tmp[14] + tmp[10],  9);
		tmp[ 6] ^= OFRotateLeft(tmp[ 2] + tmp[14], 13);
		tmp[10] ^= OFRotateLeft(tmp[ 6] + tmp[ 2], 18);
		tmp[ 3] ^= OFRotateLeft(tmp[15] + tmp[11],  7);
		tmp[ 7] ^= OFRotateLeft(tmp[ 3] + tmp[15],  9);
		tmp[11] ^= OFRotateLeft(tmp[ 7] + tmp[ 3], 13);
		tmp[15] ^= OFRotateLeft(tmp[11] + tmp[ 7], 18);
		tmp[ 1] ^= OFRotateLeft(tmp[ 0] + tmp[ 3],  7);
		tmp[ 2] ^= OFRotateLeft(tmp[ 1] + tmp[ 0],  9);
		tmp[ 3] ^= OFRotateLeft(tmp[ 2] + tmp[ 1], 13);
		tmp[ 0] ^= OFRotateLeft(tmp[ 3] + tmp[ 2], 18);
		tmp[ 6] ^= OFRotateLeft(tmp[ 5] + tmp[ 4],  7);
		tmp[ 7] ^= OFRotateLeft(tmp[ 6] + tmp[ 5],  9);
		tmp[ 4] ^= OFRotateLeft(tmp[ 7] + tmp[ 6], 13);
		tmp[ 5] ^= OFRotateLeft(tmp[ 4] + tmp[ 7], 18);
		tmp[11] ^= OFRotateLeft(tmp[10] + tmp[ 9],  7);
		tmp[ 8] ^= OFRotateLeft(tmp[11] + tmp[10],  9);
		tmp[ 9] ^= OFRotateLeft(tmp[ 8] + tmp[11], 13);
		tmp[10] ^= OFRotateLeft(tmp[ 9] + tmp[ 8], 18);
		tmp[12] ^= OFRotateLeft(tmp[15] + tmp[14],  7);
		tmp[13] ^= OFRotateLeft(tmp[12] + tmp[15],  9);
		tmp[14] ^= OFRotateLeft(tmp[13] + tmp[12], 13);
		tmp[15] ^= OFRotateLeft(tmp[14] + tmp[13], 18);
	}

	for (uint_fast8_t i = 0; i < 16; i++)
		buffer[i] = OFToLittleEndian32(OFFromLittleEndian32(buffer[i]) +
		    tmp[i]);

	OFZeroMemory(tmp, sizeof(tmp));
}

void
OFScryptBlockMix(uint32_t *output, const uint32_t *input, size_t blockSize)
{
	uint32_t tmp[16];

	/* Check defined here and executed in OFScrypt() */
#define OVERFLOW_CHECK_1					\
	if (param.blockSize > SIZE_MAX / 2 ||			\
	    2 * param.blockSize - 1 > SIZE_MAX / 16)		\
		@throw [OFOutOfRangeException exception];

	memcpy(tmp, input + (2 * blockSize - 1) * 16, 64);

	for (size_t i = 0; i < 2 * blockSize; i++) {
		for (size_t j = 0; j < 16; j++)
			tmp[j] ^= input[i * 16 + j];

		OFSalsa20_8Core(tmp);

		/*
		 * Even indices are stored in the first half and odd ones in
		 * the second.
		 */
		memcpy(output + ((i / 2) + (i & 1) * blockSize) * 16, tmp, 64);
	}

	OFZeroMemory(tmp, sizeof(tmp));
}

void
OFScryptROMix(uint32_t *buffer, size_t blockSize, size_t costFactor,
    uint32_t *tmp)
{
	/* Check defined here and executed in OFScrypt() */
#define OVERFLOW_CHECK_2						\
	if (param.blockSize > SIZE_MAX / 128 / param.costFactor)	\
		@throw [OFOutOfRangeException exception];

	uint32_t *tmp2 = tmp + 32 * blockSize;

	memcpy(tmp, buffer, 128 * blockSize);

	for (size_t i = 0; i < costFactor; i++) {
		memcpy(tmp2 + i * 32 * blockSize, tmp, 128 * blockSize);
		OFScryptBlockMix(tmp, tmp2 + i * 32 * blockSize, blockSize);
	}

	for (size_t i = 0; i < costFactor; i++) {
		uint32_t j = OFFromLittleEndian32(
		    tmp[(2 * blockSize - 1) * 16]) & (costFactor - 1);

		for (size_t k = 0; k < 32 * blockSize; k++)
			tmp[k] ^= tmp2[j * 32 * blockSize + k];

		OFScryptBlockMix(buffer, tmp, blockSize);

		if (i < costFactor - 1)
			memcpy(tmp, buffer, 128 * blockSize);
	}
}

void
OFScrypt(OFScryptParameters param)
{
	OFSecureData *tmp = nil, *buffer = nil;
	OFHMAC *HMAC = nil;

	if (param.blockSize == 0 || param.costFactor <= 1 ||
	    (param.costFactor & (param.costFactor - 1)) != 0 ||
	    param.parallelization == 0)
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
		uint32_t *tmpItems, *bufferItems;

		if (param.costFactor > SIZE_MAX - 1 ||
		    (param.costFactor + 1) > SIZE_MAX / 128)
			@throw [OFOutOfRangeException exception];

		tmp = [[OFSecureData alloc]
			 initWithItemSize: param.blockSize
				    count: (param.costFactor + 1) * 128

		    allowsSwappableMemory: param.allowsSwappableMemory];
		tmpItems = tmp.mutableItems;

		if (param.parallelization > SIZE_MAX / 128)
			@throw [OFOutOfRangeException exception];

		buffer = [[OFSecureData alloc]
			 initWithItemSize: param.blockSize
				    count: param.parallelization * 128

		    allowsSwappableMemory: param.allowsSwappableMemory];
		bufferItems = buffer.mutableItems;

		HMAC = [[OFHMAC alloc]
			initWithHashClass: [OFSHA256Hash class]
		    allowsSwappableMemory: param.allowsSwappableMemory];

		of_pbkdf2((of_pbkdf2_parameters_t){
			.HMAC                  = HMAC,
			.iterations            = 1,
			.salt                  = param.salt,
			.saltLength            = param.saltLength,
			.password              = param.password,
			.passwordLength        = param.passwordLength,
			.key                   = (unsigned char *)bufferItems,
			.keyLength             = param.parallelization * 128 *
			                         param.blockSize,
			.allowsSwappableMemory = param.allowsSwappableMemory
		});

		for (size_t i = 0; i < param.parallelization; i++)
			of_scrypt_romix(bufferItems + i * 32 * param.blockSize,
			    param.blockSize, param.costFactor, tmpItems);

		of_pbkdf2((of_pbkdf2_parameters_t){
			.HMAC                  = HMAC,
			.iterations            = 1,
			.salt                  = (unsigned char *)bufferItems,
			.saltLength            = param.parallelization * 128 *
			                         param.blockSize,
			.password              = param.password,
			.passwordLength        = param.passwordLength,







<
|
>







<
|
>







|













|


|







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
		uint32_t *tmpItems, *bufferItems;

		if (param.costFactor > SIZE_MAX - 1 ||
		    (param.costFactor + 1) > SIZE_MAX / 128)
			@throw [OFOutOfRangeException exception];

		tmp = [[OFSecureData alloc]

			    initWithCount: (param.costFactor + 1) * 128
				 itemSize: param.blockSize
		    allowsSwappableMemory: param.allowsSwappableMemory];
		tmpItems = tmp.mutableItems;

		if (param.parallelization > SIZE_MAX / 128)
			@throw [OFOutOfRangeException exception];

		buffer = [[OFSecureData alloc]

			    initWithCount: param.parallelization * 128
				 itemSize: param.blockSize
		    allowsSwappableMemory: param.allowsSwappableMemory];
		bufferItems = buffer.mutableItems;

		HMAC = [[OFHMAC alloc]
			initWithHashClass: [OFSHA256Hash class]
		    allowsSwappableMemory: param.allowsSwappableMemory];

		OFPBKDF2((OFPBKDF2Parameters){
			.HMAC                  = HMAC,
			.iterations            = 1,
			.salt                  = param.salt,
			.saltLength            = param.saltLength,
			.password              = param.password,
			.passwordLength        = param.passwordLength,
			.key                   = (unsigned char *)bufferItems,
			.keyLength             = param.parallelization * 128 *
			                         param.blockSize,
			.allowsSwappableMemory = param.allowsSwappableMemory
		});

		for (size_t i = 0; i < param.parallelization; i++)
			OFScryptROMix(bufferItems + i * 32 * param.blockSize,
			    param.blockSize, param.costFactor, tmpItems);

		OFPBKDF2((OFPBKDF2Parameters){
			.HMAC                  = HMAC,
			.iterations            = 1,
			.salt                  = (unsigned char *)bufferItems,
			.saltLength            = param.parallelization * 128 *
			                         param.blockSize,
			.password              = param.password,
			.passwordLength        = param.passwordLength,

Modified src/OFSecureData.h from [f20113d2e2] to [6ad4e10f23].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 *	    deallocated. Check the @ref allowsSwappableMemory property to see
 *	    whether a particular OFSecureData might be allocated in swappable
 *	    memory.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFSecureData: OFData
{
	struct page *_page;
	bool _allowsSwappableMemory;
}

/**
 * @brief Whether the data may be stored in swappable memory.
 */
@property (readonly, nonatomic) bool allowsSwappableMemory;







|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 *	    deallocated. Check the @ref allowsSwappableMemory property to see
 *	    whether a particular OFSecureData might be allocated in swappable
 *	    memory.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFSecureData: OFData
{
	void *_page;
	bool _allowsSwappableMemory;
}

/**
 * @brief Whether the data may be stored in swappable memory.
 */
@property (readonly, nonatomic) bool allowsSwappableMemory;
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
+ (instancetype)dataWithCount: (size_t)count
	allowsSwappableMemory: (bool)allowsSwappableMemory;

/**
 * @brief Creates a new, autoreleased OFSecureData with count items of the
 *	  specified item size, all set to zero.
 *
 * @param itemSize The size of a single item in the OFSecureData in bytes
 * @param count The number of zero items the OFSecureData should contain

 * @param allowsSwappableMemory Whether the data may be stored in swappable
 *			       memory
 * @return A new, autoreleased OFSecureData
 */
+ (instancetype)dataWithItemSize: (size_t)itemSize
			   count: (size_t)count
	   allowsSwappableMemory: (bool)allowsSwappableMemory;

+ (instancetype)dataWithItems: (const void *)items
			count: (size_t)count OF_UNAVAILABLE;
+ (instancetype)dataWithItems: (const void *)items

		     itemSize: (size_t)itemSize
			count: (size_t)count OF_UNAVAILABLE;
+ (instancetype)dataWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
+ (instancetype)dataWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path OF_UNAVAILABLE;
#endif
+ (instancetype)dataWithContentsOfURL: (OFURL *)URL OF_UNAVAILABLE;
+ (instancetype)dataWithStringRepresentation: (OFString *)string OF_UNAVAILABLE;
+ (instancetype)dataWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE;







<

>




|
|
|




>
|
<




<

>







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
+ (instancetype)dataWithCount: (size_t)count
	allowsSwappableMemory: (bool)allowsSwappableMemory;

/**
 * @brief Creates a new, autoreleased OFSecureData with count items of the
 *	  specified item size, all set to zero.
 *

 * @param count The number of zero items the OFSecureData should contain
 * @param itemSize The size of a single item in the OFSecureData in bytes
 * @param allowsSwappableMemory Whether the data may be stored in swappable
 *			       memory
 * @return A new, autoreleased OFSecureData
 */
+ (instancetype)dataWithCount: (size_t)count
		     itemSize: (size_t)itemSize
	allowsSwappableMemory: (bool)allowsSwappableMemory;

+ (instancetype)dataWithItems: (const void *)items
			count: (size_t)count OF_UNAVAILABLE;
+ (instancetype)dataWithItems: (const void *)items
			count: (size_t)count
		     itemSize: (size_t)itemSize OF_UNAVAILABLE;

+ (instancetype)dataWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
+ (instancetype)dataWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path OF_UNAVAILABLE;
#endif
+ (instancetype)dataWithContentsOfURL: (OFURL *)URL OF_UNAVAILABLE;
+ (instancetype)dataWithStringRepresentation: (OFString *)string OF_UNAVAILABLE;
+ (instancetype)dataWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE;
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
 *
 * @param itemSize The size of a single item in the OFSecureData in bytes
 * @param count The number of zero items the OFSecureData should contain
 * @param allowsSwappableMemory Whether the data may be stored in swappable
 *				memory
 * @return An initialized OFSecureData
 */
- (instancetype)initWithItemSize: (size_t)itemSize
			   count: (size_t)count
	   allowsSwappableMemory: (bool)allowsSwappableMemory
    OF_DESIGNATED_INITIALIZER;

- (instancetype)initWithItems: (const void *)items
			count: (size_t)count OF_UNAVAILABLE;
- (instancetype)initWithItems: (const void *)items

		     itemSize: (size_t)itemSize
			count: (size_t)count OF_UNAVAILABLE;
- (instancetype)initWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
- (instancetype)initWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path OF_UNAVAILABLE;
#endif
- (instancetype)initWithContentsOfURL: (OFURL *)URL OF_UNAVAILABLE;
- (instancetype)initWithStringRepresentation: (OFString *)string OF_UNAVAILABLE;
- (instancetype)initWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE;







|
|
|





>
|
<




<

>







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
 *
 * @param itemSize The size of a single item in the OFSecureData in bytes
 * @param count The number of zero items the OFSecureData should contain
 * @param allowsSwappableMemory Whether the data may be stored in swappable
 *				memory
 * @return An initialized OFSecureData
 */
- (instancetype)initWithCount: (size_t)count
		     itemSize: (size_t)itemSize
	allowsSwappableMemory: (bool)allowsSwappableMemory
    OF_DESIGNATED_INITIALIZER;

- (instancetype)initWithItems: (const void *)items
			count: (size_t)count OF_UNAVAILABLE;
- (instancetype)initWithItems: (const void *)items
			count: (size_t)count
		     itemSize: (size_t)itemSize OF_UNAVAILABLE;

- (instancetype)initWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
- (instancetype)initWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE;
#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path OF_UNAVAILABLE;
#endif
- (instancetype)initWithContentsOfURL: (OFURL *)URL OF_UNAVAILABLE;
- (instancetype)initWithStringRepresentation: (OFString *)string OF_UNAVAILABLE;
- (instancetype)initWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE;

Modified src/OFSecureData.m from [8eda9d8f37] to [ea6060539c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif

#import "OFSecureData.h"
#import "OFString.h"
#import "OFSystemInfo.h"




#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#ifdef OF_HAVE_THREADS
# import "tlskey.h"
#endif

#define CHUNK_SIZE 16

#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
struct page {
	struct page *next, *previous;
	void *map;
	unsigned char *page;
};

# if defined(OF_HAVE_COMPILER_TLS)
static thread_local struct page *firstPage = NULL;
static thread_local struct page *lastPage = NULL;
static thread_local struct page **preallocatedPages = NULL;
static thread_local size_t numPreallocatedPages = 0;
# elif defined(OF_HAVE_THREADS)
static of_tlskey_t firstPageKey, lastPageKey;
static of_tlskey_t preallocatedPagesKey, numPreallocatedPagesKey;
# else
static struct page *firstPage = NULL;
static struct page *lastPage = NULL;
static struct page **preallocatedPages = NULL;
static size_t numPreallocatedPages = 0;
# endif

static void *
mapPages(size_t numPages)
{
	size_t pageSize = [OFSystemInfo pageSize];







>
>
>







|
<
<
|
<

<
|
|





|
|
|


|
|

|
|
|







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
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif

#import "OFSecureData.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#ifdef OF_HAVE_THREADS
# import "OFTLSKey.h"
#endif

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)


static const size_t chunkSize = 16;



struct Page {
	struct Page *next, *previous;
	void *map;
	unsigned char *page;
};

# if defined(OF_HAVE_COMPILER_TLS)
static thread_local struct Page *firstPage = NULL;
static thread_local struct Page *lastPage = NULL;
static thread_local struct Page **preallocatedPages = NULL;
static thread_local size_t numPreallocatedPages = 0;
# elif defined(OF_HAVE_THREADS)
static OFTLSKey firstPageKey, lastPageKey;
static OFTLSKey preallocatedPagesKey, numPreallocatedPagesKey;
# else
static struct Page *firstPage = NULL;
static struct Page *lastPage = NULL;
static struct Page **preallocatedPages = NULL;
static size_t numPreallocatedPages = 0;
# endif

static void *
mapPages(size_t numPages)
{
	size_t pageSize = [OFSystemInfo pageSize];
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
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
	if (numPages > SIZE_MAX / pageSize)
		@throw [OFOutOfRangeException exception];

	munlock(pointer, numPages * pageSize);
	munmap(pointer, numPages * pageSize);
}

static struct page *
addPage(bool allowPreallocated)
{
	size_t pageSize = [OFSystemInfo pageSize];
	size_t mapSize = OF_ROUND_UP_POW2(8, pageSize / CHUNK_SIZE) / 8;

	struct page *page;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	struct page *lastPage;
# endif

	if (allowPreallocated) {
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		uintptr_t numPreallocatedPages =
		    (uintptr_t)of_tlskey_get(numPreallocatedPagesKey);
# endif

		if (numPreallocatedPages > 0) {
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
			struct page **preallocatedPages =
			    of_tlskey_get(preallocatedPagesKey);
# endif

			numPreallocatedPages--;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
			OF_ENSURE(of_tlskey_set(numPreallocatedPagesKey,
			    (void *)numPreallocatedPages));
# endif

			page = preallocatedPages[numPreallocatedPages];

			if (numPreallocatedPages == 0) {
				of_free(preallocatedPages);
				preallocatedPages = NULL;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
				OF_ENSURE(of_tlskey_set(preallocatedPagesKey,
				    preallocatedPages));
# endif
			}

			return page;
		}
	}

	page = of_malloc(1, sizeof(*page));
	@try {
		page->map = of_calloc(1, mapSize);
	} @catch (id e) {
		of_free(page);
		@throw e;
	}
	@try {
		page->page = mapPages(1);
	} @catch (id e) {
		of_free(page->map);
		of_free(page);
		@throw e;
	}
	of_explicit_memset(page->page, 0, pageSize);

# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	lastPage = of_tlskey_get(lastPageKey);
# endif

	page->previous = lastPage;
	page->next = NULL;

	if (lastPage != NULL)
		lastPage->next = page;

# if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
	lastPage = page;

	if (firstPage == NULL)
		firstPage = page;
# else
	OF_ENSURE(of_tlskey_set(lastPageKey, page));

	if (of_tlskey_get(firstPageKey) == NULL)
		OF_ENSURE(of_tlskey_set(firstPageKey, page));
# endif

	return page;
}

static void
removePageIfEmpty(struct page *page)
{
	unsigned char *map = page->map;
	size_t pageSize = [OFSystemInfo pageSize];
	size_t mapSize = OF_ROUND_UP_POW2(8, pageSize / CHUNK_SIZE) / 8;


	for (size_t i = 0; i < mapSize; i++)
		if (map[i] != 0)
			return;

	unmapPages(page->page, 1);
	of_free(page->map);

	if (page->previous != NULL)
		page->previous->next = page->next;
	if (page->next != NULL)
		page->next->previous = page->previous;

# if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
	if (firstPage == page)
		firstPage = page->next;
	if (lastPage == page)
		lastPage = page->previous;
# else
	if (of_tlskey_get(firstPageKey) == page)
		OF_ENSURE(of_tlskey_set(firstPageKey, page->next));
	if (of_tlskey_get(lastPageKey) == page)
		OF_ENSURE(of_tlskey_set(lastPageKey, page->previous));
# endif

	of_free(page);
}

static void *
allocateMemory(struct page *page, size_t bytes)
{
	size_t chunks, chunksLeft, pageSize, i, firstChunk;

	bytes = OF_ROUND_UP_POW2(CHUNK_SIZE, bytes);
	chunks = chunksLeft = bytes / CHUNK_SIZE;
	firstChunk = 0;
	pageSize = [OFSystemInfo pageSize];

	for (i = 0; i < pageSize / CHUNK_SIZE; i++) {
		if (of_bitset_isset(page->map, i)) {
			chunksLeft = chunks;
			firstChunk = i + 1;
			continue;
		}

		if (--chunksLeft == 0)
			break;
	}

	if (chunksLeft == 0) {
		for (size_t j = firstChunk; j < firstChunk + chunks; j++)
			of_bitset_set(page->map, j);

		return page->page + (CHUNK_SIZE * firstChunk);
	}

	return NULL;
}

static void
freeMemory(struct page *page, void *pointer, size_t bytes)
{
	size_t chunks, chunkIndex;

	bytes = OF_ROUND_UP_POW2(CHUNK_SIZE, bytes);
	chunks = bytes / CHUNK_SIZE;
	chunkIndex = ((uintptr_t)pointer - (uintptr_t)page->page) / CHUNK_SIZE;

	of_explicit_memset(pointer, 0, bytes);

	for (size_t i = 0; i < chunks; i++)
		of_bitset_clear(page->map, chunkIndex + i);
}
#endif

@implementation OFSecureData
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON) && \
    !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
+ (void)initialize
{
	if (self != [OFSecureData class])
		return;

	if (!of_tlskey_new(&firstPageKey) || !of_tlskey_new(&lastPageKey) ||
	    !of_tlskey_new(&preallocatedPagesKey) ||
	    !of_tlskey_new(&numPreallocatedPagesKey))
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}
#endif

+ (void)preallocateUnswappableMemoryWithSize: (size_t)size
{
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	size_t pageSize = [OFSystemInfo pageSize];
	size_t numPages = OF_ROUND_UP_POW2(pageSize, size) / pageSize;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	struct page **preallocatedPages = of_tlskey_get(preallocatedPagesKey);
	size_t numPreallocatedPages;
# endif
	size_t i;

	if (preallocatedPages != NULL)
		@throw [OFInvalidArgumentException exception];

	preallocatedPages = of_calloc(numPages, sizeof(struct page));
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	of_tlskey_set(preallocatedPagesKey, preallocatedPages);
# endif

	@try {
		for (i = 0; i < numPages; i++)
			preallocatedPages[i] = addPage(false);
	} @catch (id e) {
		for (size_t j = 0; j < i; j++)
			removePageIfEmpty(preallocatedPages[j]);

		of_free(preallocatedPages);
		preallocatedPages = NULL;

		@throw e;
	}

	numPreallocatedPages = numPages;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	of_tlskey_set(numPreallocatedPagesKey,
	    (void *)(uintptr_t)numPreallocatedPages);
# endif
#else
	@throw [OFNotImplementedException exceptionWithSelector: _cmd
							 object: self];
#endif
}

+ (instancetype)dataWithCount: (size_t)count
	allowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithCount: count
		      allowsSwappableMemory: allowsSwappableMemory]
	    autorelease];
}

+ (instancetype)dataWithItemSize: (size_t)itemSize
			   count: (size_t)count
	   allowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithItemSize: itemSize
					 count: count
			 allowsSwappableMemory: allowsSwappableMemory]
	    autorelease];
}

+ (instancetype)dataWithItems: (const void *)items
			count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)dataWithItems: (const void *)items
		     itemSize: (size_t)itemSize
			count: (size_t)count

{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)dataWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)dataWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone
{
	OF_UNRECOGNIZED_SELECTOR
}

#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path







|



|
>
|

|





|




|
|




|
|





|


|
|







|

|

|





|
|


|


|














|

|
|






|



|
>






|












|
|
|
|


|



|



|
|



|
|











|

|






|



|
|
|

|


|













|
|
|









|

|







|

|









|







|
|















|
|
|

|
|
|










<

>












<

>







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
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
	if (numPages > SIZE_MAX / pageSize)
		@throw [OFOutOfRangeException exception];

	munlock(pointer, numPages * pageSize);
	munmap(pointer, numPages * pageSize);
}

static struct Page *
addPage(bool allowPreallocated)
{
	size_t pageSize = [OFSystemInfo pageSize];
	size_t mapSize = OFRoundUpToPowerOf2(CHAR_BIT, pageSize / chunkSize) /
	    CHAR_BIT;
	struct Page *page;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	struct Page *lastPage;
# endif

	if (allowPreallocated) {
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		uintptr_t numPreallocatedPages =
		    (uintptr_t)OFTLSKeyGet(numPreallocatedPagesKey);
# endif

		if (numPreallocatedPages > 0) {
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
			struct Page **preallocatedPages =
			    OFTLSKeyGet(preallocatedPagesKey);
# endif

			numPreallocatedPages--;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
			OFEnsure(OFTLSKeySet(numPreallocatedPagesKey,
			    (void *)numPreallocatedPages) == 0);
# endif

			page = preallocatedPages[numPreallocatedPages];

			if (numPreallocatedPages == 0) {
				OFFreeMemory(preallocatedPages);
				preallocatedPages = NULL;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
				OFEnsure(OFTLSKeySet(preallocatedPagesKey,
				    preallocatedPages) == 0);
# endif
			}

			return page;
		}
	}

	page = OFAllocMemory(1, sizeof(*page));
	@try {
		page->map = OFAllocZeroedMemory(1, mapSize);
	} @catch (id e) {
		OFFreeMemory(page);
		@throw e;
	}
	@try {
		page->page = mapPages(1);
	} @catch (id e) {
		OFFreeMemory(page->map);
		OFFreeMemory(page);
		@throw e;
	}
	OFZeroMemory(page->page, pageSize);

# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	lastPage = OFTLSKeyGet(lastPageKey);
# endif

	page->previous = lastPage;
	page->next = NULL;

	if (lastPage != NULL)
		lastPage->next = page;

# if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
	lastPage = page;

	if (firstPage == NULL)
		firstPage = page;
# else
	OFEnsure(OFTLSKeySet(lastPageKey, page) == 0);

	if (OFTLSKeyGet(firstPageKey) == NULL)
		OFEnsure(OFTLSKeySet(firstPageKey, page) == 0);
# endif

	return page;
}

static void
removePageIfEmpty(struct Page *page)
{
	unsigned char *map = page->map;
	size_t pageSize = [OFSystemInfo pageSize];
	size_t mapSize = OFRoundUpToPowerOf2(CHAR_BIT, pageSize / chunkSize) /
	    CHAR_BIT;

	for (size_t i = 0; i < mapSize; i++)
		if (map[i] != 0)
			return;

	unmapPages(page->page, 1);
	OFFreeMemory(page->map);

	if (page->previous != NULL)
		page->previous->next = page->next;
	if (page->next != NULL)
		page->next->previous = page->previous;

# if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
	if (firstPage == page)
		firstPage = page->next;
	if (lastPage == page)
		lastPage = page->previous;
# else
	if (OFTLSKeyGet(firstPageKey) == page)
		OFEnsure(OFTLSKeySet(firstPageKey, page->next) == 0);
	if (OFTLSKeyGet(lastPageKey) == page)
		OFEnsure(OFTLSKeySet(lastPageKey, page->previous) == 0);
# endif

	OFFreeMemory(page);
}

static void *
allocateMemory(struct Page *page, size_t bytes)
{
	size_t chunks, chunksLeft, pageSize, i, firstChunk;

	bytes = OFRoundUpToPowerOf2(chunkSize, bytes);
	chunks = chunksLeft = bytes / chunkSize;
	firstChunk = 0;
	pageSize = [OFSystemInfo pageSize];

	for (i = 0; i < pageSize / chunkSize; i++) {
		if (OFBitsetIsSet(page->map, i)) {
			chunksLeft = chunks;
			firstChunk = i + 1;
			continue;
		}

		if (--chunksLeft == 0)
			break;
	}

	if (chunksLeft == 0) {
		for (size_t j = firstChunk; j < firstChunk + chunks; j++)
			OFBitsetSet(page->map, j);

		return page->page + (chunkSize * firstChunk);
	}

	return NULL;
}

static void
freeMemory(struct Page *page, void *pointer, size_t bytes)
{
	size_t chunks, chunkIndex;

	bytes = OFRoundUpToPowerOf2(chunkSize, bytes);
	chunks = bytes / chunkSize;
	chunkIndex = ((uintptr_t)pointer - (uintptr_t)page->page) / chunkSize;

	OFZeroMemory(pointer, bytes);

	for (size_t i = 0; i < chunks; i++)
		OFBitsetClear(page->map, chunkIndex + i);
}
#endif

@implementation OFSecureData
@synthesize allowsSwappableMemory = _allowsSwappableMemory;

#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON) && \
    !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
+ (void)initialize
{
	if (self != [OFSecureData class])
		return;

	if (OFTLSKeyNew(&firstPageKey) != 0 || OFTLSKeyNew(&lastPageKey) != 0 ||
	    OFTLSKeyNew(&preallocatedPagesKey) != 0 ||
	    OFTLSKeyNew(&numPreallocatedPagesKey) != 0)
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}
#endif

+ (void)preallocateUnswappableMemoryWithSize: (size_t)size
{
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	size_t pageSize = [OFSystemInfo pageSize];
	size_t numPages = OFRoundUpToPowerOf2(pageSize, size) / pageSize;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	struct Page **preallocatedPages = OFTLSKeyGet(preallocatedPagesKey);
	size_t numPreallocatedPages;
# endif
	size_t i;

	if (preallocatedPages != NULL)
		@throw [OFInvalidArgumentException exception];

	preallocatedPages = OFAllocZeroedMemory(numPages, sizeof(struct Page));
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OFEnsure(OFTLSKeySet(preallocatedPagesKey, preallocatedPages) == 0);
# endif

	@try {
		for (i = 0; i < numPages; i++)
			preallocatedPages[i] = addPage(false);
	} @catch (id e) {
		for (size_t j = 0; j < i; j++)
			removePageIfEmpty(preallocatedPages[j]);

		OFFreeMemory(preallocatedPages);
		preallocatedPages = NULL;

		@throw e;
	}

	numPreallocatedPages = numPages;
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OFEnsure(OFTLSKeySet(numPreallocatedPagesKey,
	    (void *)(uintptr_t)numPreallocatedPages) == 0);
# endif
#else
	@throw [OFNotImplementedException exceptionWithSelector: _cmd
							 object: self];
#endif
}

+ (instancetype)dataWithCount: (size_t)count
	allowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithCount: count
		      allowsSwappableMemory: allowsSwappableMemory]
	    autorelease];
}

+ (instancetype)dataWithCount: (size_t)count
		     itemSize: (size_t)itemSize
	allowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [[[self alloc] initWithCount: count
				   itemSize: itemSize
		      allowsSwappableMemory: allowsSwappableMemory]
	    autorelease];
}

+ (instancetype)dataWithItems: (const void *)items
			count: (size_t)count
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)dataWithItems: (const void *)items

			count: (size_t)count
		     itemSize: (size_t)itemSize
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)dataWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)dataWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone
{
	OF_UNRECOGNIZED_SELECTOR
}

#ifdef OF_HAVE_FILES
+ (instancetype)dataWithContentsOfFile: (OFString *)path
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
	OF_UNRECOGNIZED_SELECTOR
}


- (instancetype)initWithCount: (size_t)count
	allowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [self initWithItemSize: 1
				count: count
		allowsSwappableMemory: allowsSwappableMemory];
}

- (instancetype)initWithItemSize: (size_t)itemSize
			   count: (size_t)count
	   allowsSwappableMemory: (bool)allowsSwappableMemory
{
	self = [super init];

	@try {
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
		size_t pageSize = [OFSystemInfo pageSize];
#endif

		if (count > SIZE_MAX / itemSize)
			@throw [OFOutOfRangeException exception];

		if (allowsSwappableMemory) {
			_items = [self allocMemoryWithSize: itemSize
						     count: count];
			memset(_items, 0, count * itemSize);
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
		} else if (count * itemSize >= pageSize)
			_items = mapPages(OF_ROUND_UP_POW2(pageSize,
			    count * itemSize) / pageSize);
		else {
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
			struct page *lastPage = of_tlskey_get(lastPageKey);
# endif

			for (struct page *page = lastPage; page != NULL;
			    page = page->previous) {
				_items = allocateMemory(page, count * itemSize);

				if (_items != NULL) {
					_page = page;
					break;
				}







|
|
|


|
|
|












|
|



|



|


|







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
	OF_UNRECOGNIZED_SELECTOR
}


- (instancetype)initWithCount: (size_t)count
	allowsSwappableMemory: (bool)allowsSwappableMemory
{
	return [self initWithCount: count
			  itemSize: 1
	     allowsSwappableMemory: allowsSwappableMemory];
}

- (instancetype)initWithCount: (size_t)count
		     itemSize: (size_t)itemSize
	allowsSwappableMemory: (bool)allowsSwappableMemory
{
	self = [super init];

	@try {
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
		size_t pageSize = [OFSystemInfo pageSize];
#endif

		if (count > SIZE_MAX / itemSize)
			@throw [OFOutOfRangeException exception];

		if (allowsSwappableMemory) {
			_items = OFAllocMemory(count, itemSize);
			_freeWhenDone = true;
			memset(_items, 0, count * itemSize);
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
		} else if (count * itemSize >= pageSize)
			_items = mapPages(OFRoundUpToPowerOf2(pageSize,
			    count * itemSize) / pageSize);
		else {
# if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
			struct Page *lastPage = OFTLSKeyGet(lastPageKey);
# endif

			for (struct Page *page = lastPage; page != NULL;
			    page = page->previous) {
				_items = allocateMemory(page, count * itemSize);

				if (_items != NULL) {
					_page = page;
					break;
				}
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
#else
		} else
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: nil];
#endif

		_itemSize = itemSize;
		_count = count;

		_allowsSwappableMemory = allowsSwappableMemory;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithItems: (const void *)items
			count: (size_t)count
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithItems: (const void *)items
		     itemSize: (size_t)itemSize
			count: (size_t)count

{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithItemsNoCopy: (void *)items
			   itemSize: (size_t)itemSize
			      count: (size_t)count

		       freeWhenDone: (bool)freeWhenDone
{
	OF_INVALID_INIT_METHOD
}

#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path







<

>









|
<





<

>












<

>







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
#else
		} else
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: nil];
#endif


		_count = count;
		_itemSize = itemSize;
		_allowsSwappableMemory = allowsSwappableMemory;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithItems: (const void *)items count: (size_t)count

{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithItems: (const void *)items

			count: (size_t)count
		     itemSize: (size_t)itemSize
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithItemsNoCopy: (void *)items
			      count: (size_t)count
		       freeWhenDone: (bool)freeWhenDone
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithItemsNoCopy: (void *)items

			      count: (size_t)count
			   itemSize: (size_t)itemSize
		       freeWhenDone: (bool)freeWhenDone
{
	OF_INVALID_INIT_METHOD
}

#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541

#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	if (!_allowsSwappableMemory) {
		size_t pageSize = [OFSystemInfo pageSize];

		if (_count * _itemSize > pageSize)
			unmapPages(_items,
			    OF_ROUND_UP_POW2(pageSize, _count * _itemSize) /
			    pageSize);
		else if (_page != NULL) {
			if (_items != NULL)
				freeMemory(_page, _items, _count * _itemSize);

			removePageIfEmpty(_page);
		}







|







525
526
527
528
529
530
531
532
533
534
535
536
537
538
539

#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	if (!_allowsSwappableMemory) {
		size_t pageSize = [OFSystemInfo pageSize];

		if (_count * _itemSize > pageSize)
			unmapPages(_items,
			    OFRoundUpToPowerOf2(pageSize, _count * _itemSize) /
			    pageSize);
		else if (_page != NULL) {
			if (_items != NULL)
				freeMemory(_page, _items, _count * _itemSize);

			removePageIfEmpty(_page);
		}
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
		@throw [OFOutOfRangeException exception];

	return _items + idx * _itemSize;
}

- (void)zero
{
	of_explicit_memset(_items, 0, _count * _itemSize);
}

- (id)copy
{
	OFSecureData *copy = [[OFSecureData alloc]

		 initWithItemSize: _itemSize
			    count: _count
	    allowsSwappableMemory: _allowsSwappableMemory];

	memcpy(copy.mutableItems, _items, _count * _itemSize);

	return copy;
}

- (id)mutableCopy
{
	OFSecureData *copy = [[OFSecureData alloc]

		 initWithItemSize: _itemSize
			    count: _count
	    allowsSwappableMemory: _allowsSwappableMemory];

	memcpy(copy.mutableItems, _items, _count * _itemSize);

	return copy;
}








|





>
|
<










>
|
<







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
		@throw [OFOutOfRangeException exception];

	return _items + idx * _itemSize;
}

- (void)zero
{
	OFZeroMemory(_items, _count * _itemSize);
}

- (id)copy
{
	OFSecureData *copy = [[OFSecureData alloc]
		    initWithCount: _count
			 itemSize: _itemSize

	    allowsSwappableMemory: _allowsSwappableMemory];

	memcpy(copy.mutableItems, _items, _count * _itemSize);

	return copy;
}

- (id)mutableCopy
{
	OFSecureData *copy = [[OFSecureData alloc]
		    initWithCount: _count
			 itemSize: _itemSize

	    allowsSwappableMemory: _allowsSwappableMemory];

	memcpy(copy.mutableItems, _items, _count * _itemSize);

	return copy;
}

Modified src/OFSeekableStream.h from [1f7744a123] to [dfe5585e9c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#endif

#import "OFStream.h"

OF_ASSUME_NONNULL_BEGIN

#if defined(OF_WINDOWS)
typedef __int64 of_offset_t;
#elif defined(OF_ANDROID)
typedef long long of_offset_t;
#elif defined(OF_MORPHOS)
typedef signed long long of_offset_t;
#elif defined(OF_HAVE_OFF64_T)
typedef off64_t of_offset_t;
#else
typedef off_t of_offset_t;
#endif

/**
 * @class OFSeekableStream OFSeekableStream.h ObjFW/OFSeekableStream.h
 *
 * @brief A stream that supports seeking.
 *







|

|

|

|

|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#endif

#import "OFStream.h"

OF_ASSUME_NONNULL_BEGIN

#if defined(OF_WINDOWS)
typedef __int64 OFFileOffset;
#elif defined(OF_ANDROID)
typedef long long OFFileOffset;
#elif defined(OF_MORPHOS)
typedef signed long long OFFileOffset;
#elif defined(OF_HAVE_OFF64_T)
typedef off64_t OFFileOffset;
#else
typedef off_t OFFileOffset;
#endif

/**
 * @class OFSeekableStream OFSeekableStream.h ObjFW/OFSeekableStream.h
 *
 * @brief A stream that supports seeking.
 *
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
 *		 Value      | Description
 *		 -----------|---------------------------------------
 *		 `SEEK_SET` | Seek to the specified byte
 *		 `SEEK_CUR` | Seek to the current location + offset
 *		 `SEEK_END` | Seek to the end of the stream + offset
 * @return The new offset form the start of the file
 */
- (of_offset_t)seekToOffset: (of_offset_t)offset
		     whence: (int)whence;

/**
 * @brief Seek the stream on the lowlevel.
 *
 * @warning Do not call this directly!
 *
 * @note Override this method with your actual seek implementation when
 *	 subclassing!
 *
 * @param offset The offset to seek to
 * @param whence From where to seek.@n
 *		 Possible values are:
 *		 Value      | Description
 *		 -----------|---------------------------------------
 *		 `SEEK_SET` | Seek to the specified byte
 *		 `SEEK_CUR` | Seek to the current location + offset
 *		 `SEEK_END` | Seek to the end of the stream + offset
 * @return The new offset from the start of the file
 */
- (of_offset_t)lowlevelSeekToOffset: (of_offset_t)offset
			     whence: (int)whence;
@end

OF_ASSUME_NONNULL_END







<
|



















<
|



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
 *		 Value      | Description
 *		 -----------|---------------------------------------
 *		 `SEEK_SET` | Seek to the specified byte
 *		 `SEEK_CUR` | Seek to the current location + offset
 *		 `SEEK_END` | Seek to the end of the stream + offset
 * @return The new offset form the start of the file
 */

- (OFFileOffset)seekToOffset: (OFFileOffset)offset whence: (int)whence;

/**
 * @brief Seek the stream on the lowlevel.
 *
 * @warning Do not call this directly!
 *
 * @note Override this method with your actual seek implementation when
 *	 subclassing!
 *
 * @param offset The offset to seek to
 * @param whence From where to seek.@n
 *		 Possible values are:
 *		 Value      | Description
 *		 -----------|---------------------------------------
 *		 `SEEK_SET` | Seek to the specified byte
 *		 `SEEK_CUR` | Seek to the current location + offset
 *		 `SEEK_END` | Seek to the end of the stream + offset
 * @return The new offset from the start of the file
 */

- (OFFileOffset)lowlevelSeekToOffset: (OFFileOffset)offset whence: (int)whence;
@end

OF_ASSUME_NONNULL_END

Modified src/OFSeekableStream.m from [57a7a2c048] to [9e766aeca1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
			@throw e;
		}
	}

	return [super init];
}

- (of_offset_t)lowlevelSeekToOffset: (of_offset_t)offset
			     whence: (int)whence
{
	OF_UNRECOGNIZED_SELECTOR
}

- (of_offset_t)seekToOffset: (of_offset_t)offset
		     whence: (int)whence
{
	if (whence == SEEK_CUR)
		offset -= _readBufferLength;

	offset = [self lowlevelSeekToOffset: offset
				     whence: whence];

	[self freeMemory: _readBufferMemory];
	_readBuffer = _readBufferMemory = NULL;
	_readBufferLength = 0;

	return offset;
}
@end







<
|




<
|




|
<

|






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
			@throw e;
		}
	}

	return [super init];
}


- (OFFileOffset)lowlevelSeekToOffset: (OFFileOffset)offset whence: (int)whence
{
	OF_UNRECOGNIZED_SELECTOR
}


- (OFFileOffset)seekToOffset: (OFFileOffset)offset whence: (int)whence
{
	if (whence == SEEK_CUR)
		offset -= _readBufferLength;

	offset = [self lowlevelSeekToOffset: offset whence: whence];


	OFFreeMemory(_readBufferMemory);
	_readBuffer = _readBufferMemory = NULL;
	_readBufferLength = 0;

	return offset;
}
@end

Modified src/OFSelectKernelEventObserver.h from [4b0f84060b] to [87e3b347a2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFSelectKernelEventObserver.m from [22b01117bc] to [14ac36d0e0].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

#define __NO_EXT_QNX

#include "config.h"

#include "platform.h"

#ifdef OF_WINDOWS
/* Win32 has a ridiculous default of 64, even though it supports much more. */
# define FD_SETSIZE 1024
#endif

#include <errno.h>
#include <string.h>

#include <sys/time.h>

#import "OFSelectKernelEventObserver.h"
#import "OFArray.h"


#import "OFInitializationFailedException.h"
#import "OFObserveFailedException.h"
#import "OFOutOfRangeException.h"

#import "socket_helpers.h"

#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif






@implementation OFSelectKernelEventObserver
- (instancetype)init
{
	self = [super init];

	@try {



#ifdef OF_AMIGAOS
		_maxFD = 0;
#else
# ifndef OF_WINDOWS
		if (_cancelFD[0] >= (int)FD_SETSIZE)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
# endif

		FD_ZERO(&_readFDs);
		FD_ZERO(&_writeFDs);
		FD_SET(_cancelFD[0], &_readFDs);

		if (_cancelFD[0] > INT_MAX)
			@throw [OFOutOfRangeException exception];

		_maxFD = (int)_cancelFD[0];
#endif

<
<
|













|

|















>





<
<



>
>
>
>
>







>
>
>

|







<
<







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


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

#define __NO_EXT_QNX

#include "platform.h"

#ifdef OF_WINDOWS
/* Win32 has a ridiculous default of 64, even though it supports much more. */
# define FD_SETSIZE 1024
#endif

#include <errno.h>
#include <string.h>

#include <sys/time.h>

#import "OFSelectKernelEventObserver.h"
#import "OFArray.h"
#import "OFSocket+Private.h"

#import "OFInitializationFailedException.h"
#import "OFObserveFailedException.h"
#import "OFOutOfRangeException.h"



#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif

#ifdef OF_HPUX
/* FD_SET causes warnings on HP-UX/IA64. */
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif

@implementation OFSelectKernelEventObserver
- (instancetype)init
{
	self = [super init];

	@try {
		FD_ZERO(&_readFDs);
		FD_ZERO(&_writeFDs);

#ifdef OF_AMIGAOS
		_maxFD = -1;
#else
# ifndef OF_WINDOWS
		if (_cancelFD[0] >= (int)FD_SETSIZE)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
# endif



		FD_SET(_cancelFD[0], &_readFDs);

		if (_cancelFD[0] > INT_MAX)
			@throw [OFOutOfRangeException exception];

		_maxFD = (int)_cancelFD[0];
#endif
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
	if (fd >= (int)FD_SETSIZE)
		@throw [OFOutOfRangeException exception];
#endif

	if (fd > _maxFD)
		_maxFD = fd;

	FD_SET((of_socket_t)fd, &_readFDs);

	[super addObjectForReading: object];
}

- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	int fd = object.fileDescriptorForWriting;







|







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
	if (fd >= (int)FD_SETSIZE)
		@throw [OFOutOfRangeException exception];
#endif

	if (fd > _maxFD)
		_maxFD = fd;

	FD_SET((OFSocketHandle)fd, &_readFDs);

	[super addObjectForReading: object];
}

- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	int fd = object.fileDescriptorForWriting;
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
	if (fd >= (int)FD_SETSIZE)
		@throw [OFOutOfRangeException exception];
#endif

	if (fd > _maxFD)
		_maxFD = fd;

	FD_SET((of_socket_t)fd, &_writeFDs);

	[super addObjectForWriting: object];
}

- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object
{
	/* TODO: Adjust _maxFD */

	int fd = object.fileDescriptorForReading;

	if (fd < 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EBADF];

#ifndef OF_WINDOWS
	if (fd >= (int)FD_SETSIZE)
		@throw [OFOutOfRangeException exception];
#endif

	FD_CLR((of_socket_t)fd, &_readFDs);

	[super removeObjectForReading: object];
}

- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	/* TODO: Adjust _maxFD */

	int fd = object.fileDescriptorForWriting;

	if (fd < 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EBADF];


#ifndef OF_WINDOWS
	if (fd >= (int)FD_SETSIZE)
		@throw [OFOutOfRangeException exception];
#endif

	FD_CLR((of_socket_t)fd, &_writeFDs);

	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (of_time_interval_t)timeInterval
{
	fd_set readFDs;
	fd_set writeFDs;
	struct timeval timeout;
	int events;
#ifdef OF_AMIGAOS

	ULONG execSignalMask, cancelSignal;
#endif
	void *pool;

	if ([self of_processReadBuffers])
		return;

#ifdef FD_COPY







|



















|




















|




|






>
|







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
	if (fd >= (int)FD_SETSIZE)
		@throw [OFOutOfRangeException exception];
#endif

	if (fd > _maxFD)
		_maxFD = fd;

	FD_SET((OFSocketHandle)fd, &_writeFDs);

	[super addObjectForWriting: object];
}

- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object
{
	/* TODO: Adjust _maxFD */

	int fd = object.fileDescriptorForReading;

	if (fd < 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EBADF];

#ifndef OF_WINDOWS
	if (fd >= (int)FD_SETSIZE)
		@throw [OFOutOfRangeException exception];
#endif

	FD_CLR((OFSocketHandle)fd, &_readFDs);

	[super removeObjectForReading: object];
}

- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object
{
	/* TODO: Adjust _maxFD */

	int fd = object.fileDescriptorForWriting;

	if (fd < 0)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EBADF];


#ifndef OF_WINDOWS
	if (fd >= (int)FD_SETSIZE)
		@throw [OFOutOfRangeException exception];
#endif

	FD_CLR((OFSocketHandle)fd, &_writeFDs);

	[super removeObjectForWriting: object];
}

- (void)observeForTimeInterval: (OFTimeInterval)timeInterval
{
	fd_set readFDs;
	fd_set writeFDs;
	struct timeval timeout;
	int events;
#ifdef OF_AMIGAOS
	BYTE cancelSignal;
	ULONG execSignalMask;
#endif
	void *pool;

	if ([self of_processReadBuffers])
		return;

#ifdef FD_COPY
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
	 * satisfy the required range, we just cast to int.
	 */
#ifndef OF_WINDOWS
	timeout.tv_sec = (time_t)timeInterval;
#else
	timeout.tv_sec = (long)timeInterval;
#endif
	timeout.tv_usec = (int)((timeInterval - timeout.tv_sec) * 1000);

#ifdef OF_AMIGAOS
	if ((cancelSignal = AllocSignal(-1)) == (ULONG)-1)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EAGAIN];

	execSignalMask = _execSignalMask | (1ul << cancelSignal);

	Forbid();








|


|







198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
	 * satisfy the required range, we just cast to int.
	 */
#ifndef OF_WINDOWS
	timeout.tv_sec = (time_t)timeInterval;
#else
	timeout.tv_sec = (long)timeInterval;
#endif
	timeout.tv_usec = (int)((timeInterval - timeout.tv_sec) * 1000000);

#ifdef OF_AMIGAOS
	if ((cancelSignal = AllocSignal(-1)) == (BYTE)-1)
		@throw [OFObserveFailedException exceptionWithObserver: self
								 errNo: EAGAIN];

	execSignalMask = _execSignalMask | (1ul << cancelSignal);

	Forbid();

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
	events = select(_maxFD + 1, &readFDs, &writeFDs, NULL,
	    (timeInterval != -1 ? &timeout : NULL));
#endif

	if (events < 0)
		@throw [OFObserveFailedException
		    exceptionWithObserver: self
				    errNo: of_socket_errno()];

#ifdef OF_AMIGAOS
	if (execSignalMask != 0 &&
	    [_delegate respondsToSelector: @selector(execSignalWasReceived:)])
		[_delegate execSignalWasReceived: execSignalMask];
#else
	if (FD_ISSET(_cancelFD[0], &readFDs)) {
		char buffer;

# ifdef OF_HAVE_PIPE
		OF_ENSURE(read(_cancelFD[0], &buffer, 1) == 1);
# else
		OF_ENSURE(recvfrom(_cancelFD[0], (void *)&buffer, 1, 0, NULL,
		    NULL) == 1);
# endif
	}
#endif

	pool = objc_autoreleasePoolPush();

	for (id <OFReadyForReadingObserving> object in
	    [[_readObjects copy] autorelease]) {
		void *pool2 = objc_autoreleasePoolPush();
		int fd = object.fileDescriptorForReading;

		if (FD_ISSET((of_socket_t)fd, &readFDs) &&
		    [_delegate respondsToSelector:
		    @selector(objectIsReadyForReading:)])
			[_delegate objectIsReadyForReading: object];

		objc_autoreleasePoolPop(pool2);
	}

	for (id <OFReadyForWritingObserving> object in
	    [[_writeObjects copy] autorelease]) {
		void *pool2 = objc_autoreleasePoolPush();
		int fd = object.fileDescriptorForWriting;

		if (FD_ISSET((of_socket_t)fd, &writeFDs) &&
		    [_delegate respondsToSelector:
		    @selector(objectIsReadyForWriting:)])
			[_delegate objectIsReadyForWriting: object];

		objc_autoreleasePoolPop(pool2);
	}

	objc_autoreleasePoolPop(pool);
}
@end







|










|

|












|












|










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
	events = select(_maxFD + 1, &readFDs, &writeFDs, NULL,
	    (timeInterval != -1 ? &timeout : NULL));
#endif

	if (events < 0)
		@throw [OFObserveFailedException
		    exceptionWithObserver: self
				    errNo: OFSocketErrNo()];

#ifdef OF_AMIGAOS
	if (execSignalMask != 0 &&
	    [_delegate respondsToSelector: @selector(execSignalWasReceived:)])
		[_delegate execSignalWasReceived: execSignalMask];
#else
	if (FD_ISSET(_cancelFD[0], &readFDs)) {
		char buffer;

# ifdef OF_HAVE_PIPE
		OFEnsure(read(_cancelFD[0], &buffer, 1) == 1);
# else
		OFEnsure(recvfrom(_cancelFD[0], (void *)&buffer, 1, 0, NULL,
		    NULL) == 1);
# endif
	}
#endif

	pool = objc_autoreleasePoolPush();

	for (id <OFReadyForReadingObserving> object in
	    [[_readObjects copy] autorelease]) {
		void *pool2 = objc_autoreleasePoolPush();
		int fd = object.fileDescriptorForReading;

		if (FD_ISSET((OFSocketHandle)fd, &readFDs) &&
		    [_delegate respondsToSelector:
		    @selector(objectIsReadyForReading:)])
			[_delegate objectIsReadyForReading: object];

		objc_autoreleasePoolPop(pool2);
	}

	for (id <OFReadyForWritingObserving> object in
	    [[_writeObjects copy] autorelease]) {
		void *pool2 = objc_autoreleasePoolPush();
		int fd = object.fileDescriptorForWriting;

		if (FD_ISSET((OFSocketHandle)fd, &writeFDs) &&
		    [_delegate respondsToSelector:
		    @selector(objectIsReadyForWriting:)])
			[_delegate objectIsReadyForWriting: object];

		objc_autoreleasePoolPop(pool2);
	}

	objc_autoreleasePoolPop(pool);
}
@end

Modified src/OFSequencedPacketSocket+Private.h from [a194b9d55d] to [779ca7e02d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFSequencedPacketSocket.h from [d3b63ac877] to [c4199b3715].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFKernelEventObserver.h"
#import "OFRunLoop.h"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFData;
@class OFSequencedPacketSocket;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when a packet has been received.
 *
 * @param length The length of the packet
 * @param exception An exception which occurred while receiving or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next receive
 */
typedef bool (^of_sequenced_packet_socket_async_receive_block_t)(size_t length,
    id _Nullable exception);

/**
 * @brief A block which is called when a packet has been sent.
 *
 * @param data The data which was sent
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return The data to repeat the send with or nil if it should not repeat
 */
typedef OFData *_Nullable (^of_sequenced_packet_socket_async_send_data_block_t)(
    OFData *_Nonnull data, id _Nullable exception);

/**
 * @brief A block which is called when the socket accepted a connection.
 *
 * @param acceptedSocket The socket which has been accepted
 * @param exception An exception which occurred while accepting the socket or
 *		    `nil` on success
 * @return A bool whether the same block should be used for the next incoming
 *	   connection
 */
typedef bool (^of_sequenced_packet_socket_async_accept_block_t)(
    OFSequencedPacketSocket *acceptedSocket, id _Nullable exception);
#endif

/**
 * @protocol OFSequencedPacketSocketDelegate OFSequencedPacketSocket.h \
 *	     ObjFW/OFSequencedPacketSocket.h
 *

<
<
|
















<
|

















|





<




|
|










|







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


 * 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 "OFKernelEventObserver.h"
#import "OFRunLoop.h"

#import "OFSocket.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFData;
@class OFSequencedPacketSocket;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when a packet has been received.
 *
 * @param length The length of the packet
 * @param exception An exception which occurred while receiving or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next receive
 */
typedef bool (^OFSequencedPacketSocketAsyncReceiveBlock)(size_t length,
    id _Nullable exception);

/**
 * @brief A block which is called when a packet has been sent.
 *

 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return The data to repeat the send with or nil if it should not repeat
 */
typedef OFData *_Nullable (^OFSequencedPacketSocketAsyncSendDataBlock)(
    id _Nullable exception);

/**
 * @brief A block which is called when the socket accepted a connection.
 *
 * @param acceptedSocket The socket which has been accepted
 * @param exception An exception which occurred while accepting the socket or
 *		    `nil` on success
 * @return A bool whether the same block should be used for the next incoming
 *	   connection
 */
typedef bool (^OFSequencedPacketSocketAsyncAcceptBlock)(
    OFSequencedPacketSocket *acceptedSocket, id _Nullable exception);
#endif

/**
 * @protocol OFSequencedPacketSocketDelegate OFSequencedPacketSocket.h \
 *	     ObjFW/OFSequencedPacketSocket.h
 *
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
 *	    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 OFSequencedPacketSocket: OFObject <OFCopying,
    OFReadyForReadingObserving, OFReadyForWritingObserving>
{
	of_socket_t _socket;
	bool _canBlock, _listening;
	of_socket_address_t _remoteAddress;
	id _Nullable _delegate;
	OF_RESERVE_IVARS(OFSequencedPacketSocket, 4)
}

/**
 * @brief Whether the socket can block.
 *







|

|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
 *	    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 OFSequencedPacketSocket: OFObject <OFCopying,
    OFReadyForReadingObserving, OFReadyForWritingObserving>
{
	OFSocketHandle _socket;
	bool _canBlock, _listening;
	OFSocketAddress _remoteAddress;
	id _Nullable _delegate;
	OF_RESERVE_IVARS(OFSequencedPacketSocket, 4)
}

/**
 * @brief Whether the socket can block.
 *
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@property (readonly, nonatomic, getter=isListening) bool listening;

/**
 * @brief The remote address.
 *
 * @note This only works for accepted sockets!
 */
@property (readonly, nonatomic) const of_socket_address_t *remoteAddress;

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







|







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@property (readonly, nonatomic, getter=isListening) bool listening;

/**
 * @brief The remote address.
 *
 * @note This only works for accepted sockets!
 */
@property (readonly, nonatomic) const OFSocketAddress *remoteAddress;

/**
 * @brief The delegate for asynchronous operations on the socket.
 *
 * @note The delegate is retained for as long as asynchronous operations are
 *	 still ongoing.
 */
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
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 * @return The length of the received packet
 */
- (size_t)receiveIntoBuffer: (void *)buffer
		     length: (size_t)length;

/**
 * @brief Asynchronously receives a packet and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length;

/**
 * @brief Asynchronously receives a packet and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 * @param runLoopMode The run loop mode in which to perform the async receive
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously receives a packet and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 * @param block The block to call when the packet has been received. If the
 *		block returns true, it will be called again with the same
 *		buffer and maximum length when more packets have been received.
 *		If you want the next method in the queue to handle the packet
 *		received next, you need to return false from the method.
 */
- (void)
    asyncReceiveIntoBuffer: (void *)buffer
		    length: (size_t)length
		     block: (of_sequenced_packet_socket_async_receive_block_t)
				block;

/**
 * @brief Asynchronously receives a packet and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 * @param runLoopMode The run loop mode in which to perform the async receive
 * @param block The block to call when the packet has been received. If the
 *		block returns true, it will be called again with the same
 *		buffer and maximum length when more packets have been received.
 *		If you want the next method in the queue to handle the packet
 *		received next, you need to return false from the method.
 */
- (void)
    asyncReceiveIntoBuffer: (void *)buffer
		    length: (size_t)length
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_sequenced_packet_socket_async_receive_block_t)
				block;
#endif

/**
 * @brief Sends the specified packet.
 *
 * @param buffer The buffer to send as a packet
 * @param length The length of the buffer
 */
- (void)sendBuffer: (const void *)buffer
	    length: (size_t)length;

/**
 * @brief Asynchronously sends the specified packet.
 *
 * @param data The data to send as a packet
 */
- (void)asyncSendData: (OFData *)data;

/**
 * @brief Asynchronously sends the specified packet.
 *
 * @param data The data to send as a packet
 * @param runLoopMode The run loop mode in which to perform the async send
 */
- (void)asyncSendData: (OFData *)data
	  runLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously sends the specified packet.
 *
 * @param data The data to send as a packet
 * @param block The block to call when the packet has been sent. It should
 *		return the data for the next send with the same callback or nil
 *		if it should not repeat.
 */
- (void)asyncSendData: (OFData *)data
		block: (of_sequenced_packet_socket_async_send_data_block_t)
			   block;

/**
 * @brief Asynchronously sends the specified packet.
 *
 * @param data The data to send as a packet
 * @param runLoopMode The run loop mode in which to perform the async send
 * @param block The block to call when the packet has been sent. It should
 *		return the data for the next send with the same callback or nil
 *		if it should not repeat.
 */
- (void)asyncSendData: (OFData *)data
	  runLoopMode: (of_run_loop_mode_t)runLoopMode
		block: (of_sequenced_packet_socket_async_send_data_block_t)
			   block;
#endif

/**
 * @brief Listen on the socket.
 *
 * @param backlog Maximum length for the queue of pending connections.
 */







|
<










|
<













|
















<
|
|
<
|
















<
|
|
|
<
|








|
<














|
<











<
|











|
<
|







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
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 * @return The length of the received packet
 */
- (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length;


/**
 * @brief Asynchronously receives a packet and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer length: (size_t)length;


/**
 * @brief Asynchronously receives a packet and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 * @param runLoopMode The run loop mode in which to perform the async receive
 */
- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (OFRunLoopMode)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously receives a packet and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 * @param block The block to call when the packet has been received. If the
 *		block returns true, it will be called again with the same
 *		buffer and maximum length when more packets have been received.
 *		If you want the next method in the queue to handle the packet
 *		received next, you need to return false from the method.
 */

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length

			 block: (OFSequencedPacketSocketAsyncReceiveBlock)block;

/**
 * @brief Asynchronously receives a packet and stores it into the specified
 *	  buffer.
 *
 * If the buffer is too small, the receive operation fails.
 *
 * @param buffer The buffer to write the packet to
 * @param length The length of the buffer
 * @param runLoopMode The run loop mode in which to perform the async receive
 * @param block The block to call when the packet has been received. If the
 *		block returns true, it will be called again with the same
 *		buffer and maximum length when more packets have been received.
 *		If you want the next method in the queue to handle the packet
 *		received next, you need to return false from the method.
 */

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (OFRunLoopMode)runLoopMode

			 block: (OFSequencedPacketSocketAsyncReceiveBlock)block;
#endif

/**
 * @brief Sends the specified packet.
 *
 * @param buffer The buffer to send as a packet
 * @param length The length of the buffer
 */
- (void)sendBuffer: (const void *)buffer length: (size_t)length;


/**
 * @brief Asynchronously sends the specified packet.
 *
 * @param data The data to send as a packet
 */
- (void)asyncSendData: (OFData *)data;

/**
 * @brief Asynchronously sends the specified packet.
 *
 * @param data The data to send as a packet
 * @param runLoopMode The run loop mode in which to perform the async send
 */
- (void)asyncSendData: (OFData *)data runLoopMode: (OFRunLoopMode)runLoopMode;


#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously sends the specified packet.
 *
 * @param data The data to send as a packet
 * @param block The block to call when the packet has been sent. It should
 *		return the data for the next send with the same callback or nil
 *		if it should not repeat.
 */
- (void)asyncSendData: (OFData *)data

		block: (OFSequencedPacketSocketAsyncSendDataBlock)block;

/**
 * @brief Asynchronously sends the specified packet.
 *
 * @param data The data to send as a packet
 * @param runLoopMode The run loop mode in which to perform the async send
 * @param block The block to call when the packet has been sent. It should
 *		return the data for the next send with the same callback or nil
 *		if it should not repeat.
 */
- (void)asyncSendData: (OFData *)data
	  runLoopMode: (OFRunLoopMode)runLoopMode

		block: (OFSequencedPacketSocketAsyncSendDataBlock)block;
#endif

/**
 * @brief Listen on the socket.
 *
 * @param backlog Maximum length for the queue of pending connections.
 */
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
- (void)asyncAccept;

/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param runLoopMode The run loop mode in which to perform the async accept
 */
- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param block The block to execute when a new connection has been accepted.
 *		Returns whether the next incoming connection should be accepted
 *		by the specified block as well.
 */
- (void)asyncAcceptWithBlock:
    (of_sequenced_packet_socket_async_accept_block_t)block;

/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param runLoopMode The run loop mode in which to perform the async accept
 * @param block The block to execute when a new connection has been accepted.
 *		Returns whether the next incoming connection should be accepted
 *		by the specified block as well.
 */

- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
    block: (of_sequenced_packet_socket_async_accept_block_t)block;
#endif

/**
 * @brief Cancels all pending asynchronous requests on the socket.
 */
- (void)cancelAsyncRequests;








|









|
<









>
|
|







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
- (void)asyncAccept;

/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param runLoopMode The run loop mode in which to perform the async accept
 */
- (void)asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param block The block to execute when a new connection has been accepted.
 *		Returns whether the next incoming connection should be accepted
 *		by the specified block as well.
 */
- (void)asyncAcceptWithBlock: (OFSequencedPacketSocketAsyncAcceptBlock)block;


/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param runLoopMode The run loop mode in which to perform the async accept
 * @param block The block to execute when a new connection has been accepted.
 *		Returns whether the next incoming connection should be accepted
 *		by the specified block as well.
 */
- (void)
    asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode
			 block: (OFSequencedPacketSocketAsyncAcceptBlock)block;
#endif

/**
 * @brief Cancels all pending asynchronous requests on the socket.
 */
- (void)cancelAsyncRequests;

Modified src/OFSequencedPacketSocket.m from [395d4dcdd4] to [da50d869cb].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <assert.h>
#include <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFSequencedPacketSocket.h"
#import "OFSequencedPacketSocket+Private.h"
#import "OFData.h"
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"



#import "OFAcceptFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFListenFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFWriteFailedException.h"

#import "socket.h"
#import "socket_helpers.h"

@implementation OFSequencedPacketSocket
@synthesize listening = _listening, delegate = _delegate;

+ (void)initialize
{
	if (self != [OFSequencedPacketSocket class])
		return;

	if (!of_socket_init())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)socket
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	@try {
		if (self.class == [OFSequencedPacketSocket class]) {
			[self doesNotRecognizeSelector: _cmd];
			abort();
		}

		_socket = INVALID_SOCKET;
		_canBlock = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_socket != INVALID_SOCKET)
		[self close];

	[super dealloc];
}

#ifndef OF_WII
- (int)of_socketError
{
	int errNo;
	socklen_t len = sizeof(errNo);

	if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char *)&errNo,
	    &len) != 0)
		return of_socket_errno();

	return errNo;
}
#endif

- (id)copy
{

<
<
|















>
>
>
>
>












>
>











<
<
<








|



















|











|













|







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


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

#ifndef _XOPEN_SOURCE_EXTENDED
# define _XOPEN_SOURCE_EXTENDED
#endif
#define _HPUX_ALT_XOPEN_SOCKET_API

#include <assert.h>
#include <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFSequencedPacketSocket.h"
#import "OFSequencedPacketSocket+Private.h"
#import "OFData.h"
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"

#import "OFAcceptFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFListenFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFWriteFailedException.h"




@implementation OFSequencedPacketSocket
@synthesize listening = _listening, delegate = _delegate;

+ (void)initialize
{
	if (self != [OFSequencedPacketSocket class])
		return;

	if (!OFSocketInit())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)socket
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	@try {
		if (self.class == [OFSequencedPacketSocket class]) {
			[self doesNotRecognizeSelector: _cmd];
			abort();
		}

		_socket = OFInvalidSocketHandle;
		_canBlock = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_socket != OFInvalidSocketHandle)
		[self close];

	[super dealloc];
}

#ifndef OF_WII
- (int)of_socketError
{
	int errNo;
	socklen_t len = sizeof(errNo);

	if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char *)&errNo,
	    &len) != 0)
		return OFSocketErrNo();

	return errNo;
}
#endif

- (id)copy
{
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
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

	if (fcntl(_socket, F_SETFL, flags) == -1)
		@throw [OFSetOptionFailedException exceptionWithObject: self
								 errNo: errno];

	_canBlock = canBlock;
#elif defined(OF_WINDOWS)
	u_long v = canBlock;

	if (ioctlsocket(_socket, FIONBIO, &v) == SOCKET_ERROR)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	_canBlock = canBlock;
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (size_t)receiveIntoBuffer: (void *)buffer
		     length: (size_t)length
{
	ssize_t ret;

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

#ifndef OF_WINDOWS
	if ((ret = recv(_socket, buffer, length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
#else
	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((ret = recv(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
#endif

	return ret;
}

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
{
	[self asyncReceiveIntoBuffer: buffer
			      length: length
			 runLoopMode: of_run_loop_mode_default];
}

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	[OFRunLoop of_addAsyncReceiveForSequencedPacketSocket: self
						       buffer: buffer
						       length: length
							 mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
							block: NULL
# endif
						     delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)
    asyncReceiveIntoBuffer: (void *)buffer
		    length: (size_t)length
		     block: (of_sequenced_packet_socket_async_receive_block_t)
				block
{
	[self asyncReceiveIntoBuffer: buffer
			      length: length
			 runLoopMode: of_run_loop_mode_default
			       block: block];
}

- (void)
    asyncReceiveIntoBuffer: (void *)buffer
		    length: (size_t)length
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_sequenced_packet_socket_async_receive_block_t)
				block
{
	[OFRunLoop of_addAsyncReceiveForSequencedPacketSocket: self
						       buffer: buffer
						       length: length
							 mode: runLoopMode
							block: block
						     delegate: nil];
}
#endif

- (void)sendBuffer: (const void *)buffer
	    length: (size_t)length
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

#ifndef OF_WINDOWS
	ssize_t bytesWritten;

	if (length > SSIZE_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = send(_socket, (void *)buffer, length, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: of_socket_errno()];
#else
	int bytesWritten;

	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = send(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: of_socket_errno()];
#endif

	if ((size_t)bytesWritten != length)
		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: bytesWritten
							     errNo: 0];
}

- (void)asyncSendData: (OFData *)data
{
	[self asyncSendData: data
		runLoopMode: of_run_loop_mode_default];
}

- (void)asyncSendData: (OFData *)data
	  runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	[OFRunLoop of_addAsyncSendForSequencedPacketSocket: self
						      data: data
						      mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
						     block: NULL
# endif
						  delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncSendData: (OFData *)data
		block: (of_sequenced_packet_socket_async_send_data_block_t)block
{
	[self asyncSendData: data
		runLoopMode: of_run_loop_mode_default
		      block: block];
}

- (void)asyncSendData: (OFData *)data
	  runLoopMode: (of_run_loop_mode_t)runLoopMode
		block: (of_sequenced_packet_socket_async_send_data_block_t)block
{
	[OFRunLoop of_addAsyncSendForSequencedPacketSocket: self
						      data: data
						      mode: runLoopMode
						     block: block
						  delegate: nil];
}
#endif

- (void)listen
{
	[self listenWithBacklog: SOMAXCONN];
}

- (void)listenWithBacklog: (int)backlog
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (listen(_socket, backlog) == -1)
		@throw [OFListenFailedException
		    exceptionWithSocket: self
				backlog: backlog
				  errNo: of_socket_errno()];

	_listening = true;
}

- (instancetype)accept
{
	OFSequencedPacketSocket *client =







|




|







|
<



|







|








|





|
<



|




|












<
|
|
<
|



|






|
<
|










|
<

|













|











|











|
<


|
<












|


|




|
|
















|






|







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

	if (fcntl(_socket, F_SETFL, flags) == -1)
		@throw [OFSetOptionFailedException exceptionWithObject: self
								 errNo: errno];

	_canBlock = canBlock;
#elif defined(OF_WINDOWS)
	u_long v = !canBlock;

	if (ioctlsocket(_socket, FIONBIO, &v) == SOCKET_ERROR)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];

	_canBlock = canBlock;
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length

{
	ssize_t ret;

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

#ifndef OF_WINDOWS
	if ((ret = recv(_socket, buffer, length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: OFSocketErrNo()];
#else
	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((ret = recv(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: OFSocketErrNo()];
#endif

	return ret;
}

- (void)asyncReceiveIntoBuffer: (void *)buffer length: (size_t)length

{
	[self asyncReceiveIntoBuffer: buffer
			      length: length
			 runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length
		   runLoopMode: (OFRunLoopMode)runLoopMode
{
	[OFRunLoop of_addAsyncReceiveForSequencedPacketSocket: self
						       buffer: buffer
						       length: length
							 mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
							block: NULL
# endif
						     delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS

- (void)asyncReceiveIntoBuffer: (void *)buffer
			length: (size_t)length

			 block: (OFSequencedPacketSocketAsyncReceiveBlock)block
{
	[self asyncReceiveIntoBuffer: buffer
			      length: length
			 runLoopMode: OFDefaultRunLoopMode
			       block: block];
}

- (void)
    asyncReceiveIntoBuffer: (void *)buffer
		    length: (size_t)length
	       runLoopMode: (OFRunLoopMode)runLoopMode

		     block: (OFSequencedPacketSocketAsyncReceiveBlock)block
{
	[OFRunLoop of_addAsyncReceiveForSequencedPacketSocket: self
						       buffer: buffer
						       length: length
							 mode: runLoopMode
							block: block
						     delegate: nil];
}
#endif

- (void)sendBuffer: (const void *)buffer length: (size_t)length

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

#ifndef OF_WINDOWS
	ssize_t bytesWritten;

	if (length > SSIZE_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = send(_socket, (void *)buffer, length, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: OFSocketErrNo()];
#else
	int bytesWritten;

	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = send(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: OFSocketErrNo()];
#endif

	if ((size_t)bytesWritten != length)
		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: bytesWritten
							     errNo: 0];
}

- (void)asyncSendData: (OFData *)data
{
	[self asyncSendData: data runLoopMode: OFDefaultRunLoopMode];

}

- (void)asyncSendData: (OFData *)data runLoopMode: (OFRunLoopMode)runLoopMode

{
	[OFRunLoop of_addAsyncSendForSequencedPacketSocket: self
						      data: data
						      mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
						     block: NULL
# endif
						  delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncSendData: (OFData *)data
		block: (OFSequencedPacketSocketAsyncSendDataBlock)block
{
	[self asyncSendData: data
		runLoopMode: OFDefaultRunLoopMode
		      block: block];
}

- (void)asyncSendData: (OFData *)data
	  runLoopMode: (OFRunLoopMode)runLoopMode
		block: (OFSequencedPacketSocketAsyncSendDataBlock)block
{
	[OFRunLoop of_addAsyncSendForSequencedPacketSocket: self
						      data: data
						      mode: runLoopMode
						     block: block
						  delegate: nil];
}
#endif

- (void)listen
{
	[self listenWithBacklog: SOMAXCONN];
}

- (void)listenWithBacklog: (int)backlog
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (listen(_socket, backlog) == -1)
		@throw [OFListenFailedException
		    exceptionWithSocket: self
				backlog: backlog
				  errNo: OFSocketErrNo()];

	_listening = true;
}

- (instancetype)accept
{
	OFSequencedPacketSocket *client =
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
	client->_remoteAddress.length =
	    (socklen_t)sizeof(client->_remoteAddress.sockaddr);

#if defined(HAVE_PACCEPT) && defined(SOCK_CLOEXEC)
	if ((client->_socket = paccept(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length, NULL, SOCK_CLOEXEC)) ==
	    INVALID_SOCKET)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: of_socket_errno()];
#elif defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
	if ((client->_socket = accept4(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length, SOCK_CLOEXEC)) == INVALID_SOCKET)

		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: of_socket_errno()];
#else
	if ((client->_socket = accept(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length)) == INVALID_SOCKET)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: of_socket_errno()];

# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(client->_socket, F_GETFD, 0)) != -1)
		fcntl(client->_socket, F_SETFD, flags | FD_CLOEXEC);
# endif
#endif

	assert(client->_remoteAddress.length <=
	    (socklen_t)sizeof(client->_remoteAddress.sockaddr));

	switch (client->_remoteAddress.sockaddr.sockaddr.sa_family) {
	case AF_INET:
		client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPV4;
		break;
#ifdef OF_HAVE_IPV6
	case AF_INET6:
		client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPV6;
		break;
#endif
#ifdef OF_HAVE_IPX
	case AF_IPX:
		client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPX;
		break;
#endif
	default:
		client->_remoteAddress.family =
		    OF_SOCKET_ADDRESS_FAMILY_UNKNOWN;
		break;
	}

	return client;
}

- (void)asyncAccept
{
	[self asyncAcceptWithRunLoopMode: of_run_loop_mode_default];
}

- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
{
	[OFRunLoop of_addAsyncAcceptForSocket: self
					 mode: runLoopMode
					block: NULL
				     delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncAcceptWithBlock: (of_sequenced_packet_socket_async_accept_block_t)
				  block
{
	[self asyncAcceptWithRunLoopMode: of_run_loop_mode_default
				   block: block];
}


- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
    block: (of_sequenced_packet_socket_async_accept_block_t)block
{
	[OFRunLoop of_addAsyncAcceptForSocket: self
					 mode: runLoopMode
					block: block
				     delegate: nil];
}
#endif

- (const of_socket_address_t *)remoteAddress
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_remoteAddress.length == 0)
		@throw [OFInvalidArgumentException exception];

	if (_remoteAddress.length > (socklen_t)sizeof(_remoteAddress.sockaddr))
		@throw [OFOutOfRangeException exception];

	return &_remoteAddress;
}

- (void)cancelAsyncRequests
{
	[OFRunLoop of_cancelAsyncRequestsForObject: self
					      mode: of_run_loop_mode_default];
}

- (int)fileDescriptorForReading
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == INVALID_SOCKET)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (int)fileDescriptorForWriting
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == INVALID_SOCKET)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (void)close
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	_listening = false;
	memset(&_remoteAddress, 0, sizeof(_remoteAddress));

	closesocket(_socket);
	_socket = INVALID_SOCKET;
}
@end







|


|



|
>


|



|


|












|



|




|



|
<








|


|








|
<

|
<


>
|
|








|

|














|







|














|











|






|


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
	client->_remoteAddress.length =
	    (socklen_t)sizeof(client->_remoteAddress.sockaddr);

#if defined(HAVE_PACCEPT) && defined(SOCK_CLOEXEC)
	if ((client->_socket = paccept(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length, NULL, SOCK_CLOEXEC)) ==
	    OFInvalidSocketHandle)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: OFSocketErrNo()];
#elif defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
	if ((client->_socket = accept4(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length, SOCK_CLOEXEC)) ==
	    OFInvalidSocketHandle)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: OFSocketErrNo()];
#else
	if ((client->_socket = accept(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length)) == OFInvalidSocketHandle)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: OFSocketErrNo()];

# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(client->_socket, F_GETFD, 0)) != -1)
		fcntl(client->_socket, F_SETFD, flags | FD_CLOEXEC);
# endif
#endif

	assert(client->_remoteAddress.length <=
	    (socklen_t)sizeof(client->_remoteAddress.sockaddr));

	switch (client->_remoteAddress.sockaddr.sockaddr.sa_family) {
	case AF_INET:
		client->_remoteAddress.family = OFSocketAddressFamilyIPv4;
		break;
#ifdef OF_HAVE_IPV6
	case AF_INET6:
		client->_remoteAddress.family = OFSocketAddressFamilyIPv6;
		break;
#endif
#ifdef OF_HAVE_IPX
	case AF_IPX:
		client->_remoteAddress.family = OFSocketAddressFamilyIPX;
		break;
#endif
	default:
		client->_remoteAddress.family = OFSocketAddressFamilyUnknown;

		break;
	}

	return client;
}

- (void)asyncAccept
{
	[self asyncAcceptWithRunLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode
{
	[OFRunLoop of_addAsyncAcceptForSocket: self
					 mode: runLoopMode
					block: NULL
				     delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncAcceptWithBlock: (OFSequencedPacketSocketAsyncAcceptBlock)block

{
	[self asyncAcceptWithRunLoopMode: OFDefaultRunLoopMode block: block];

}

- (void)
    asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode
			 block: (OFSequencedPacketSocketAsyncAcceptBlock)block
{
	[OFRunLoop of_addAsyncAcceptForSocket: self
					 mode: runLoopMode
					block: block
				     delegate: nil];
}
#endif

- (const OFSocketAddress *)remoteAddress
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_remoteAddress.length == 0)
		@throw [OFInvalidArgumentException exception];

	if (_remoteAddress.length > (socklen_t)sizeof(_remoteAddress.sockaddr))
		@throw [OFOutOfRangeException exception];

	return &_remoteAddress;
}

- (void)cancelAsyncRequests
{
	[OFRunLoop of_cancelAsyncRequestsForObject: self
					      mode: OFDefaultRunLoopMode];
}

- (int)fileDescriptorForReading
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == OFInvalidSocketHandle)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (int)fileDescriptorForWriting
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == OFInvalidSocketHandle)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (void)close
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	_listening = false;
	memset(&_remoteAddress, 0, sizeof(_remoteAddress));

	closesocket(_socket);
	_socket = OFInvalidSocketHandle;
}
@end

Modified src/OFSerialization.h from [dd863e03f3] to [6913e132ee].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

OF_ASSUME_NONNULL_BEGIN

#define OF_SERIALIZATION_NS @"https://objfw.nil.im/serialization"

@class OFXMLElement;

/**
 * @protocol OFSerialization OFSerialization.h ObjFW/OFSerialization.h
 *
 * @brief A protocol for serializing objects.
 */

<
<
|

















<
|







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


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

OF_ASSUME_NONNULL_BEGIN


@class OFConstantString;
@class OFXMLElement;

/**
 * @protocol OFSerialization OFSerialization.h ObjFW/OFSerialization.h
 *
 * @brief A protocol for serializing objects.
 */
38
39
40
41
42
43
44
45








46
 * @brief Initializes the object with the specified XML element serialization.
 *
 * @param element An OFXMLElement with the serialized object
 * @return An initialized object
 */
- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end









OF_ASSUME_NONNULL_END








>
>
>
>
>
>
>
>

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 * @brief Initializes the object with the specified XML element serialization.
 *
 * @param element An OFXMLElement with the serialized object
 * @return An initialized object
 */
- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern OFConstantString *const OFSerializationNS;
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Added src/OFSerialization.m version [0e1e99b940].









































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
 * 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 "OFSerialization.h"
#import "OFString.h"

OFConstantString *const OFSerializationNS =
    @"https://objfw.nil.im/serialization";

Modified src/OFSet.h from [18028f8bc4] to [4c0dd3a76f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
 * @brief A block for enumerating an OFSet.
 *
 * @param object The current object
 * @param stop A pointer to a variable that can be set to true to stop the
 *             enumeration
 */
typedef void (^of_set_enumeration_block_t)(id object, bool *stop);

/**
 * @brief A block for filtering an OFSet.
 *
 * @param object The object to inspect
 * @return Whether the object should be in the filtered set
 */
typedef bool (^of_set_filter_block_t)(id object);
#endif

/**
 * @class OFSet OFSet.h ObjFW/OFSet.h
 *
 * @brief An abstract class for an unordered set of unique objects.
 *







|







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
 * @brief A block for enumerating an OFSet.
 *
 * @param object The current object
 * @param stop A pointer to a variable that can be set to true to stop the
 *             enumeration
 */
typedef void (^OFSetEnumerationBlock)(id object, bool *stop);

/**
 * @brief A block for filtering an OFSet.
 *
 * @param object The object to inspect
 * @return Whether the object should be in the filtered set
 */
typedef bool (^OFSetFilterBlock)(id object);
#endif

/**
 * @class OFSet OFSet.h ObjFW/OFSet.h
 *
 * @brief An abstract class for an unordered set of unique objects.
 *
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
 * @brief Initializes an already allocated set with the specified objects.
 *
 * @param firstObject The first object for the set
 * @return An initialized set with the specified objects
 */
- (instancetype)initWithObjects: (ObjectType)firstObject, ... OF_SENTINEL;












/**
 * @brief Initializes an already allocated set with the specified objects.
 *
 * @param objects An array of objects for the set
 * @param count The number of objects in the specified array
 * @return An initialized set with the specified objects
 */
- (instancetype)initWithObjects: (ObjectType const _Nonnull *_Nonnull)objects
			  count: (size_t)count;

/**
 * @brief Initializes an already allocated set with the specified object and
 *	  va_list.
 *
 * @param firstObject The first object for the set
 * @param arguments A va_list with the other objects
 * @return An initialized set with the specified object and va_list
 */
- (instancetype)initWithObject: (ObjectType)firstObject
		     arguments: (va_list)arguments;

/**
 * @brief Returns whether the receiver is a subset of the specified set.
 *
 * @return Whether the receiver is a subset of the specified set
 */
- (bool)isSubsetOfSet: (OFSet OF_GENERIC(ObjectType) *)set;

/**
 * @brief Returns whether the receiver and the specified set have at least one
 *	  object in common.
 *
 * @return Whether the receiver and the specified set have at least one object
 *	   in common
 */
- (bool)intersectsSet: (OFSet OF_GENERIC(ObjectType) *)set;

/**
 * @brief Creates a new set which contains the objects which are in the
 *	  receiver, but not in the specified set.
 *
 * @param set The set whose objects will not be in the new set
 */
- (OFSet OF_GENERIC(ObjectType) *)setBySubtractingSet:
    (OFSet OF_GENERIC(ObjectType) *)set;

/**
 * @brief Creates a new set by creating the intersection of the receiver and
 *	  the specified set.
 *
 * @param set The set to intersect with
 */
- (OFSet OF_GENERIC(ObjectType) *)setByIntersectingWithSet:
    (OFSet OF_GENERIC(ObjectType) *)set;

/**
 * @brief Creates a new set by creating the union of the receiver and the
 *	  specified set.
 *
 * @param set The set to create the union with
 */
- (OFSet OF_GENERIC(ObjectType) *)setByAddingSet:
    (OFSet OF_GENERIC(ObjectType) *)set;

/**
 * @brief Checks whether the set contains an object equal to the specified
 *	  object.
 *
 * @param object The object which is checked for being in the set







>
>
>
>
>
>
>
>
>
>
>











|
<

<
<
|

|
<

















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






|







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
 * @brief Initializes an already allocated set with the specified objects.
 *
 * @param firstObject The first object for the set
 * @return An initialized set with the specified objects
 */
- (instancetype)initWithObjects: (ObjectType)firstObject, ... OF_SENTINEL;

/**
 * @brief Initializes an already allocated set with the specified object and
 *	  va_list.
 *
 * @param firstObject The first object for the set
 * @param arguments A va_list with the other objects
 * @return An initialized set with the specified object and va_list
 */
- (instancetype)initWithObject: (ObjectType)firstObject
		     arguments: (va_list)arguments;

/**
 * @brief Initializes an already allocated set with the specified objects.
 *
 * @param objects An array of objects for the set
 * @param count The number of objects in the specified array
 * @return An initialized set with the specified objects
 */
- (instancetype)initWithObjects: (ObjectType const _Nonnull *_Nonnull)objects
			  count: (size_t)count;

/**
 * @brief Returns an OFEnumerator to enumerate through all objects of the set.

 *


 * @return An OFEnumerator to enumerate through all objects of the set
 */
- (OFEnumerator OF_GENERIC(ObjectType) *)objectEnumerator;


/**
 * @brief Returns whether the receiver is a subset of the specified set.
 *
 * @return Whether the receiver is a subset of the specified set
 */
- (bool)isSubsetOfSet: (OFSet OF_GENERIC(ObjectType) *)set;

/**
 * @brief Returns whether the receiver and the specified set have at least one
 *	  object in common.
 *
 * @return Whether the receiver and the specified set have at least one object
 *	   in common
 */
- (bool)intersectsSet: (OFSet OF_GENERIC(ObjectType) *)set;



















/**
 * @brief Creates a new set by creating the union of the receiver and the
 *	  specified set.
 *
 * @param set The set to create the union with
 */
- (OFSet OF_GENERIC(ObjectType) *)setByAddingObjectsFromSet:
    (OFSet OF_GENERIC(ObjectType) *)set;

/**
 * @brief Checks whether the set contains an object equal to the specified
 *	  object.
 *
 * @param object The object which is checked for being in the set
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
 * @ref setValue:forKey: is called for each object.
 *
 * @note A @ref OFNull value is translated to nil!
 *
 * @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;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each object in the set.
 *
 * @param block The block to execute for each object in the set
 */
- (void)enumerateObjectsUsingBlock: (of_set_enumeration_block_t)block;

/**
 * @brief Creates a new set, only containing the objects for which the block
 *	  returns true.
 *
 * @param block A block which determines if the object should be in the new set
 * @return A new, autoreleased OFSet
 */
- (OFSet OF_GENERIC(ObjectType) *)filteredSetUsingBlock:
    (of_set_filter_block_t)block;
#endif
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END

#import "OFMutableSet.h"







|
<







|








|
|









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
 * @ref setValue:forKey: is called for each object.
 *
 * @note A @ref OFNull value is translated to nil!
 *
 * @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;


#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes a block for each object in the set.
 *
 * @param block The block to execute for each object in the set
 */
- (void)enumerateObjectsUsingBlock: (OFSetEnumerationBlock)block;

/**
 * @brief Creates a new set, only containing the objects for which the block
 *	  returns true.
 *
 * @param block A block which determines if the object should be in the new set
 * @return A new, autoreleased OFSet
 */
- (OFSet OF_GENERIC(ObjectType) *)
    filteredSetUsingBlock: (OFSetFilterBlock)block;
#endif
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END

#import "OFMutableSet.h"

Modified src/OFSet.m from [fd18f62182] to [c2dcb9f429].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
	ret = [[OFMapTableSet alloc] initWithObject: firstObject
					  arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	return (id)[[OFMapTableSet alloc] initWithObjects: objects
						    count: count];
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	return (id)[[OFMapTableSet alloc] initWithObject: firstObject
					       arguments: arguments];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{







|
<





|
<







56
57
58
59
60
61
62
63

64
65
66
67
68
69

70
71
72
73
74
75
76
	ret = [[OFMapTableSet alloc] initWithObject: firstObject
					  arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	return (id)[[OFMapTableSet alloc] initWithObjects: objects
						    count: count];
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	return (id)[[OFMapTableSet alloc] initWithObject: firstObject
					       arguments: arguments];
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
	ret = [[[self alloc] initWithObject: firstObject
				  arguments: arguments] autorelease];
	va_end(arguments);

	return ret;
}

+ (instancetype)setWithObjects: (id const *)objects
			 count: (size_t)count
{
	return [[[self alloc] initWithObjects: objects
					count: count] autorelease];
}

- (instancetype)init
{







|
<







136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
	ret = [[[self alloc] initWithObject: firstObject
				  arguments: arguments] autorelease];
	va_end(arguments);

	return ret;
}

+ (instancetype)setWithObjects: (id const *)objects count: (size_t)count

{
	return [[[self alloc] initWithObjects: objects
					count: count] autorelease];
}

- (instancetype)init
{
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
}

- (instancetype)initWithArray: (OFArray *)array
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObjects: (id)firstObject, ...
{
	id ret;
	va_list arguments;

	va_start(arguments, firstObject);
	ret = [self initWithObject: firstObject
			 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	OF_INVALID_INIT_METHOD







|
<










|
<





|
<







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
}

- (instancetype)initWithArray: (OFArray *)array
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObjects: (id)firstObject, ...
{
	id ret;
	va_list arguments;

	va_start(arguments, firstObject);
	ret = [self initWithObject: firstObject arguments: arguments];

	va_end(arguments);

	return ret;
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	OF_INVALID_INIT_METHOD
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
	}

	[ret makeImmutable];

	return ret;
}

- (void)setValue: (id)value
	  forKey: (OFString *)key
{
	for (id object in self)
		[object setValue: value
			  forKey: key];
}

- (bool)containsObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFEnumerator *)objectEnumerator
{
	OF_UNRECOGNIZED_SELECTOR
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	OFEnumerator *enumerator;
	int i;

	memcpy(&enumerator, state->extra, sizeof(enumerator));







|
<


|
<












|







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
	}

	[ret makeImmutable];

	return ret;
}

- (void)setValue: (id)value forKey: (OFString *)key

{
	for (id object in self)
		[object setValue: value forKey: key];

}

- (bool)containsObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFEnumerator *)objectEnumerator
{
	OF_UNRECOGNIZED_SELECTOR
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	OFEnumerator *enumerator;
	int i;

	memcpy(&enumerator, state->extra, sizeof(enumerator));
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
		[ret appendString: [object description]];

		if (++i < count)
			[ret appendString: @",\n"];

		objc_autoreleasePoolPop(pool2);
	}
	[ret replaceOccurrencesOfString: @"\n"
			     withString: @"\n\t"];
	[ret appendString: @"\n)}"];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}








|
<

<







321
322
323
324
325
326
327
328

329

330
331
332
333
334
335
336
		[ret appendString: [object description]];

		if (++i < count)
			[ret appendString: @",\n"];

		objc_autoreleasePoolPop(pool2);
	}
	[ret replaceOccurrencesOfString: @"\n" withString: @"\n\t"];

	[ret appendString: @"\n)}"];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

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
- (OFXMLElement *)XMLElementBySerializing
{
	void *pool = objc_autoreleasePoolPush();
	OFXMLElement *element;

	if ([self isKindOfClass: [OFMutableSet class]])
		element = [OFXMLElement elementWithName: @"OFMutableSet"
					      namespace: OF_SERIALIZATION_NS];
	else
		element = [OFXMLElement elementWithName: @"OFSet"
					      namespace: OF_SERIALIZATION_NS];

	for (id <OFSerialization> object in self) {
		void *pool2 = objc_autoreleasePoolPush();

		[element addChild: object.XMLElementBySerializing];

		objc_autoreleasePoolPop(pool2);
	}

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFSet *)setBySubtractingSet: (OFSet *)set
{
	OFMutableSet *new;

	new = [[self mutableCopy] autorelease];
	[new minusSet: set];

	[new makeImmutable];

	return new;
}

- (OFSet *)setByIntersectingWithSet: (OFSet *)set
{
	OFMutableSet *new;

	new = [[self mutableCopy] autorelease];
	[new intersectSet: set];

	[new makeImmutable];

	return new;
}

- (OFSet *)setByAddingSet: (OFSet *)set
{
	OFMutableSet *new;

	new = [[self mutableCopy] autorelease];
	[new unionSet: set];

	[new makeImmutable];

	return new;
}

- (OFArray *)allObjects
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *ret = [[[self objectEnumerator] allObjects] retain];
	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (id)anyObject
{
	void *pool = objc_autoreleasePoolPush();
	id ret = [[[self objectEnumerator] nextObject] retain];
	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (of_set_enumeration_block_t)block
{
	bool stop = false;

	for (id object in self) {
		block(object, &stop);

		if (stop)
			break;
	}
}

- (OFSet *)filteredSetUsingBlock: (of_set_filter_block_t)block
{
	OFMutableSet *ret = [OFMutableSet set];

	[self enumerateObjectsUsingBlock: ^ (id object, bool *stop) {
		if (block(object))
			[ret addObject: object];
	}];







|


|



<

<










<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

<
<
|

<

<








<








<




|











|







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
- (OFXMLElement *)XMLElementBySerializing
{
	void *pool = objc_autoreleasePoolPush();
	OFXMLElement *element;

	if ([self isKindOfClass: [OFMutableSet class]])
		element = [OFXMLElement elementWithName: @"OFMutableSet"
					      namespace: OFSerializationNS];
	else
		element = [OFXMLElement elementWithName: @"OFSet"
					      namespace: OFSerializationNS];

	for (id <OFSerialization> object in self) {
		void *pool2 = objc_autoreleasePoolPush();

		[element addChild: object.XMLElementBySerializing];

		objc_autoreleasePoolPop(pool2);
	}

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

























- (OFSet *)setByAddingObjectsFromSet: (OFSet *)set
{


	OFMutableSet *new = [[self mutableCopy] autorelease];
	[new unionSet: set];

	[new makeImmutable];

	return new;
}

- (OFArray *)allObjects
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *ret = [[[self objectEnumerator] allObjects] retain];
	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (id)anyObject
{
	void *pool = objc_autoreleasePoolPush();
	id ret = [[[self objectEnumerator] nextObject] retain];
	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateObjectsUsingBlock: (OFSetEnumerationBlock)block
{
	bool stop = false;

	for (id object in self) {
		block(object, &stop);

		if (stop)
			break;
	}
}

- (OFSet *)filteredSetUsingBlock: (OFSetFilterBlock)block
{
	OFMutableSet *ret = [OFMutableSet set];

	[self enumerateObjectsUsingBlock: ^ (id object, bool *stop) {
		if (block(object))
			[ret addObject: object];
	}];

Modified src/OFSettings.h from [a6f65cccf4] to [372d44cad4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

/**
 * @brief Sets the specified path to the specified string.
 *
 * @param string The string to set
 * @param path The path to store the string at
 */
- (void)setString: (OFString *)string
	  forPath: (OFString *)path;

/**
 * @brief Sets the specified path to the specified integer.
 *
 * @param integer The integer to set
 * @param path The path to store the integer at
 */
- (void)setInteger: (long long)integer
	   forPath: (OFString *)path;

/**
 * @brief Sets the specified path to the specified bool.
 *
 * @param bool_ The bool to set
 * @param path The path to store the bool at
 */
- (void)setBool: (bool)bool_
	forPath: (OFString *)path;

/**
 * @brief Sets the specified path to the specified float.
 *
 * @param float_ The float to set
 * @param path The path to store the float at
 */
- (void)setFloat: (float)float_
	 forPath: (OFString *)path;

/**
 * @brief Sets the specified path to the specified double.
 *
 * @param double_ The double to set
 * @param path The path to store the double at
 */
- (void)setDouble: (double)double_
	  forPath: (OFString *)path;

/**
 * @brief Sets the specified path to the specified array of strings.
 *
 * @param array The array of strings to set
 * @param path The path to store the array of strings at
 */
- (void)setArray: (OFArray OF_GENERIC(OFString *) *)array
	 forPath: (OFString *)path;

/**
 * @brief Returns the string for the specified path, or `nil` if the path does
 *	  not exist.
 *
 * @param path The path for which the string value should be returned
 * @return The string value of the specified path
 */
- (nullable OFString *)stringForPath: (OFString *)path;

/**
 * @brief Returns the string for the specified path, or the default value if
 *	  the path does not exist.
 *
 * @param path The path for which the string value should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The string value of the specified path
 */
- (nullable OFString *)stringForPath: (OFString *)path
			defaultValue: (nullable OFString *)defaultValue;

/**
 * @brief Returns the integer for the specified path, or the default value if
 *	  the path does not exist.
 *
 * @param path The path for which the integer value should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The integer value of the specified path
 */
- (long long)integerForPath: (OFString *)path
	       defaultValue: (long long)defaultValue;

/**
 * @brief Returns the bool for the specified path, or the default value if the
 *	  path does not exist.
 *
 * @param path The path for which the bool value should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The bool value of the specified path
 */
- (bool)boolForPath: (OFString *)path
       defaultValue: (bool)defaultValue;

/**
 * @brief Returns the float for the specified path, or the default value if the
 *	  path does not exist.
 *
 * @param path The path for which the float value should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The float value of the specified path
 */
- (float)floatForPath: (OFString *)path
	 defaultValue: (float)defaultValue;

/**
 * @brief Returns the double for the specified path, or the default value if
 *	  the path does not exist.
 *
 * @param path The path for which the double value should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The double value of the specified path
 */
- (double)doubleForPath: (OFString *)path
	   defaultValue: (double)defaultValue;

/**
 * @brief Returns the array of strings for the specified path, or an empty
 *	  array if the path does not exist.
 *
 * @param path The path for which the array of strings should be returned
 * @return The array of strings of the specified path
 */
- (OFArray OF_GENERIC(OFString *) *)arrayForPath: (OFString *)path;

/**
 * @brief Removes the value for the specified path.
 *
 * @param path The path for which the value should be removed
 */
- (void)removeValueForPath: (OFString *)path;







|
<


|

|
|

<
|







|
<







|
<







|
<





|

|
|





|
|







|

|





|


|

|

|
|





|

|

|
<





|

|

|
<





|

|

|
<






|

|







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

/**
 * @brief Sets the specified path to the specified string.
 *
 * @param string The string to set
 * @param path The path to store the string at
 */
- (void)setString: (OFString *)string forPath: (OFString *)path;


/**
 * @brief Sets the specified path to the specified long long.
 *
 * @param longLong The long long to set
 * @param path The path to store the long long at
 */

- (void)setLongLong: (long long)longLong forPath: (OFString *)path;

/**
 * @brief Sets the specified path to the specified bool.
 *
 * @param bool_ The bool to set
 * @param path The path to store the bool at
 */
- (void)setBool: (bool)bool_ forPath: (OFString *)path;


/**
 * @brief Sets the specified path to the specified float.
 *
 * @param float_ The float to set
 * @param path The path to store the float at
 */
- (void)setFloat: (float)float_ forPath: (OFString *)path;


/**
 * @brief Sets the specified path to the specified double.
 *
 * @param double_ The double to set
 * @param path The path to store the double at
 */
- (void)setDouble: (double)double_ forPath: (OFString *)path;


/**
 * @brief Sets the specified path to the specified array of strings.
 *
 * @param array The array of strings to set
 * @param path The path to store the array of string at
 */
- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array
	       forPath: (OFString *)path;

/**
 * @brief Returns the string for the specified path, or `nil` if the path does
 *	  not exist.
 *
 * @param path The path for which the string should be returned
 * @return The string of the specified path
 */
- (nullable OFString *)stringForPath: (OFString *)path;

/**
 * @brief Returns the string for the specified path, or the default value if
 *	  the path does not exist.
 *
 * @param path The path for which the string should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The string of the specified path
 */
- (nullable OFString *)stringForPath: (OFString *)path
			defaultValue: (nullable OFString *)defaultValue;

/**
 * @brief Returns the long long for the specified path, or the default value if
 *	  the path does not exist.
 *
 * @param path The path for which the long long should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The long long of the specified path
 */
- (long long)longLongForPath: (OFString *)path
		defaultValue: (long long)defaultValue;

/**
 * @brief Returns the bool for the specified path, or the default value if the
 *	  path does not exist.
 *
 * @param path The path for which the bool should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The bool of the specified path
 */
- (bool)boolForPath: (OFString *)path defaultValue: (bool)defaultValue;


/**
 * @brief Returns the float for the specified path, or the default value if the
 *	  path does not exist.
 *
 * @param path The path for which the float should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The float of the specified path
 */
- (float)floatForPath: (OFString *)path defaultValue: (float)defaultValue;


/**
 * @brief Returns the double for the specified path, or the default value if
 *	  the path does not exist.
 *
 * @param path The path for which the double should be returned
 * @param defaultValue The default value to return if the path does not exist
 * @return The double of the specified path
 */
- (double)doubleForPath: (OFString *)path defaultValue: (double)defaultValue;


/**
 * @brief Returns the array of strings for the specified path, or an empty
 *	  array if the path does not exist.
 *
 * @param path The path for which the array of strings should be returned
 * @return The array of string values of the specified path
 */
- (OFArray OF_GENERIC(OFString *) *)stringArrayForPath: (OFString *)path;

/**
 * @brief Removes the value for the specified path.
 *
 * @param path The path for which the value should be removed
 */
- (void)removeValueForPath: (OFString *)path;

Modified src/OFSettings.m from [5da0b0a891] to [206814d076].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
- (instancetype)initWithApplicationName: (OFString *)applicationName
{
	self = [super init];

	@try {
		_applicationName = [applicationName copy];
	} @catch (id e) {
		@throw e;
		[self release];

	}

	return self;
}

- (void)dealloc
{
	[_applicationName release];

	[super dealloc];
}

- (void)setString: (OFString *)string
	  forPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setInteger: (long long)integer
	   forPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setBool: (bool)bool_
	forPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setFloat: (float)float_
	 forPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setDouble: (double)double_
	  forPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setArray: (OFArray *)array
	 forPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFString *)stringForPath: (OFString *)path
{
	return [self stringForPath: path
		      defaultValue: nil];
}

- (OFString *)stringForPath: (OFString *)path
	       defaultValue: (OFString *)defaultValue
{
	OF_UNRECOGNIZED_SELECTOR
}

- (long long)integerForPath: (OFString *)path
	       defaultValue: (long long)defaultValue





{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)boolForPath: (OFString *)path
       defaultValue: (bool)defaultValue
{
	OF_UNRECOGNIZED_SELECTOR
}

- (float)floatForPath: (OFString *)path
	 defaultValue: (float)defaultValue
{
	OF_UNRECOGNIZED_SELECTOR
}

- (double)doubleForPath: (OFString *)path
	   defaultValue: (double)defaultValue
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFArray *)arrayForPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)removeValueForPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR







<

>












|
<




<
|




|
<




|
<




|
<




|
|






|
<








|
|
>
>
>
>
>




|
<




|
<




|
<
<
<
<
<
<







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
- (instancetype)initWithApplicationName: (OFString *)applicationName
{
	self = [super init];

	@try {
		_applicationName = [applicationName copy];
	} @catch (id e) {

		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_applicationName release];

	[super dealloc];
}

- (void)setString: (OFString *)string forPath: (OFString *)path

{
	OF_UNRECOGNIZED_SELECTOR
}


- (void)setLongLong: (long long)longLong forPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setBool: (bool)bool_ forPath: (OFString *)path

{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setFloat: (float)float_ forPath: (OFString *)path

{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setDouble: (double)double_ forPath: (OFString *)path

{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array
	       forPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFString *)stringForPath: (OFString *)path
{
	return [self stringForPath: path defaultValue: nil];

}

- (OFString *)stringForPath: (OFString *)path
	       defaultValue: (OFString *)defaultValue
{
	OF_UNRECOGNIZED_SELECTOR
}

- (long long)longLongForPath: (OFString *)path
		defaultValue: (long long)defaultValue
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)boolForPath: (OFString *)path defaultValue: (bool)defaultValue
{
	OF_UNRECOGNIZED_SELECTOR
}

- (float)floatForPath: (OFString *)path defaultValue: (float)defaultValue

{
	OF_UNRECOGNIZED_SELECTOR
}

- (double)doubleForPath: (OFString *)path defaultValue: (double)defaultValue

{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFArray OF_GENERIC(OFString *) *)stringArrayForPath: (OFString *)path






{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)removeValueForPath: (OFString *)path
{
	OF_UNRECOGNIZED_SELECTOR

Renamed and modified src/OFDimensionValue.h [76c9d8a875] to src/OFSizeValue.h [d063d8cdb8].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFValue.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFDimensionValue: OFValue
{
	of_dimension_t _dimension;
}


@end

OF_ASSUME_NONNULL_END

<
<
|

















|

|

>
>



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


 * 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 "OFValue.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFSizeValue: OFValue
{
	OFSize _size;
}

- (instancetype)initWithSize: (OFSize)size;
@end

OF_ASSUME_NONNULL_END

Renamed and modified src/OFDimensionValue.m [54ce362b98] to src/OFSizeValue.m [86214acf8b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFDimensionValue.h"
#import "OFMethodSignature.h"
#import "OFString.h"

#import "OFOutOfRangeException.h"

@implementation OFDimensionValue
@synthesize dimensionValue = _dimension;

- (instancetype)initWithDimension: (of_dimension_t)dimension
{
	self = [super init];

	_dimension = dimension;

	return self;
}

- (const char *)objCType
{
	return @encode(of_dimension_t);
}

- (void)getValue: (void *)value
	    size: (size_t)size
{
	if (size != sizeof(_dimension))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_dimension, sizeof(_dimension));
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFValue: of_dimension_t { %f, %f }>",
	    _dimension.width, _dimension.height];
}
@end

<
<
|













|





|
|

|



|






|


|
<

|


|





|
<


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


 * 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 "OFSizeValue.h"
#import "OFMethodSignature.h"
#import "OFString.h"

#import "OFOutOfRangeException.h"

@implementation OFSizeValue
@synthesize sizeValue = _size;

- (instancetype)initWithSize: (OFSize)size
{
	self = [super init];

	_size = size;

	return self;
}

- (const char *)objCType
{
	return @encode(OFSize);
}

- (void)getValue: (void *)value size: (size_t)size

{
	if (size != sizeof(_size))
		@throw [OFOutOfRangeException exception];

	memcpy(value, &_size, sizeof(_size));
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<OFValue: OFSize { %f, %f }>", _size.width, _size.height];

}
@end

Renamed and modified src/socket_helpers.h [76048c8293] to src/OFSocket+Private.h [00458f91c6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif

#include "socket.h"

#ifndef INVALID_SOCKET
# define INVALID_SOCKET -1
#endif

#ifndef INADDR_NONE
# define INADDR_NONE ((in_addr_t)-1)
#endif

#ifndef SOMAXCONN
/*







|
<
<
<
<







20
21
22
23
24
25
26
27




28
29
30
31
32
33
34
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif

#include "OFSocket.h"





#ifndef INADDR_NONE
# define INADDR_NONE ((in_addr_t)-1)
#endif

#ifndef SOMAXCONN
/*
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
#  include <proto/bsdsocket.h>
# endif
# include <sys/filio.h>
# define closesocket(sock) CloseSocket(sock)
# define ioctlsocket(fd, req, arg) IoctlSocket(fd, req, arg)
# define hstrerror(err) "unknown (no hstrerror)"
# define SOCKET_ERROR -1
# ifdef OF_HAVE_THREADS
#  define SocketBase ((struct Library *)of_tlskey_get(of_socket_base_key))
#  ifdef OF_AMIGAOS4
#   define ISocket \
	((struct SocketIFace *)of_tlskey_get(of_socket_interface_key))
#  endif
# endif
# ifdef OF_MORPHOS
typedef uint32_t in_addr_t;
# endif
#elif !defined(OF_WINDOWS) && !defined(OF_WII)
# define closesocket(sock) close(sock)
#endif

#ifdef OF_MORPHOS_IXEMUL
typedef uint32_t in_addr_t;
#endif

#ifdef OF_WII
# define accept(sock, addr, addrlen) net_accept(sock, addr, addrlen)
# define bind(sock, addr, addrlen) net_bind(sock, addr, addrlen)
# define closesocket(sock) net_close(sock)
# define connect(sock, addr, addrlen) \
    net_connect(sock, (struct sockaddr *)addr, addrlen)
# define fcntl(fd, cmd, flags) net_fcntl(fd, cmd, flags)







|
|

|
<









<
<
<
<







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
#  include <proto/bsdsocket.h>
# endif
# include <sys/filio.h>
# define closesocket(sock) CloseSocket(sock)
# define ioctlsocket(fd, req, arg) IoctlSocket(fd, req, arg)
# define hstrerror(err) "unknown (no hstrerror)"
# define SOCKET_ERROR -1
# if defined(OF_HAVE_THREADS) && !defined(OF_MORPHOS)
#  define SocketBase ((struct Library *)OFTLSKeyGet(OFSocketBaseKey))
#  ifdef OF_AMIGAOS4
#   define ISocket ((struct SocketIFace *)OFTLSKeyGet(OFSocketInterfaceKey))

#  endif
# endif
# ifdef OF_MORPHOS
typedef uint32_t in_addr_t;
# endif
#elif !defined(OF_WINDOWS) && !defined(OF_WII)
# define closesocket(sock) close(sock)
#endif





#ifdef OF_WII
# define accept(sock, addr, addrlen) net_accept(sock, addr, addrlen)
# define bind(sock, addr, addrlen) net_bind(sock, addr, addrlen)
# define closesocket(sock) net_close(sock)
# define connect(sock, addr, addrlen) \
    net_connect(sock, (struct sockaddr *)addr, addrlen)
# define fcntl(fd, cmd, flags) net_fcntl(fd, cmd, flags)

Renamed and modified src/socket.h [750c66c5d1] to src/OFSocket.h [fb1a5b55eb].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#ifndef OF_HAVE_SOCKETS
# error No sockets available!
#endif

#include <stdbool.h>

#import "OFString.h"




#ifdef OF_HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef OF_HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef OF_HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif
#ifdef OF_HAVE_NETINET_SCTP_H
# include <netinet/sctp.h>
#endif
#ifdef OF_HAVE_NETIPX_IPX_H
# include <netipx/ipx.h>
#endif

#include "platform.h"





#ifdef OF_WINDOWS
# include <windows.h>
# include <ws2tcpip.h>
# ifdef OF_HAVE_IPX
#  include <wsipx.h>
# endif
#endif

/** @file */

#ifdef OF_WII
# include <network.h>
#endif

#ifdef OF_PSP
# include <stdint.h>
#endif

#import "macros.h"
#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS)
# import "tlskey.h"
#endif

OF_ASSUME_NONNULL_BEGIN

#ifndef OF_WINDOWS
typedef int of_socket_t;

#else
typedef SOCKET of_socket_t;





#endif

#ifdef OF_WII
typedef u8 sa_family_t;
#endif

#ifdef OF_MORPHOS
typedef long socklen_t;
typedef u_char sa_family_t;
typedef u_short in_port_t;
#endif

#ifdef OF_MORPHOS_IXEMUL
typedef int socklen_t;
#endif

/**
 * @brief A socket address family.
 */
typedef enum {
	/** An unknown address family. */
	OF_SOCKET_ADDRESS_FAMILY_UNKNOWN,
	/** IPv4 */
	OF_SOCKET_ADDRESS_FAMILY_IPV4,
	/** IPv6 */
	OF_SOCKET_ADDRESS_FAMILY_IPV6,
	/** IPX */
	OF_SOCKET_ADDRESS_FAMILY_IPX,


	/** Any address family */
	OF_SOCKET_ADDRESS_FAMILY_ANY = 255
} of_socket_address_family_t;

#ifndef OF_HAVE_IPV6
struct sockaddr_in6 {
	sa_family_t sin6_family;
	in_port_t sin6_port;
	uint32_t sin6_flowinfo;
	struct in6_addr {







>
>
>










<
<
<



|
|
>
>
>
>




















<
<
<




|
>

|
>
>
>
>
>












<
<
<
<





|

|

|

|
>
>

|
|







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
#ifndef OF_HAVE_SOCKETS
# error No sockets available!
#endif

#include <stdbool.h>

#import "OFString.h"
#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS)
# import "OFTLSKey.h"
#endif

#ifdef OF_HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef OF_HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef OF_HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif



#ifdef OF_HAVE_NETIPX_IPX_H
# include <netipx/ipx.h>
#endif
#ifdef OF_HAVE_SYS_UN_H
# include <sys/un.h>
#endif
#ifdef OF_HAVE_AFUNIX_H
# include <afunix.h>
#endif

#ifdef OF_WINDOWS
# include <windows.h>
# include <ws2tcpip.h>
# ifdef OF_HAVE_IPX
#  include <wsipx.h>
# endif
#endif

/** @file */

#ifdef OF_WII
# include <network.h>
#endif

#ifdef OF_PSP
# include <stdint.h>
#endif

#import "macros.h"




OF_ASSUME_NONNULL_BEGIN

#ifndef OF_WINDOWS
typedef int OFSocketHandle;
static const OFSocketHandle OFInvalidSocketHandle = -1;
#else
typedef SOCKET OFSocketHandle;
static const OFSocketHandle OFInvalidSocketHandle = INVALID_SOCKET;
#endif

#ifdef OF_WINDOWS
typedef short sa_family_t;
#endif

#ifdef OF_WII
typedef u8 sa_family_t;
#endif

#ifdef OF_MORPHOS
typedef long socklen_t;
typedef u_char sa_family_t;
typedef u_short in_port_t;
#endif





/**
 * @brief A socket address family.
 */
typedef enum {
	/** An unknown address family. */
	OFSocketAddressFamilyUnknown,
	/** IPv4 */
	OFSocketAddressFamilyIPv4,
	/** IPv6 */
	OFSocketAddressFamilyIPv6,
	/** IPX */
	OFSocketAddressFamilyIPX,
	/** UNIX */
	OFSocketAddressFamilyUNIX,
	/** Any address family */
	OFSocketAddressFamilyAny = 255
} OFSocketAddressFamily;

#ifndef OF_HAVE_IPV6
struct sockaddr_in6 {
	sa_family_t sin6_family;
	in_port_t sin6_port;
	uint32_t sin6_flowinfo;
	struct in6_addr {
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
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
# define IPX_NODE_LEN 6
# define sipx_family sa_family
# define sipx_network sa_netnum
# define sipx_node sa_nodenum
# define sipx_port sa_socket
#endif








/**
 * @struct of_socket_address_t socket.h ObjFW/socket.h
 *
 * @brief A struct which represents a host / port pair for a socket.
 */
struct OF_BOXABLE of_socket_address_t {
	/*
	 * Even though struct sockaddr contains the family, we need to use our
	 * own family, as we need to support storing an IPv6 address on systems
	 * that don't support IPv6. These may not have AF_INET6 defined and we
	 * can't just define it, as the value is system-dependent and might
	 * clash with an existing value.
	 */
	of_socket_address_family_t family;
	union {
		struct sockaddr sockaddr;
		struct sockaddr_in in;
		struct sockaddr_in6 in6;
		struct sockaddr_ipx ipx;

	} sockaddr;
	socklen_t length;
};
typedef struct of_socket_address_t of_socket_address_t;

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Parses the specified IP and port into an of_socket_address_t.

 *
 * @param IP The IP to parse
 * @param port The port to use
 * @return The parsed IP and port as an of_socket_address_t
 */
extern of_socket_address_t of_socket_address_parse_ip(
    OFString *IP, uint16_t port);

/**
 * @brief Parses the specified IPv4 and port into an of_socket_address_t.
 *
 * @param IP The IPv4 to parse
 * @param port The port to use
 * @return The parsed IPv4 and port as an of_socket_address_t
 */
extern of_socket_address_t of_socket_address_parse_ipv4(
    OFString *IP, uint16_t port);

/**
 * @brief Parses the specified IPv6 and port into an of_socket_address_t.
 *
 * @param IP The IPv6 to parse
 * @param port The port to use
 * @return The parsed IPv6 and port as an of_socket_address_t
 */
extern of_socket_address_t of_socket_address_parse_ipv6(
    OFString *IP, uint16_t port);

/**
 * @brief Creates an IPX address for the specified network, node and port.
 *
 * @param node The node in the IPX network
 * @param network The IPX network
 * @param port The IPX port (sometimes called socket number) on the node

 */
extern of_socket_address_t of_socket_address_ipx(
    const unsigned char node[_Nonnull IPX_NODE_LEN], uint32_t network,
    uint16_t port);

/**








 * @brief Compares two of_socket_address_t for equality.
 *
 * @param address1 The address to compare with the second address
 * @param address2 The second address
 * @return Whether the two addresses are equal
 */
extern bool of_socket_address_equal(
    const of_socket_address_t *_Nonnull address1,
    const of_socket_address_t *_Nonnull address2);

/**
 * @brief Returns the hash for the specified of_socket_address_t.
 *
 * @param address The address to hash
 * @return The hash for the specified of_socket_address_t
 */
extern unsigned long of_socket_address_hash(
    const of_socket_address_t *_Nonnull address);

/**
 * @brief Converts the specified of_socket_address_t to an IP string and port.
 *
 * @param address The address to convert to a string
 * @param port A pointer to an uint16_t which should be set to the port of the
 *	       address or NULL if the port is not needed
 * @return The address as an IP string
 */
extern OFString *_Nonnull of_socket_address_ip_string(
    const of_socket_address_t *_Nonnull address, uint16_t *_Nullable port);

/**
 * @brief Sets the port of the specified of_socket_address_t, independent of
 *	  the address family used.
 *
 * @param address The address on which to set the port
 * @param port The port to set on the address
 */
extern void of_socket_address_set_port(of_socket_address_t *_Nonnull address,
    uint16_t port);

/**
 * @brief Returns the port of the specified of_socket_address_t, independent of
 *	  the address family used.
 *
 * @param address The address on which to get the port
 * @return The port of the address
 */
extern uint16_t of_socket_address_get_port(
    const of_socket_address_t *_Nonnull address);

/**
 * @brief Sets the IPX network of the specified of_socket_address_t.
 *
 * @param address The address on which to set the IPX network
 * @param network The IPX network to set on the address
 */
extern void of_socket_address_set_ipx_network(
    of_socket_address_t *_Nonnull address, uint32_t network);

/**
 * @brief Returns the IPX network of the specified of_socket_address_t.
 *
 * @param address The address on which to get the IPX network
 * @return The IPX network of the address
 */
extern uint32_t of_socket_address_get_ipx_network(
    const of_socket_address_t *_Nonnull address);

/**
 * @brief Sets the IPX node of the specified of_socket_address_t.
 *
 * @param address The address on which to set the IPX node
 * @param node The IPX node to set on the address
 */
extern void of_socket_address_set_ipx_node(
    of_socket_address_t *_Nonnull address,
    const unsigned char node[_Nonnull IPX_NODE_LEN]);

/**
 * @brief Gets the IPX node of the specified of_socket_address_t.
 *
 * @param address The address on which to get the IPX node
 * @param node A byte array to store the IPX node of the address
 */
extern void of_socket_address_get_ipx_node(
    const of_socket_address_t *_Nonnull address,
    unsigned char node[_Nonnull IPX_NODE_LEN]);










extern bool of_socket_init(void);
#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS)
extern void of_socket_deinit(void);
#endif
extern int of_socket_errno(void);
#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
extern int of_getsockname(of_socket_t sock, struct sockaddr *restrict addr,
    socklen_t *restrict addrLen);
#endif

#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS)
extern of_tlskey_t of_socket_base_key;
# ifdef OF_AMIGAOS4
extern of_tlskey_t of_socket_interface_key;
# endif
#endif
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







>
>
>
>
>
>
>

|



|







|





>


<
|





|
>



|

<
|


|



|

<
|


|



|

<
|


|




>

|




>
>
>
>
>
>
>
>
|





<
|
|


|


|

|
|


|


<
<


|
|


|





|



|





<
|


|




|
|


|




|
|


|




<
|



|




<
|


>
>
>
>
>
>
>
>
>
|
|
|

|

|



|
|

|







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
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
# define IPX_NODE_LEN 6
# define sipx_family sa_family
# define sipx_network sa_netnum
# define sipx_node sa_nodenum
# define sipx_port sa_socket
#endif

#if !defined(OF_HAVE_UNIX_SOCKETS) && !defined(OF_MORPHOS) && !defined(OF_MINT)
struct sockaddr_un {
	sa_family_t sun_family;
	char sun_path[108];
};
#endif

/**
 * @struct OFSocketAddress OFSocket.h ObjFW/OFSocket.h
 *
 * @brief A struct which represents a host / port pair for a socket.
 */
typedef struct OF_BOXABLE {
	/*
	 * Even though struct sockaddr contains the family, we need to use our
	 * own family, as we need to support storing an IPv6 address on systems
	 * that don't support IPv6. These may not have AF_INET6 defined and we
	 * can't just define it, as the value is system-dependent and might
	 * clash with an existing value.
	 */
	OFSocketAddressFamily family;
	union {
		struct sockaddr sockaddr;
		struct sockaddr_in in;
		struct sockaddr_in6 in6;
		struct sockaddr_ipx ipx;
		struct sockaddr_un un;
	} sockaddr;
	socklen_t length;

} OFSocketAddress;

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Parses the specified IP (either v4 or v6) and port into an
 *	  @ref OFSocketAddress.
 *
 * @param IP The IP to parse
 * @param port The port to use
 * @return The parsed IP and port as an OFSocketAddress
 */

extern OFSocketAddress OFSocketAddressParseIP(OFString *IP, uint16_t port);

/**
 * @brief Parses the specified IPv4 and port into an @ref OFSocketAddress.
 *
 * @param IP The IPv4 to parse
 * @param port The port to use
 * @return The parsed IPv4 and port as an OFSocketAddress
 */

extern OFSocketAddress OFSocketAddressParseIPv4(OFString *IP, uint16_t port);

/**
 * @brief Parses the specified IPv6 and port into an @ref OFSocketAddress.
 *
 * @param IP The IPv6 to parse
 * @param port The port to use
 * @return The parsed IPv6 and port as an OFSocketAddress
 */

extern OFSocketAddress OFSocketAddressParseIPv6(OFString *IP, uint16_t port);

/**
 * @brief Creates an IPX address for the specified node, network and port.
 *
 * @param node The node in the IPX network
 * @param network The IPX network
 * @param port The IPX port (sometimes called socket number) on the node
 * @return An IPX socket address with the specified node, network and port.
 */
extern OFSocketAddress OFSocketAddressMakeIPX(
    const unsigned char node[_Nonnull IPX_NODE_LEN], uint32_t network,
    uint16_t port);

/**
 * @brief Creates a UNIX socket address from the specified path.
 *
 * @param path The path of the UNIX socket
 * @return A UNIX socket address with the specified path
 */
extern OFSocketAddress OFSocketAddressMakeUNIX(OFString *path);

/**
 * @brief Compares two OFSocketAddress for equality.
 *
 * @param address1 The address to compare with the second address
 * @param address2 The second address
 * @return Whether the two addresses are equal
 */

extern bool OFSocketAddressEqual(const OFSocketAddress *_Nonnull address1,
    const OFSocketAddress *_Nonnull address2);

/**
 * @brief Returns the hash for the specified @ref OFSocketAddress.
 *
 * @param address The address to hash
 * @return The hash for the specified OFSocketAddress
 */
extern unsigned long OFSocketAddressHash(
    const OFSocketAddress *_Nonnull address);

/**
 * @brief Converts the specified @ref OFSocketAddress to a string.
 *
 * @param address The address to convert to a string


 * @return The address as an IP string
 */
extern OFString *_Nonnull OFSocketAddressString(
    const OFSocketAddress *_Nonnull address);

/**
 * @brief Sets the port of the specified @ref OFSocketAddress, independent of
 *	  the address family used.
 *
 * @param address The address on which to set the port
 * @param port The port to set on the address
 */
extern void OFSocketAddressSetPort(OFSocketAddress *_Nonnull address,
    uint16_t port);

/**
 * @brief Returns the port of the specified @ref OFSocketAddress, independent of
 *	  the address family used.
 *
 * @param address The address on which to get the port
 * @return The port of the address
 */

extern uint16_t OFSocketAddressPort(const OFSocketAddress *_Nonnull address);

/**
 * @brief Sets the IPX network of the specified @ref OFSocketAddress.
 *
 * @param address The address on which to set the IPX network
 * @param network The IPX network to set on the address
 */
extern void OFSocketAddressSetIPXNetwork(OFSocketAddress *_Nonnull address,
    uint32_t network);

/**
 * @brief Returns the IPX network of the specified @ref OFSocketAddress.
 *
 * @param address The address on which to get the IPX network
 * @return The IPX network of the address
 */
extern uint32_t OFSocketAddressIPXNetwork(
    const OFSocketAddress *_Nonnull address);

/**
 * @brief Sets the IPX node of the specified @ref OFSocketAddress.
 *
 * @param address The address on which to set the IPX node
 * @param node The IPX node to set on the address
 */

extern void OFSocketAddressSetIPXNode(OFSocketAddress *_Nonnull address,
    const unsigned char node[_Nonnull IPX_NODE_LEN]);

/**
 * @brief Gets the IPX node of the specified @ref OFSocketAddress.
 *
 * @param address The address on which to get the IPX node
 * @param node A byte array to store the IPX node of the address
 */

extern void OFSocketAddressIPXNode(const OFSocketAddress *_Nonnull address,
    unsigned char node[_Nonnull IPX_NODE_LEN]);

/**
 * @brief Gets the UNIX socket path of the specified @ref OFSocketAddress.
 *
 * @param address The address on which to get the UNIX socket path
 * @return The UNIX socket path
 */
extern OFString *_Nullable OFSocketAddressUNIXPath(
    const OFSocketAddress *_Nonnull address);

extern bool OFSocketInit(void);
#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
extern void OFSocketDeinit(void);
#endif
extern int OFSocketErrNo(void);
#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
extern int OFGetSockName(OFSocketHandle sock, struct sockaddr *restrict addr,
    socklen_t *restrict addrLen);
#endif

#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
extern OFTLSKey OFSocketBaseKey;
# ifdef OF_AMIGAOS4
extern OFTLSKey OFSocketInterfaceKey;
# endif
#endif
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Renamed and modified src/socket.m [2544d0730b] to src/OFSocket.m [8f11763ca5].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"






#ifdef OF_NINTENDO_3DS
# include <malloc.h>  /* For memalign() */
#endif

#include <errno.h>

#import "OFArray.h"
#import "OFCharacterSet.h"
#import "OFLocale.h"






#import "OFString.h"




#import "OFException.h"  /* For some E* -> WSAE* defines */
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFLockFailedException.h"

#import "OFUnlockFailedException.h"

#import "socket.h"
#import "socket_helpers.h"
#ifdef OF_HAVE_THREADS
# ifndef OF_AMIGAOS
#  import "mutex.h"
# else
#  import "tlskey.h"
# endif
#endif
#import "once.h"

#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif

#ifdef OF_NINTENDO_3DS
# include <3ds/types.h>
# include <3ds/services/soc.h>
#endif

#if defined(OF_HAVE_THREADS) && !defined(OF_AMIGAOS)


static of_mutex_t mutex;




#endif
#if !defined(OF_AMIGAOS) || !defined(OF_HAVE_THREADS)
static bool initSuccessful = false;
#else

# ifdef OF_HAVE_THREADS

of_tlskey_t of_socket_base_key;
#  ifdef OF_AMIGAOS4
of_tlskey_t of_socket_interface_key;
#  endif
# else
struct Library *SocketBase;
#  ifdef OF_AMIGAOS4
struct SocketIFace *ISocket = NULL;
#  endif
# endif
#endif

#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS)
OF_CONSTRUCTOR()
{
	if (!of_tlskey_new(&of_socket_base_key))
		@throw [OFInitializationFailedException exception];

# ifdef OF_AMIGAOS4
	if (!of_tlskey_new(&of_socket_interface_key))
		@throw [OFInitializationFailedException exception];
# endif
}
#endif

#if !defined(OF_AMIGAOS) || !defined(OF_HAVE_THREADS)
static void
init(void)
{
# if defined(OF_WINDOWS)
	WSADATA wsa;

	if (WSAStartup(MAKEWORD(2, 0), &wsa))

<
<
|















>
>
>
>
>









>
>
>
>
>
>

>
>
>






>


<
<
<
<
<
<
<
<
<
<
<









|
>
>
|
>
>
>
>

|

|
>
|
>
|

|









|


|



|





|







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


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

#ifndef _XOPEN_SOURCE_EXTENDED
# define _XOPEN_SOURCE_EXTENDED
#endif
#define _HPUX_ALT_XOPEN_SOCKET_API

#ifdef OF_NINTENDO_3DS
# include <malloc.h>  /* For memalign() */
#endif

#include <errno.h>

#import "OFArray.h"
#import "OFCharacterSet.h"
#import "OFLocale.h"
#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif
#import "OFOnce.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#ifdef OF_HAVE_THREADS
# import "OFTLSKey.h"
#endif

#import "OFException.h"  /* For some E* -> WSAE* defines */
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFLockFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFUnlockFailedException.h"












#ifdef OF_AMIGAOS
# include <proto/exec.h>
#endif

#ifdef OF_NINTENDO_3DS
# include <3ds/types.h>
# include <3ds/services/soc.h>
#endif

#if defined(OF_HAVE_THREADS) && (!defined(OF_AMIGAOS) || defined(OF_MORPHOS))
static OFMutex *mutex;

static void
releaseMutex(void)
{
	[mutex release];
}
#endif
#if !defined(OF_AMIGAOS) || defined(OF_MORPHOS) || !defined(OF_HAVE_THREADS)
static bool initSuccessful = false;
#endif

#ifdef OF_AMIGAOS
# if defined(OF_HAVE_THREADS) && !defined(OF_MORPHOS)
OFTLSKey OFSocketBaseKey;
#  ifdef OF_AMIGAOS4
OFTLSKey OFSocketInterfaceKey;
#  endif
# else
struct Library *SocketBase;
#  ifdef OF_AMIGAOS4
struct SocketIFace *ISocket = NULL;
#  endif
# endif
#endif

#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
OF_CONSTRUCTOR()
{
	if (OFTLSKeyNew(&OFSocketBaseKey) != 0)
		@throw [OFInitializationFailedException exception];

# ifdef OF_AMIGAOS4
	if (OFTLSKeyNew(&OFSocketInterfaceKey) != 0)
		@throw [OFInitializationFailedException exception];
# endif
}
#endif

#if !defined(OF_AMIGAOS) || defined(OF_MORPHOS) || !defined(OF_HAVE_THREADS)
static void
init(void)
{
# if defined(OF_WINDOWS)
	WSADATA wsa;

	if (WSAStartup(MAKEWORD(2, 0), &wsa))
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

	if (socInit(ctx, 0x100000) != 0)
		return;

	atexit((void (*)(void))socExit);
# endif

# if defined(OF_HAVE_THREADS) && !defined(OF_AMIGAOS)
	if (!of_mutex_new(&mutex))
		return;

#  ifdef OF_WII
	if (!of_spinlock_new(&spinlock))
		return;
#  endif
# endif

	initSuccessful = true;
}

# ifdef OF_AMIGAOS
OF_DESTRUCTOR()
{

#  ifdef OF_AMIGAOS4
	if (ISocket != NULL)
		DropInterface((struct Interface *)ISocket);
#  endif

	if (SocketBase != NULL)
		CloseLibrary(SocketBase);
}
# endif

#endif

bool
of_socket_init(void)
{
#if !defined(OF_AMIGAOS) || !defined(OF_HAVE_THREADS)
	static of_once_t onceControl = OF_ONCE_INIT;
	of_once(&onceControl, init);

	return initSuccessful;
#else
	struct Library *socketBase;
# ifdef OF_AMIGAOS4
	struct SocketIFace *socketInterface;
# endif

# ifdef OF_AMIGAOS4
	if ((socketInterface = of_tlskey_get(of_socket_interface_key)) != NULL)
# else
	if ((socketBase = of_tlskey_get(of_socket_base_key)) != NULL)
# endif
		return true;

	if ((socketBase = OpenLibrary("bsdsocket.library", 4)) == NULL)
		return false;

# ifdef OF_AMIGAOS4
	if ((socketInterface = (struct SocketIFace *)
	    GetInterface(socketBase, "main", 1, NULL)) == NULL) {
		CloseLibrary(socketBase);
		return false;
	}
# endif

	if (!of_tlskey_set(of_socket_base_key, socketBase)) {
		CloseLibrary(socketBase);
# ifdef OF_AMIGAOS4
		DropInterface((struct Interface *)socketInterface);
# endif
		return false;
	}

# ifdef OF_AMIGAOS4
	if (!of_tlskey_set(of_socket_interface_key, socketInterface)) {
		CloseLibrary(socketBase);
		DropInterface((struct Interface *)socketInterface);
		return false;
	}
# endif

	return true;
#endif
}

#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS)
void
of_socket_deinit(void)
{
	struct Library *socketBase = of_tlskey_get(of_socket_base_key);
# ifdef OF_AMIGAOS4
	struct SocketIFace *socketInterface =
	    of_tlskey_get(of_socket_interface_key);

	if (socketInterface != NULL)
		DropInterface((struct Interface *)socketInterface);
# endif
	if (socketBase != NULL)
		CloseLibrary(socketBase);
}
#endif

int
of_socket_errno()
{
#if defined(OF_WINDOWS)
	switch (WSAGetLastError()) {
	case WSAEACCES:
		return EACCES;
	case WSAEADDRINUSE:
		return EADDRINUSE;







|
|
|


|







<


>







<

>



|

|
|
|









|

|














|








|










|

|

|

|
<










|







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

	if (socInit(ctx, 0x100000) != 0)
		return;

	atexit((void (*)(void))socExit);
# endif

# if defined(OF_HAVE_THREADS) && (!defined(OF_AMIGAOS) || defined(OF_MORPHOS))
	mutex = [[OFMutex alloc] init];
	atexit(releaseMutex);

#  ifdef OF_WII
	if (OFSpinlockNew(&spinlock) != 0)
		return;
#  endif
# endif

	initSuccessful = true;
}


OF_DESTRUCTOR()
{
# ifdef OF_AMIGAOS
#  ifdef OF_AMIGAOS4
	if (ISocket != NULL)
		DropInterface((struct Interface *)ISocket);
#  endif

	if (SocketBase != NULL)
		CloseLibrary(SocketBase);

# endif
}
#endif

bool
OFSocketInit(void)
{
#if !defined(OF_AMIGAOS) || defined(OF_MORPHOS) || !defined(OF_HAVE_THREADS)
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, init);

	return initSuccessful;
#else
	struct Library *socketBase;
# ifdef OF_AMIGAOS4
	struct SocketIFace *socketInterface;
# endif

# ifdef OF_AMIGAOS4
	if ((socketInterface = OFTLSKeyGet(OFSocketInterfaceKey)) != NULL)
# else
	if ((socketBase = OFTLSKeyGet(OFSocketBaseKey)) != NULL)
# endif
		return true;

	if ((socketBase = OpenLibrary("bsdsocket.library", 4)) == NULL)
		return false;

# ifdef OF_AMIGAOS4
	if ((socketInterface = (struct SocketIFace *)
	    GetInterface(socketBase, "main", 1, NULL)) == NULL) {
		CloseLibrary(socketBase);
		return false;
	}
# endif

	if (OFTLSKeySet(OFSocketBaseKey, socketBase) != 0) {
		CloseLibrary(socketBase);
# ifdef OF_AMIGAOS4
		DropInterface((struct Interface *)socketInterface);
# endif
		return false;
	}

# ifdef OF_AMIGAOS4
	if (OFTLSKeySet(OFSocketInterfaceKey, socketInterface) != 0) {
		CloseLibrary(socketBase);
		DropInterface((struct Interface *)socketInterface);
		return false;
	}
# endif

	return true;
#endif
}

#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
void
OFSocketDeinit(void)
{
	struct Library *socketBase = OFTLSKeyGet(OFSocketBaseKey);
# ifdef OF_AMIGAOS4
	struct SocketIFace *socketInterface = OFTLSKeyGet(OFSocketInterfaceKey);


	if (socketInterface != NULL)
		DropInterface((struct Interface *)socketInterface);
# endif
	if (socketBase != NULL)
		CloseLibrary(socketBase);
}
#endif

int
OFSocketErrNo()
{
#if defined(OF_WINDOWS)
	switch (WSAGetLastError()) {
	case WSAEACCES:
		return EACCES;
	case WSAEADDRINUSE:
		return EADDRINUSE;
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
#else
	return errno;
#endif
}

#ifndef OF_WII
int
of_getsockname(of_socket_t sock, struct sockaddr *restrict addr,
    socklen_t *restrict addrLen)
{
	int ret;

# if defined(OF_HAVE_THREADS) && !defined(OF_AMIGAOS)
	if (!of_mutex_lock(&mutex))
		@throw [OFLockFailedException exception];

# endif

	ret = getsockname(sock, addr, addrLen);

# if defined(OF_HAVE_THREADS) && !defined(OF_AMIGAOS)
	if (!of_mutex_unlock(&mutex))
		@throw [OFUnlockFailedException exception];
# endif

	return ret;
}
#endif

of_socket_address_t
of_socket_address_parse_ipv4(OFString *IPv4, uint16_t port)
{
	void *pool = objc_autoreleasePoolPush();
	OFCharacterSet *whitespaceCharacterSet =
	    [OFCharacterSet whitespaceCharacterSet];
	of_socket_address_t ret;
	struct sockaddr_in *addrIn = &ret.sockaddr.in;
	OFArray OF_GENERIC(OFString *) *components;
	uint32_t addr;

	memset(&ret, '\0', sizeof(ret));
	ret.family = OF_SOCKET_ADDRESS_FAMILY_IPV4;
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
	ret.length = 8;
#else
	ret.length = sizeof(ret.sockaddr.in);
#endif

	addrIn->sin_family = AF_INET;
	addrIn->sin_port = OF_BSWAP16_IF_LE(port);
#ifdef OF_WII
	addrIn->sin_len = ret.length;
#endif

	components = [IPv4 componentsSeparatedByString: @"."];

	if (components.count != 4)
		@throw [OFInvalidFormatException exception];

	addr = 0;

	for (OFString *component in components) {
		unsigned long long number;

		if (component.length == 0)
			@throw [OFInvalidFormatException exception];

		if ([component indexOfCharacterFromSet:
		    whitespaceCharacterSet] != OF_NOT_FOUND)
			@throw [OFInvalidFormatException exception];

		number = component.unsignedLongLongValue;

		if (number > UINT8_MAX)
			@throw [OFInvalidFormatException exception];

		addr = (addr << 8) | ((uint32_t)number & 0xFF);
	}

	addrIn->sin_addr.s_addr = OF_BSWAP32_IF_LE(addr);

	objc_autoreleasePoolPop(pool);

	return ret;
}

static uint16_t
parseIPv6Component(OFString *component)
{
	unsigned long long number;

	if ([component indexOfCharacterFromSet:
	    [OFCharacterSet whitespaceCharacterSet]] != OF_NOT_FOUND)
		@throw [OFInvalidFormatException exception];

	number = [component unsignedLongLongValueWithBase: 16];

	if (number > UINT16_MAX)
		@throw [OFInvalidFormatException exception];

	return (uint16_t)number;
}

of_socket_address_t
of_socket_address_parse_ipv6(OFString *IPv6, uint16_t port)
{
	void *pool = objc_autoreleasePoolPush();
	of_socket_address_t ret;
	struct sockaddr_in6 *addrIn6 = &ret.sockaddr.in6;
	size_t doubleColon;

	memset(&ret, '\0', sizeof(ret));
	ret.family = OF_SOCKET_ADDRESS_FAMILY_IPV6;
	ret.length = sizeof(ret.sockaddr.in6);

#ifdef AF_INET6
	addrIn6->sin6_family = AF_INET6;
#else
	addrIn6->sin6_family = AF_UNSPEC;
#endif
	addrIn6->sin6_port = OF_BSWAP16_IF_LE(port);

	doubleColon = [IPv6 rangeOfString: @"::"].location;

	if (doubleColon != OF_NOT_FOUND) {
		OFString *left = [IPv6 substringToIndex: doubleColon];
		OFString *right = [IPv6 substringFromIndex: doubleColon + 2];
		OFArray OF_GENERIC(OFString *) *leftComponents;
		OFArray OF_GENERIC(OFString *) *rightComponents;
		size_t i;

		if ([right hasPrefix: @":"] || [right containsString: @"::"])







|




|
<
<
|

<

<
|
|
<






|
|




|





|







|


















|










|












|










|
|


|




|







|



|







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
#else
	return errno;
#endif
}

#ifndef OF_WII
int
OFGetSockName(OFSocketHandle sock, struct sockaddr *restrict addr,
    socklen_t *restrict addrLen)
{
	int ret;

# if defined(OF_HAVE_THREADS) && (!defined(OF_AMIGAOS) || defined(OF_MORPHOS))


	[mutex lock];
# endif

	ret = getsockname(sock, addr, addrLen);

# if defined(OF_HAVE_THREADS) && (!defined(OF_AMIGAOS) || defined(OF_MORPHOS))
	[mutex unlock];

# endif

	return ret;
}
#endif

OFSocketAddress
OFSocketAddressParseIPv4(OFString *IPv4, uint16_t port)
{
	void *pool = objc_autoreleasePoolPush();
	OFCharacterSet *whitespaceCharacterSet =
	    [OFCharacterSet whitespaceCharacterSet];
	OFSocketAddress ret;
	struct sockaddr_in *addrIn = &ret.sockaddr.in;
	OFArray OF_GENERIC(OFString *) *components;
	uint32_t addr;

	memset(&ret, '\0', sizeof(ret));
	ret.family = OFSocketAddressFamilyIPv4;
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
	ret.length = 8;
#else
	ret.length = sizeof(ret.sockaddr.in);
#endif

	addrIn->sin_family = AF_INET;
	addrIn->sin_port = OFToBigEndian16(port);
#ifdef OF_WII
	addrIn->sin_len = ret.length;
#endif

	components = [IPv4 componentsSeparatedByString: @"."];

	if (components.count != 4)
		@throw [OFInvalidFormatException exception];

	addr = 0;

	for (OFString *component in components) {
		unsigned long long number;

		if (component.length == 0)
			@throw [OFInvalidFormatException exception];

		if ([component indexOfCharacterFromSet:
		    whitespaceCharacterSet] != OFNotFound)
			@throw [OFInvalidFormatException exception];

		number = component.unsignedLongLongValue;

		if (number > UINT8_MAX)
			@throw [OFInvalidFormatException exception];

		addr = (addr << 8) | ((uint32_t)number & 0xFF);
	}

	addrIn->sin_addr.s_addr = OFToBigEndian32(addr);

	objc_autoreleasePoolPop(pool);

	return ret;
}

static uint16_t
parseIPv6Component(OFString *component)
{
	unsigned long long number;

	if ([component indexOfCharacterFromSet:
	    [OFCharacterSet whitespaceCharacterSet]] != OFNotFound)
		@throw [OFInvalidFormatException exception];

	number = [component unsignedLongLongValueWithBase: 16];

	if (number > UINT16_MAX)
		@throw [OFInvalidFormatException exception];

	return (uint16_t)number;
}

OFSocketAddress
OFSocketAddressParseIPv6(OFString *IPv6, uint16_t port)
{
	void *pool = objc_autoreleasePoolPush();
	OFSocketAddress ret;
	struct sockaddr_in6 *addrIn6 = &ret.sockaddr.in6;
	size_t doubleColon;

	memset(&ret, '\0', sizeof(ret));
	ret.family = OFSocketAddressFamilyIPv6;
	ret.length = sizeof(ret.sockaddr.in6);

#ifdef AF_INET6
	addrIn6->sin6_family = AF_INET6;
#else
	addrIn6->sin6_family = AF_UNSPEC;
#endif
	addrIn6->sin6_port = OFToBigEndian16(port);

	doubleColon = [IPv6 rangeOfString: @"::"].location;

	if (doubleColon != OFNotFound) {
		OFString *left = [IPv6 substringToIndex: doubleColon];
		OFString *right = [IPv6 substringFromIndex: doubleColon + 2];
		OFArray OF_GENERIC(OFString *) *leftComponents;
		OFArray OF_GENERIC(OFString *) *rightComponents;
		size_t i;

		if ([right hasPrefix: @":"] || [right containsString: @"::"])
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
	}

	objc_autoreleasePoolPop(pool);

	return ret;
}

of_socket_address_t
of_socket_address_parse_ip(OFString *IP, uint16_t port)
{
	of_socket_address_t ret;

	@try {
		ret = of_socket_address_parse_ipv6(IP, port);
	} @catch (OFInvalidFormatException *e) {
		ret = of_socket_address_parse_ipv4(IP, port);
	}

	return ret;
}

of_socket_address_t
of_socket_address_ipx(const unsigned char node[IPX_NODE_LEN], uint32_t network,
    uint16_t port)
{
	of_socket_address_t ret;

	memset(&ret, '\0', sizeof(ret));
	ret.family = OF_SOCKET_ADDRESS_FAMILY_IPX;
	ret.length = sizeof(ret.sockaddr.ipx);

#ifdef AF_IPX
	ret.sockaddr.ipx.sipx_family = AF_IPX;
#else
	ret.sockaddr.ipx.sipx_family = AF_UNSPEC;
#endif
	memcpy(ret.sockaddr.ipx.sipx_node, node, IPX_NODE_LEN);
	network = OF_BSWAP32_IF_LE(network);
	memcpy(&ret.sockaddr.ipx.sipx_network, &network,
	    sizeof(ret.sockaddr.ipx.sipx_network));
	ret.sockaddr.ipx.sipx_port = OF_BSWAP16_IF_LE(port);






























	return ret;
}

bool
of_socket_address_equal(const of_socket_address_t *address1,
    const of_socket_address_t *address2)
{
	const struct sockaddr_in *addrIn1, *addrIn2;
	const struct sockaddr_in6 *addrIn6_1, *addrIn6_2;
	const struct sockaddr_ipx *addrIPX1, *addrIPX2;




	if (address1->family != address2->family)
		return false;

	switch (address1->family) {
	case OF_SOCKET_ADDRESS_FAMILY_IPV4:
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
		if (address1->length < 8 || address2->length < 8)
			@throw [OFInvalidArgumentException exception];
#else
		if (address1->length < (socklen_t)sizeof(struct sockaddr_in) ||
		    address2->length < (socklen_t)sizeof(struct sockaddr_in))
			@throw [OFInvalidArgumentException exception];
#endif

		addrIn1 = &address1->sockaddr.in;
		addrIn2 = &address2->sockaddr.in;

		if (addrIn1->sin_port != addrIn2->sin_port)
			return false;
		if (addrIn1->sin_addr.s_addr != addrIn2->sin_addr.s_addr)
			return false;

		break;
	case OF_SOCKET_ADDRESS_FAMILY_IPV6:
		if (address1->length < (socklen_t)sizeof(struct sockaddr_in6) ||
		    address2->length < (socklen_t)sizeof(struct sockaddr_in6))
			@throw [OFInvalidArgumentException exception];

		addrIn6_1 = &address1->sockaddr.in6;
		addrIn6_2 = &address2->sockaddr.in6;

		if (addrIn6_1->sin6_port != addrIn6_2->sin6_port)
			return false;
		if (memcmp(addrIn6_1->sin6_addr.s6_addr,
		    addrIn6_2->sin6_addr.s6_addr,
		    sizeof(addrIn6_1->sin6_addr.s6_addr)) != 0)
			return false;

		break;
	case OF_SOCKET_ADDRESS_FAMILY_IPX:
		if (address1->length < (socklen_t)sizeof(struct sockaddr_ipx) ||
		    address2->length < (socklen_t)sizeof(struct sockaddr_ipx))
			@throw [OFInvalidArgumentException exception];

		addrIPX1 = &address1->sockaddr.ipx;
		addrIPX2 = &address2->sockaddr.ipx;

		if (addrIPX1->sipx_port != addrIPX2->sipx_port)
			return false;
		if (memcmp(&addrIPX1->sipx_network, &addrIPX2->sipx_network,
		    4) != 0)
			return false;
		if (memcmp(addrIPX1->sipx_node, addrIPX2->sipx_node,
		    IPX_NODE_LEN) != 0)
			return false;




		break;














	default:
		@throw [OFInvalidArgumentException exception];
	}

	return true;
}

unsigned long
of_socket_address_hash(const of_socket_address_t *address)
{
	uint32_t hash;

	OF_HASH_INIT(hash);
	OF_HASH_ADD(hash, address->family);

	switch (address->family) {
	case OF_SOCKET_ADDRESS_FAMILY_IPV4:
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
		if (address->length < 8)
			@throw [OFInvalidArgumentException exception];
#else
		if (address->length < (socklen_t)sizeof(struct sockaddr_in))
			@throw [OFInvalidArgumentException exception];
#endif

		OF_HASH_ADD(hash, address->sockaddr.in.sin_port >> 8);
		OF_HASH_ADD(hash, address->sockaddr.in.sin_port);
		OF_HASH_ADD(hash, address->sockaddr.in.sin_addr.s_addr >> 24);
		OF_HASH_ADD(hash, address->sockaddr.in.sin_addr.s_addr >> 16);
		OF_HASH_ADD(hash, address->sockaddr.in.sin_addr.s_addr >> 8);
		OF_HASH_ADD(hash, address->sockaddr.in.sin_addr.s_addr);

		break;
	case OF_SOCKET_ADDRESS_FAMILY_IPV6:
		if (address->length < (socklen_t)sizeof(struct sockaddr_in6))
			@throw [OFInvalidArgumentException exception];

		OF_HASH_ADD(hash, address->sockaddr.in6.sin6_port >> 8);
		OF_HASH_ADD(hash, address->sockaddr.in6.sin6_port);

		for (size_t i = 0;
		    i < sizeof(address->sockaddr.in6.sin6_addr.s6_addr); i++)
			OF_HASH_ADD(hash,
			    address->sockaddr.in6.sin6_addr.s6_addr[i]);

		break;
	case OF_SOCKET_ADDRESS_FAMILY_IPX:;
		unsigned char network[
		    sizeof(address->sockaddr.ipx.sipx_network)];

		if (address->length < (socklen_t)sizeof(struct sockaddr_ipx))
			@throw [OFInvalidArgumentException exception];

		OF_HASH_ADD(hash, address->sockaddr.ipx.sipx_port >> 8);
		OF_HASH_ADD(hash, address->sockaddr.ipx.sipx_port);

		memcpy(network, &address->sockaddr.ipx.sipx_network,
		    sizeof(network));

		for (size_t i = 0; i < sizeof(network); i++)
			OF_HASH_ADD(hash, network[i]);

		for (size_t i = 0; i < IPX_NODE_LEN; i++)
			OF_HASH_ADD(hash, address->sockaddr.ipx.sipx_node[i]);

		break;









	default:
		@throw [OFInvalidArgumentException exception];
	}

	OF_HASH_FINALIZE(hash);

	return hash;
}

static OFString *
IPv4String(const of_socket_address_t *address, uint16_t *port)
{
	const struct sockaddr_in *addrIn = &address->sockaddr.in;
	uint32_t addr = OF_BSWAP32_IF_LE(addrIn->sin_addr.s_addr);
	OFString *string;

	string = [OFString stringWithFormat: @"%u.%u.%u.%u",
	    (addr & 0xFF000000) >> 24, (addr & 0x00FF0000) >> 16,
	    (addr & 0x0000FF00) >>  8, addr & 0x000000FF];

	if (port != NULL)
		*port = OF_BSWAP16_IF_LE(addrIn->sin_port);

	return string;
}

static OFString *
IPv6String(const of_socket_address_t *address, uint16_t *port)
{
	OFMutableString *string = [OFMutableString string];
	const struct sockaddr_in6 *addrIn6 = &address->sockaddr.in6;
	int_fast8_t zerosStart = -1, maxZerosStart = -1;
	uint_fast8_t zerosCount = 0, maxZerosCount = 0;
	bool first = true;








|
|

|


|

|





|
|


|


|








|


|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|
|




>
>
>





|

















|
|














|
|
















>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>



<
<



|

|

|
|


|








|
|
|
|
|
|


|



|
|



|



|






|
|





|


|


>
>
>
>
>
>
>
>
>




|





|


|






<
<
<




|







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
	}

	objc_autoreleasePoolPop(pool);

	return ret;
}

OFSocketAddress
OFSocketAddressParseIP(OFString *IP, uint16_t port)
{
	OFSocketAddress ret;

	@try {
		ret = OFSocketAddressParseIPv6(IP, port);
	} @catch (OFInvalidFormatException *e) {
		ret = OFSocketAddressParseIPv4(IP, port);
	}

	return ret;
}

OFSocketAddress
OFSocketAddressMakeIPX(const unsigned char node[IPX_NODE_LEN], uint32_t network,
    uint16_t port)
{
	OFSocketAddress ret;

	memset(&ret, '\0', sizeof(ret));
	ret.family = OFSocketAddressFamilyIPX;
	ret.length = sizeof(ret.sockaddr.ipx);

#ifdef AF_IPX
	ret.sockaddr.ipx.sipx_family = AF_IPX;
#else
	ret.sockaddr.ipx.sipx_family = AF_UNSPEC;
#endif
	memcpy(ret.sockaddr.ipx.sipx_node, node, IPX_NODE_LEN);
	network = OFToBigEndian32(network);
	memcpy(&ret.sockaddr.ipx.sipx_network, &network,
	    sizeof(ret.sockaddr.ipx.sipx_network));
	ret.sockaddr.ipx.sipx_port = OFToBigEndian16(port);

	return ret;
}

OFSocketAddress
OFSocketAddressMakeUNIX(OFString *path)
{
	void *pool = objc_autoreleasePoolPush();
	OFStringEncoding encoding = [OFLocale encoding];
	size_t length = [path cStringLengthWithEncoding: encoding];
	OFSocketAddress ret;

	if (length > sizeof(ret.sockaddr.un.sun_path))
		@throw [OFOutOfRangeException exception];

	memset(&ret, '\0', sizeof(ret));
	ret.family = OFSocketAddressFamilyUNIX;
	ret.length = (socklen_t)
	    (offsetof(struct sockaddr_un, sun_path) + length);

#ifdef AF_UNIX
	ret.sockaddr.un.sun_family = AF_UNIX;
#else
	ret.sockaddr.un.sun_family = AF_UNSPEC;
#endif
	memcpy(ret.sockaddr.un.sun_path,
	    [path cStringWithEncoding: encoding], length);

	objc_autoreleasePoolPop(pool);

	return ret;
}

bool
OFSocketAddressEqual(const OFSocketAddress *address1,
    const OFSocketAddress *address2)
{
	const struct sockaddr_in *addrIn1, *addrIn2;
	const struct sockaddr_in6 *addrIn6_1, *addrIn6_2;
	const struct sockaddr_ipx *addrIPX1, *addrIPX2;
	void *pool;
	OFString *path1, *path2;
	bool ret;

	if (address1->family != address2->family)
		return false;

	switch (address1->family) {
	case OFSocketAddressFamilyIPv4:
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
		if (address1->length < 8 || address2->length < 8)
			@throw [OFInvalidArgumentException exception];
#else
		if (address1->length < (socklen_t)sizeof(struct sockaddr_in) ||
		    address2->length < (socklen_t)sizeof(struct sockaddr_in))
			@throw [OFInvalidArgumentException exception];
#endif

		addrIn1 = &address1->sockaddr.in;
		addrIn2 = &address2->sockaddr.in;

		if (addrIn1->sin_port != addrIn2->sin_port)
			return false;
		if (addrIn1->sin_addr.s_addr != addrIn2->sin_addr.s_addr)
			return false;

		return true;
	case OFSocketAddressFamilyIPv6:
		if (address1->length < (socklen_t)sizeof(struct sockaddr_in6) ||
		    address2->length < (socklen_t)sizeof(struct sockaddr_in6))
			@throw [OFInvalidArgumentException exception];

		addrIn6_1 = &address1->sockaddr.in6;
		addrIn6_2 = &address2->sockaddr.in6;

		if (addrIn6_1->sin6_port != addrIn6_2->sin6_port)
			return false;
		if (memcmp(addrIn6_1->sin6_addr.s6_addr,
		    addrIn6_2->sin6_addr.s6_addr,
		    sizeof(addrIn6_1->sin6_addr.s6_addr)) != 0)
			return false;

		return true;
	case OFSocketAddressFamilyIPX:
		if (address1->length < (socklen_t)sizeof(struct sockaddr_ipx) ||
		    address2->length < (socklen_t)sizeof(struct sockaddr_ipx))
			@throw [OFInvalidArgumentException exception];

		addrIPX1 = &address1->sockaddr.ipx;
		addrIPX2 = &address2->sockaddr.ipx;

		if (addrIPX1->sipx_port != addrIPX2->sipx_port)
			return false;
		if (memcmp(&addrIPX1->sipx_network, &addrIPX2->sipx_network,
		    4) != 0)
			return false;
		if (memcmp(addrIPX1->sipx_node, addrIPX2->sipx_node,
		    IPX_NODE_LEN) != 0)
			return false;

		return true;
	case OFSocketAddressFamilyUNIX:
		pool = objc_autoreleasePoolPush();

		path1 = OFSocketAddressUNIXPath(address1);
		path2 = OFSocketAddressUNIXPath(address2);

		if (path1 == nil || path2 == nil) {
			objc_autoreleasePoolPop(pool);

			return false;
		}

		ret = [path1 isEqual: path2];

		objc_autoreleasePoolPop(pool);

		return ret;
	default:
		@throw [OFInvalidArgumentException exception];
	}


}

unsigned long
OFSocketAddressHash(const OFSocketAddress *address)
{
	unsigned long hash;

	OFHashInit(&hash);
	OFHashAdd(&hash, address->family);

	switch (address->family) {
	case OFSocketAddressFamilyIPv4:
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
		if (address->length < 8)
			@throw [OFInvalidArgumentException exception];
#else
		if (address->length < (socklen_t)sizeof(struct sockaddr_in))
			@throw [OFInvalidArgumentException exception];
#endif

		OFHashAdd(&hash, address->sockaddr.in.sin_port >> 8);
		OFHashAdd(&hash, address->sockaddr.in.sin_port);
		OFHashAdd(&hash, address->sockaddr.in.sin_addr.s_addr >> 24);
		OFHashAdd(&hash, address->sockaddr.in.sin_addr.s_addr >> 16);
		OFHashAdd(&hash, address->sockaddr.in.sin_addr.s_addr >> 8);
		OFHashAdd(&hash, address->sockaddr.in.sin_addr.s_addr);

		break;
	case OFSocketAddressFamilyIPv6:
		if (address->length < (socklen_t)sizeof(struct sockaddr_in6))
			@throw [OFInvalidArgumentException exception];

		OFHashAdd(&hash, address->sockaddr.in6.sin6_port >> 8);
		OFHashAdd(&hash, address->sockaddr.in6.sin6_port);

		for (size_t i = 0;
		    i < sizeof(address->sockaddr.in6.sin6_addr.s6_addr); i++)
			OFHashAdd(&hash,
			    address->sockaddr.in6.sin6_addr.s6_addr[i]);

		break;
	case OFSocketAddressFamilyIPX:;
		unsigned char network[
		    sizeof(address->sockaddr.ipx.sipx_network)];

		if (address->length < (socklen_t)sizeof(struct sockaddr_ipx))
			@throw [OFInvalidArgumentException exception];

		OFHashAdd(&hash, address->sockaddr.ipx.sipx_port >> 8);
		OFHashAdd(&hash, address->sockaddr.ipx.sipx_port);

		memcpy(network, &address->sockaddr.ipx.sipx_network,
		    sizeof(network));

		for (size_t i = 0; i < sizeof(network); i++)
			OFHashAdd(&hash, network[i]);

		for (size_t i = 0; i < IPX_NODE_LEN; i++)
			OFHashAdd(&hash, address->sockaddr.ipx.sipx_node[i]);

		break;
	case OFSocketAddressFamilyUNIX:;
		void *pool = objc_autoreleasePoolPush();
		OFString *path = OFSocketAddressUNIXPath(address);

		hash = path.hash;

		objc_autoreleasePoolPop(pool);

		return hash;
	default:
		@throw [OFInvalidArgumentException exception];
	}

	OFHashFinalize(&hash);

	return hash;
}

static OFString *
IPv4String(const OFSocketAddress *address)
{
	const struct sockaddr_in *addrIn = &address->sockaddr.in;
	uint32_t addr = OFFromBigEndian32(addrIn->sin_addr.s_addr);
	OFString *string;

	string = [OFString stringWithFormat: @"%u.%u.%u.%u",
	    (addr & 0xFF000000) >> 24, (addr & 0x00FF0000) >> 16,
	    (addr & 0x0000FF00) >>  8, addr & 0x000000FF];




	return string;
}

static OFString *
IPv6String(const OFSocketAddress *address)
{
	OFMutableString *string = [OFMutableString string];
	const struct sockaddr_in6 *addrIn6 = &address->sockaddr.in6;
	int_fast8_t zerosStart = -1, maxZerosStart = -1;
	uint_fast8_t zerosCount = 0, maxZerosCount = 0;
	bool first = true;

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






















			    addrIn6->sin6_addr.s6_addr[i + 1]];
			first = false;
		}
	}

	[string makeImmutable];

	if (port != NULL)
		*port = OF_BSWAP16_IF_LE(addrIn6->sin6_port);

	return string;
}

OFString *
of_socket_address_ip_string(const of_socket_address_t *address, uint16_t *port)
{
	switch (address->family) {
	case OF_SOCKET_ADDRESS_FAMILY_IPV4:
		return IPv4String(address, port);
	case OF_SOCKET_ADDRESS_FAMILY_IPV6:
		return IPv6String(address, port);
	default:
		@throw [OFInvalidArgumentException exception];
	}
}

void
of_socket_address_set_port(of_socket_address_t *address, uint16_t port)
{
	switch (address->family) {
	case OF_SOCKET_ADDRESS_FAMILY_IPV4:
		address->sockaddr.in.sin_port = OF_BSWAP16_IF_LE(port);
		break;
	case OF_SOCKET_ADDRESS_FAMILY_IPV6:
		address->sockaddr.in6.sin6_port = OF_BSWAP16_IF_LE(port);
		break;
	case OF_SOCKET_ADDRESS_FAMILY_IPX:
		address->sockaddr.ipx.sipx_port = OF_BSWAP16_IF_LE(port);
		break;
	default:
		@throw [OFInvalidArgumentException exception];
	}
}

uint16_t
of_socket_address_get_port(const of_socket_address_t *address)
{
	switch (address->family) {
	case OF_SOCKET_ADDRESS_FAMILY_IPV4:
		return OF_BSWAP16_IF_LE(address->sockaddr.in.sin_port);
	case OF_SOCKET_ADDRESS_FAMILY_IPV6:
		return OF_BSWAP16_IF_LE(address->sockaddr.in6.sin6_port);
	case OF_SOCKET_ADDRESS_FAMILY_IPX:
		return OF_BSWAP16_IF_LE(address->sockaddr.ipx.sipx_port);
	default:
		@throw [OFInvalidArgumentException exception];
	}
}

void
of_socket_address_set_ipx_network(of_socket_address_t *address,
    uint32_t network)
{
	if (address->family != OF_SOCKET_ADDRESS_FAMILY_IPX)
		@throw [OFInvalidArgumentException exception];

	network = OF_BSWAP32_IF_LE(network);
	memcpy(&address->sockaddr.ipx.sipx_network, &network,
	    sizeof(address->sockaddr.ipx.sipx_network));
}

uint32_t
of_socket_address_get_ipx_network(const of_socket_address_t *address)
{
	uint32_t network;

	if (address->family != OF_SOCKET_ADDRESS_FAMILY_IPX)
		@throw [OFInvalidArgumentException exception];

	memcpy(&network, &address->sockaddr.ipx.sipx_network, sizeof(network));

	return OF_BSWAP32_IF_LE(network);
}

void
of_socket_address_set_ipx_node(of_socket_address_t *address,
    const unsigned char node[IPX_NODE_LEN])
{
	if (address->family != OF_SOCKET_ADDRESS_FAMILY_IPX)
		@throw [OFInvalidArgumentException exception];

	memcpy(address->sockaddr.ipx.sipx_node, node, IPX_NODE_LEN);
}

void
of_socket_address_get_ipx_node(const of_socket_address_t *address,
    unsigned char node[IPX_NODE_LEN])
{
	if (address->family != OF_SOCKET_ADDRESS_FAMILY_IPX)
		@throw [OFInvalidArgumentException exception];

	memcpy(node, address->sockaddr.ipx.sipx_node, IPX_NODE_LEN);
}





























<
<
<




|


|
|
|
|






|


|
|

|
|

|
|







|


|
|
|
|
|
|






<
|

|


|





|



|




|



|


|






|


|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
			    addrIn6->sin6_addr.s6_addr[i + 1]];
			first = false;
		}
	}

	[string makeImmutable];




	return string;
}

OFString *
OFSocketAddressString(const OFSocketAddress *address)
{
	switch (address->family) {
	case OFSocketAddressFamilyIPv4:
		return IPv4String(address);
	case OFSocketAddressFamilyIPv6:
		return IPv6String(address);
	default:
		@throw [OFInvalidArgumentException exception];
	}
}

void
OFSocketAddressSetPort(OFSocketAddress *address, uint16_t port)
{
	switch (address->family) {
	case OFSocketAddressFamilyIPv4:
		address->sockaddr.in.sin_port = OFToBigEndian16(port);
		break;
	case OFSocketAddressFamilyIPv6:
		address->sockaddr.in6.sin6_port = OFToBigEndian16(port);
		break;
	case OFSocketAddressFamilyIPX:
		address->sockaddr.ipx.sipx_port = OFToBigEndian16(port);
		break;
	default:
		@throw [OFInvalidArgumentException exception];
	}
}

uint16_t
OFSocketAddressPort(const OFSocketAddress *address)
{
	switch (address->family) {
	case OFSocketAddressFamilyIPv4:
		return OFFromBigEndian16(address->sockaddr.in.sin_port);
	case OFSocketAddressFamilyIPv6:
		return OFFromBigEndian16(address->sockaddr.in6.sin6_port);
	case OFSocketAddressFamilyIPX:
		return OFFromBigEndian16(address->sockaddr.ipx.sipx_port);
	default:
		@throw [OFInvalidArgumentException exception];
	}
}

void

OFSocketAddressSetIPXNetwork(OFSocketAddress *address, uint32_t network)
{
	if (address->family != OFSocketAddressFamilyIPX)
		@throw [OFInvalidArgumentException exception];

	network = OFToBigEndian32(network);
	memcpy(&address->sockaddr.ipx.sipx_network, &network,
	    sizeof(address->sockaddr.ipx.sipx_network));
}

uint32_t
OFSocketAddressIPXNetwork(const OFSocketAddress *address)
{
	uint32_t network;

	if (address->family != OFSocketAddressFamilyIPX)
		@throw [OFInvalidArgumentException exception];

	memcpy(&network, &address->sockaddr.ipx.sipx_network, sizeof(network));

	return OFFromBigEndian32(network);
}

void
OFSocketAddressSetIPXNode(OFSocketAddress *address,
    const unsigned char node[IPX_NODE_LEN])
{
	if (address->family != OFSocketAddressFamilyIPX)
		@throw [OFInvalidArgumentException exception];

	memcpy(address->sockaddr.ipx.sipx_node, node, IPX_NODE_LEN);
}

void
OFSocketAddressIPXNode(const OFSocketAddress *address,
    unsigned char node[IPX_NODE_LEN])
{
	if (address->family != OFSocketAddressFamilyIPX)
		@throw [OFInvalidArgumentException exception];

	memcpy(node, address->sockaddr.ipx.sipx_node, IPX_NODE_LEN);
}

OFString *
OFSocketAddressUNIXPath(const OFSocketAddress *_Nonnull address)
{
	socklen_t length;

	if (address->family != OFSocketAddressFamilyUNIX)
		@throw [OFInvalidArgumentException exception];

	length = address->length - offsetof(struct sockaddr_un, sun_path);

	for (socklen_t i = 0; i < length; i++)
		if (address->sockaddr.un.sun_path[i] == 0)
			length = i;

	if (length <= 0)
		return nil;

	return [OFString stringWithCString: address->sockaddr.un.sun_path
				  encoding: [OFLocale encoding]
				    length: length];
}

Modified src/OFSortedList.h from [cd3a196397] to [3a5eb28c9b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# define ObjectType id
#endif
{
	OF_RESERVE_IVARS(OFSortedList, 4)
}

- (of_list_object_t *)appendObject: (ObjectType)object OF_UNAVAILABLE;
- (of_list_object_t *)prependObject: (ObjectType)object OF_UNAVAILABLE;
- (of_list_object_t *)insertObject: (ObjectType)object
		  beforeListObject: (of_list_object_t *)listObject
    OF_UNAVAILABLE;
- (of_list_object_t *)insertObject: (ObjectType)object
		   afterListObject: (of_list_object_t *)listObject
    OF_UNAVAILABLE;

/**
 * @brief Inserts the object to the list while keeping the list sorted.
 *
 * @param object The object to insert
 * @return The list object for the object just added
 */
- (of_list_object_t *)insertObject: (ObjectType <OFComparing>)object;
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END







|
|
|
<
|
|
<
|







|






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
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# define ObjectType id
#endif
{
	OF_RESERVE_IVARS(OFSortedList, 4)
}

- (OFListItem)appendObject: (ObjectType)object OF_UNAVAILABLE;
- (OFListItem)prependObject: (ObjectType)object OF_UNAVAILABLE;
- (OFListItem)insertObject: (ObjectType)object

	    beforeListItem: (OFListItem)listItem OF_UNAVAILABLE;
- (OFListItem)insertObject: (ObjectType)object

	     afterListItem: (OFListItem)listItem OF_UNAVAILABLE;

/**
 * @brief Inserts the object to the list while keeping the list sorted.
 *
 * @param object The object to insert
 * @return The list object for the object just added
 */
- (OFListItem)insertObject: (ObjectType <OFComparing>)object;
#if !defined(OF_HAVE_GENERICS) && !defined(DOXYGEN)
# undef ObjectType
#endif
@end

OF_ASSUME_NONNULL_END

Modified src/OFSortedList.m from [3644e3b345] to [8d1674288f].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFSortedList.h"

@implementation OFSortedList
- (of_list_object_t *)appendObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

- (of_list_object_t *)prependObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

- (of_list_object_t *)insertObject: (id)object
		  beforeListObject: (of_list_object_t *)listObject
{
	OF_UNRECOGNIZED_SELECTOR
}

- (of_list_object_t *)insertObject: (id)object
		   afterListObject: (of_list_object_t *)listObject
{
	OF_UNRECOGNIZED_SELECTOR
}

- (of_list_object_t *)insertObject: (id <OFComparing>)object
{
	of_list_object_t *iter;

	for (iter = _lastListObject; iter != NULL; iter = iter->previous) {

		if ([object compare: iter->object] != OF_ORDERED_ASCENDING)

			return [super insertObject: object
				   afterListObject: iter];
	}

	return [super prependObject: object];
}
@end

<
<
|


















|




|




|
<




|
<




|

|

|
>
|
>
|
<





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


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

#import "OFSortedList.h"

@implementation OFSortedList
- (OFListItem)appendObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFListItem)prependObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFListItem)insertObject: (id)object beforeListItem: (OFListItem)listItem

{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFListItem)insertObject: (id)object afterListItem: (OFListItem)listItem

{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFListItem)insertObject: (id <OFComparing>)object
{
	OFListItem iter;

	for (iter = _lastListItem; iter != NULL;
	    iter = OFListItemPrevious(iter)) {
		if ([object compare: OFListItemObject(iter)] !=
		    OFOrderedAscending)
			return [super insertObject: object afterListItem: iter];

	}

	return [super prependObject: object];
}
@end

Modified src/OFStdIOStream+Private.h from [c5d65c7b40] to [689e88ea63].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFStdIOStream.h from [a6d2486cab] to [fc2f501857].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@class OFColor;

/**
 * @class OFStdIOStream OFStdIOStream.h ObjFW/OFStdIOStream.h
 *
 * @brief A class for providing standard input, output and error as OFStream.
 *
 * The global variables @ref of_stdin, @ref of_stdout and @ref of_stderr are
 * instances of this class and need no initialization.
 */
#ifdef OF_STDIO_STREAM_WIN32_CONSOLE_H
OF_SUBCLASSING_RESTRICTED
#endif
@interface OFStdIOStream: OFStream
#if !defined(OF_WINDOWS) && !defined(OF_AMIGAOS)







|







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

/**
 * @class OFStdIOStream OFStdIOStream.h ObjFW/OFStdIOStream.h
 *
 * @brief A class for providing standard input, output and error as OFStream.
 *
 * The global variables @ref OFStdIn, @ref OFStdOut and @ref OFStdErr are
 * instances of this class and need no initialization.
 */
#ifdef OF_STDIO_STREAM_WIN32_CONSOLE_H
OF_SUBCLASSING_RESTRICTED
#endif
@interface OFStdIOStream: OFStream
#if !defined(OF_WINDOWS) && !defined(OF_AMIGAOS)
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

/**
 * @brief Moves the cursor to the specified absolute position. Does nothing if
 *	  there is no underlying terminal.
 *
 * @param position The position to move the cursor to
 */
- (void)setCursorPosition: (of_point_t)position;

/**
 * @brief Moves the cursor to the specified relative position. Does nothing if
 *	  there is no underlying terminal.
 *
 * @param position The position to move the cursor to
 */
- (void)setRelativeCursorPosition: (of_point_t)position;
@end

#ifdef __cplusplus
extern "C" {
#endif
/** @file */

/**
 * @brief The standard input as an OFStream.
 */
extern OFStdIOStream *_Nullable of_stdin;

/**
 * @brief The standard output as an OFStream.
 */
extern OFStdIOStream *_Nullable of_stdout;

/**
 * @brief The standard error as an OFStream.
 */
extern OFStdIOStream *_Nullable of_stderr;

/**
 * @brief Log the specified printf-style format to @ref of_stderr.
 *
 * This prefixes the output with the date, timestamp, process name and PID and




 * allows `%@` as a printf-style formatted to print objects.







 */
extern void of_log(OFConstantString *format, ...);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|







|










|




|




|


|

|
>
>
>
>
|
>
>
>
>
>
>
>

|





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

/**
 * @brief Moves the cursor to the specified absolute position. Does nothing if
 *	  there is no underlying terminal.
 *
 * @param position The position to move the cursor to
 */
- (void)setCursorPosition: (OFPoint)position;

/**
 * @brief Moves the cursor to the specified relative position. Does nothing if
 *	  there is no underlying terminal.
 *
 * @param position The position to move the cursor to
 */
- (void)setRelativeCursorPosition: (OFPoint)position;
@end

#ifdef __cplusplus
extern "C" {
#endif
/** @file */

/**
 * @brief The standard input as an OFStream.
 */
extern OFStdIOStream *_Nullable OFStdIn;

/**
 * @brief The standard output as an OFStream.
 */
extern OFStdIOStream *_Nullable OFStdOut;

/**
 * @brief The standard error as an OFStream.
 */
extern OFStdIOStream *_Nullable OFStdErr;

/**
 * @brief Logs the specified printf-style format to @ref OFStdErr.
 *
 * This prefixes the output with the date, timestamp, process name and PID.
 *
 * @param format The format for the line to log. See @ref OFStream#writeFormat:.
 */
extern void OFLog(OFConstantString *format, ...);

/**
 * @brief Logs the specified printf-style format to @ref OFStdErr.
 *
 * This prefixes the output with the date, timestamp, process name and PID.
 *
 * @param format The format for the line to log. See @ref OFStream#writeFormat:.
 * @param arguments The arguments for the format
 */
extern void OFLogV(OFConstantString *format, va_list arguments);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFStdIOStream.m from [10bbd8e570] to [f006bf536d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
void
_reference_to_OFWin32ConsoleStdIOStream(void)
{
	[OFWin32ConsoleStdIOStream class];
}
#endif

OFStdIOStream *of_stdin = nil;
OFStdIOStream *of_stdout = nil;
OFStdIOStream *of_stderr = nil;

#ifdef OF_AMIGAOS
OF_DESTRUCTOR()
{
	[of_stdin dealloc];
	[of_stdout dealloc];
	[of_stderr dealloc];
}
#endif

void
of_log(OFConstantString *format, ...)










{
	void *pool = objc_autoreleasePoolPush();
	OFDate *date;
	OFString *dateString, *me, *msg;
	va_list arguments;

	date = [OFDate date];
	dateString = [date localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"];
#ifdef OF_HAVE_FILES
	me = [OFApplication programName].lastPathComponent;
#else
	me = [OFApplication programName];
#endif

	va_start(arguments, format);
	msg = [[[OFString alloc] initWithFormat: format
				      arguments: arguments] autorelease];
	va_end(arguments);

	[of_stderr writeFormat: @"[%@.%03d %@(%d)] %@\n", dateString,
				date.microsecond / 1000, me, getpid(), msg];

	objc_autoreleasePoolPop(pool);
}

#ifdef HAVE_ISATTY
static int
colorToANSI(OFColor *color)







|
|
|




|
|
|




|
>
>
>
>
>
>
>
>
>
>




<









<


<

|
|







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
void
_reference_to_OFWin32ConsoleStdIOStream(void)
{
	[OFWin32ConsoleStdIOStream class];
}
#endif

OFStdIOStream *OFStdIn = nil;
OFStdIOStream *OFStdOut = nil;
OFStdIOStream *OFStdErr = nil;

#ifdef OF_AMIGAOS
OF_DESTRUCTOR()
{
	[OFStdIn dealloc];
	[OFStdOut dealloc];
	[OFStdErr dealloc];
}
#endif

void
OFLog(OFConstantString *format, ...)
{
	va_list arguments;

	va_start(arguments, format);
	OFLogV(format, arguments);
	va_end(arguments);
}

void
OFLogV(OFConstantString *format, va_list arguments)
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *date;
	OFString *dateString, *me, *msg;


	date = [OFDate date];
	dateString = [date localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"];
#ifdef OF_HAVE_FILES
	me = [OFApplication programName].lastPathComponent;
#else
	me = [OFApplication programName];
#endif


	msg = [[[OFString alloc] initWithFormat: format
				      arguments: arguments] autorelease];


	[OFStdErr writeFormat: @"[%@.%03d %@(%d)] %@\n", dateString,
			       date.microsecond / 1000, me, getpid(), msg];

	objc_autoreleasePoolPop(pool);
}

#ifdef HAVE_ISATTY
static int
colorToANSI(OFColor *color)
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
	if (self != [OFStdIOStream class])
		return;

# ifndef OF_AMIGAOS
	int fd;

	if ((fd = fileno(stdin)) >= 0)
		of_stdin = [[OFStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
	if ((fd = fileno(stdout)) >= 0)
		of_stdout = [[OFStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
	if ((fd = fileno(stderr)) >= 0)
		of_stderr = [[OFStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
# else
	BPTR input, output, error;
	bool inputClosable = false, outputClosable = false,
	    errorClosable = false;

	input = Input();







<
|

|


|







156
157
158
159
160
161
162

163
164
165
166
167
168
169
170
171
172
173
174
175
	if (self != [OFStdIOStream class])
		return;

# ifndef OF_AMIGAOS
	int fd;

	if ((fd = fileno(stdin)) >= 0)

		OFStdIn = [[OFStdIOStream alloc] of_initWithFileDescriptor: fd];
	if ((fd = fileno(stdout)) >= 0)
		OFStdOut = [[OFStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
	if ((fd = fileno(stderr)) >= 0)
		OFStdErr = [[OFStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
# else
	BPTR input, output, error;
	bool inputClosable = false, outputClosable = false,
	    errorClosable = false;

	input = Input();
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
	}

	if (error == 0) {
		error = Open("*", MODE_OLDFILE);
		errorClosable = true;
	}

	of_stdin = [[OFStdIOStream alloc] of_initWithHandle: input
						   closable: inputClosable];
	of_stdout = [[OFStdIOStream alloc] of_initWithHandle: output
						    closable: outputClosable];
	of_stderr = [[OFStdIOStream alloc] of_initWithHandle: error
						    closable: errorClosable];
# endif
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

#ifndef OF_AMIGAOS
- (instancetype)of_initWithFileDescriptor: (int)fd
{
	self = [super init];

	_fd = fd;

	return self;
}
#else
- (instancetype)of_initWithHandle: (BPTR)handle
			 closable: (bool)closable
{
	self = [super init];

	_handle = handle;
	_closable = closable;

	return self;







|
|
|
|
|
|



















|
<







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
	}

	if (error == 0) {
		error = Open("*", MODE_OLDFILE);
		errorClosable = true;
	}

	OFStdIn = [[OFStdIOStream alloc] of_initWithHandle: input
						  closable: inputClosable];
	OFStdOut = [[OFStdIOStream alloc] of_initWithHandle: output
						   closable: outputClosable];
	OFStdErr = [[OFStdIOStream alloc] of_initWithHandle: error
						   closable: errorClosable];
# endif
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

#ifndef OF_AMIGAOS
- (instancetype)of_initWithFileDescriptor: (int)fd
{
	self = [super init];

	_fd = fd;

	return self;
}
#else
- (instancetype)of_initWithHandle: (BPTR)handle closable: (bool)closable

{
	self = [super init];

	_handle = handle;
	_closable = closable;

	return self;
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
	if (_handle == 0)
#endif
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	ssize_t ret;

#ifndef OF_AMIGAOS
	if (_fd == -1)
		@throw [OFNotOpenException exceptionWithObject: self];








|
<







247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
	if (_handle == 0)
#endif
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	ssize_t ret;

#ifndef OF_AMIGAOS
	if (_fd == -1)
		@throw [OFNotOpenException exceptionWithObject: self];

286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
#ifndef OF_AMIGAOS
	if (_fd == -1)
		@throw [OFNotOpenException exceptionWithObject: self];

# ifndef OF_WINDOWS
	ssize_t bytesWritten;







|
<







288
289
290
291
292
293
294
295

296
297
298
299
300
301
302

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{
#ifndef OF_AMIGAOS
	if (_fd == -1)
		@throw [OFNotOpenException exceptionWithObject: self];

# ifndef OF_WINDOWS
	ssize_t bytesWritten;
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

- (void)release
{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

- (bool)hasTerminal
{
#ifdef HAVE_ISATTY
	return isatty(_fd);
#else







|







385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

- (bool)hasTerminal
{
#ifdef HAVE_ISATTY
	return isatty(_fd);
#else
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
	if (!isatty(_fd))
		return;

	[self writeFormat: @"\033[%uG", column + 1];
#endif
}

- (void)setCursorPosition: (of_point_t)position
{
	if (position.x < 0 || position.y < 0)
		@throw [OFInvalidArgumentException exception];

#ifdef HAVE_ISATTY
	if (!isatty(_fd))
		return;

	[self writeFormat: @"\033[%u;%uH",
			   (unsigned)position.y + 1, (unsigned)position.x + 1];
#endif
}

- (void)setRelativeCursorPosition: (of_point_t)position
{
#ifdef HAVE_ISATTY
	if (!isatty(_fd))
		return;

	if (position.x > 0)
		[self writeFormat: @"\033[%uC", (unsigned)position.x];







|













|







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
	if (!isatty(_fd))
		return;

	[self writeFormat: @"\033[%uG", column + 1];
#endif
}

- (void)setCursorPosition: (OFPoint)position
{
	if (position.x < 0 || position.y < 0)
		@throw [OFInvalidArgumentException exception];

#ifdef HAVE_ISATTY
	if (!isatty(_fd))
		return;

	[self writeFormat: @"\033[%u;%uH",
			   (unsigned)position.y + 1, (unsigned)position.x + 1];
#endif
}

- (void)setRelativeCursorPosition: (OFPoint)position
{
#ifdef HAVE_ISATTY
	if (!isatty(_fd))
		return;

	if (position.x > 0)
		[self writeFormat: @"\033[%uC", (unsigned)position.x];

Renamed and modified src/of_strptime.h [569fe52fbb] to src/OFStrPTime.h [0432d594e4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#import "macros.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __cplusplus
extern "C" {
#endif
extern const char *of_strptime(const char *buf, const char *fmt, struct tm *tm,
    short *tz);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|
|





25
26
27
28
29
30
31
32
33
34
35
36
37
38
#import "macros.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __cplusplus
extern "C" {
#endif
extern const char *_Nullable OFStrPTime(const char *buffer, const char *format,
    struct tm *tm, short *_Nullable tz);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Renamed and modified src/of_strptime.m [ef61304e79] to src/OFStrPTime.m [7afa230e7d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#include <string.h>

#include <time.h>

#import "macros.h"

const char *
of_strptime(const char *buffer, const char *format, struct tm *tm, short *tz)
{
	enum {
		SEARCH_CONVERSION_SPECIFIER,
		IN_CONVERSION_SPECIFIER
	} state = SEARCH_CONVERSION_SPECIFIER;
	size_t j, bufferLen, formatLen;

	bufferLen = strlen(buffer);
	formatLen = strlen(format);

	j = 0;
	for (size_t i = 0; i < formatLen; i++) {
		if (j >= bufferLen)
			return NULL;

		switch (state) {
		case SEARCH_CONVERSION_SPECIFIER:
			if (format[i] == '%')
				state = IN_CONVERSION_SPECIFIER;
			else if (format[i] != buffer[j++])
				return NULL;

			break;

		case IN_CONVERSION_SPECIFIER:;
			int k, maxLen, number = 0;

			switch (format[i]) {
			case 'd':
			case 'e':
			case 'H':
			case 'm':







|


|
|
|











|

|




|
<







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
#include <string.h>

#include <time.h>

#import "macros.h"

const char *
OFStrPTime(const char *buffer, const char *format, struct tm *tm, short *tz)
{
	enum {
		stateSearchConversionSpecifier,
		stateInConversionSpecifier
	} state = stateSearchConversionSpecifier;
	size_t j, bufferLen, formatLen;

	bufferLen = strlen(buffer);
	formatLen = strlen(format);

	j = 0;
	for (size_t i = 0; i < formatLen; i++) {
		if (j >= bufferLen)
			return NULL;

		switch (state) {
		case stateSearchConversionSpecifier:
			if (format[i] == '%')
				state = stateInConversionSpecifier;
			else if (format[i] != buffer[j++])
				return NULL;

			break;
		case stateInConversionSpecifier:;

			int k, maxLen, number = 0;

			switch (format[i]) {
			case 'd':
			case 'e':
			case 'H':
			case 'm':
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
				break;
			case 't':
				if (buffer[j++] != '\t')
					return NULL;
				break;
			}

			state = SEARCH_CONVERSION_SPECIFIER;

			break;
		}
	}

	return buffer + j;
}







|







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
				break;
			case 't':
				if (buffer[j++] != '\t')
					return NULL;
				break;
			}

			state = stateSearchConversionSpecifier;

			break;
		}
	}

	return buffer + j;
}

Modified src/OFStream+Private.h from [55a5b86424] to [571ecdcdc3].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFStream.h from [c1d35377bc] to [2b820d15bb].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  stream.
 *
 * @param length The length of the data that has been read
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next read
 */
typedef bool (^of_stream_async_read_block_t)(size_t length,
    id _Nullable exception);

/**
 * @brief A block which is called when a line was read asynchronously from a
 *	  stream.
 *
 * @param line The line which has been read or `nil` when the end of stream
 *	       occurred
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next read
 */
typedef bool (^of_stream_async_read_line_block_t)(OFString *_Nullable line,
    id _Nullable exception);

/**
 * @brief A block which is called when data was written asynchronously to a
 *	  stream.
 *
 * @param data The data which was written to the stream
 * @param bytesWritten The number of bytes which have been written. This
 *		       matches the length of the specified data on the
 *		       asynchronous write if no exception was encountered.
 * @param exception An exception which occurred while writing or `nil` on
 *		    success
 * @return The data to repeat the write with or nil if it should not repeat
 */
typedef OFData *_Nullable (^of_stream_async_write_data_block_t)(
    OFData *_Nonnull data, size_t bytesWritten, id _Nullable exception);

/**
 * @brief A block which is called when a string was written asynchronously to a
 *	  stream.
 *
 * @param string The string which was written to the stream
 * @param bytesWritten The number of bytes which have been written. This
 *		       matches the length of the specified data on the
 *		       asynchronous write if no exception was encountered.
 * @param exception An exception which occurred while writing or `nil` on
 *		    success
 * @return The string to repeat the write with or nil if it should not repeat
 */
typedef OFString *_Nullable (^of_stream_async_write_string_block_t)(
    OFString *_Nonnull string, size_t bytesWritten, id _Nullable exception);
#endif

/**
 * @protocol OFStreamDelegate OFStream.h ObjFW/OFStream.h
 *
 * A delegate for OFStream.
 */







<
|











|






<







|
|





<







|
|







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
 *	  stream.
 *
 * @param length The length of the data that has been read
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next read
 */

typedef bool (^OFStreamAsyncReadBlock)(size_t length, id _Nullable exception);

/**
 * @brief A block which is called when a line was read asynchronously from a
 *	  stream.
 *
 * @param line The line which has been read or `nil` when the end of stream
 *	       occurred
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next read
 */
typedef bool (^OFStreamAsyncReadLineBlock)(OFString *_Nullable line,
    id _Nullable exception);

/**
 * @brief A block which is called when data was written asynchronously to a
 *	  stream.
 *

 * @param bytesWritten The number of bytes which have been written. This
 *		       matches the length of the specified data on the
 *		       asynchronous write if no exception was encountered.
 * @param exception An exception which occurred while writing or `nil` on
 *		    success
 * @return The data to repeat the write with or nil if it should not repeat
 */
typedef OFData *_Nullable (^OFStreamAsyncWriteDataBlock)(size_t bytesWritten,
    id _Nullable exception);

/**
 * @brief A block which is called when a string was written asynchronously to a
 *	  stream.
 *

 * @param bytesWritten The number of bytes which have been written. This
 *		       matches the length of the specified data on the
 *		       asynchronous write if no exception was encountered.
 * @param exception An exception which occurred while writing or `nil` on
 *		    success
 * @return The string to repeat the write with or nil if it should not repeat
 */
typedef OFString *_Nullable (^OFStreamAsyncWriteStringBlock)(
    size_t bytesWritten, id _Nullable exception);
#endif

/**
 * @protocol OFStreamDelegate OFStream.h ObjFW/OFStream.h
 *
 * A delegate for OFStream.
 */
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
 *		       matches the length of the specified data on the
 *		       asynchronous write if no exception was encountered.
 * @param exception An exception that occurred while writing, or nil on success
 * @return The string to repeat the write with or nil if it should not repeat
 */
- (nullable OFString *)stream: (OFStream *)stream
	       didWriteString: (OFString *)string
		     encoding: (of_string_encoding_t)encoding
		 bytesWritten: (size_t)bytesWritten
		    exception: (nullable id)exception;
@end

/**
 * @class OFStream OFStream.h ObjFW/OFStream.h
 *







|







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
 *		       matches the length of the specified data on the
 *		       asynchronous write if no exception was encountered.
 * @param exception An exception that occurred while writing, or nil on success
 * @return The string to repeat the write with or nil if it should not repeat
 */
- (nullable OFString *)stream: (OFStream *)stream
	       didWriteString: (OFString *)string
		     encoding: (OFStringEncoding)encoding
		 bytesWritten: (size_t)bytesWritten
		    exception: (nullable id)exception;
@end

/**
 * @class OFStream OFStream.h ObjFW/OFStream.h
 *
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
 * @brief Whether the end of the stream has been reached.
 */
@property (readonly, nonatomic, getter=isAtEndOfStream) bool atEndOfStream;

/**
 * @brief Whether writes are buffered.
 */
@property (nonatomic, nonatomic) bool buffersWrites;

/**
 * @brief Whether data is present in the internal read buffer.
 */
@property (readonly, nonatomic) bool hasDataInReadBuffer;

/**







|







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
 * @brief Whether the end of the stream has been reached.
 */
@property (readonly, nonatomic, getter=isAtEndOfStream) bool atEndOfStream;

/**
 * @brief Whether writes are buffered.
 */
@property (nonatomic) bool buffersWrites;

/**
 * @brief Whether data is present in the internal read buffer.
 */
@property (readonly, nonatomic) bool hasDataInReadBuffer;

/**
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
 * result of 0 bytes.
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @return The number of bytes read
 */
- (size_t)readIntoBuffer: (void *)buffer
		  length: (size_t)length;

/**
 * @brief Reads exactly the specified length bytes from the stream into a
 *	  buffer.
 *
 * Unlike @ref readIntoBuffer:length:, this method does not return when less
 * than the specified length has been read - instead, it waits until it got
 * exactly the specified length.
 *
 * @warning Only call this when you know that specified amount of data is
 *	    available! Otherwise you will get an exception!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 */
 - (void)readIntoBuffer: (void *)buffer
	    exactLength: (size_t)length;

#ifdef OF_HAVE_SOCKETS
/**
 * @brief Asynchronously reads *at most* size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.







|
<
















|
<







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
 * result of 0 bytes.
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @return The number of bytes read
 */
- (size_t)readIntoBuffer: (void *)buffer length: (size_t)length;


/**
 * @brief Reads exactly the specified length bytes from the stream into a
 *	  buffer.
 *
 * Unlike @ref readIntoBuffer:length:, this method does not return when less
 * than the specified length has been read - instead, it waits until it got
 * exactly the specified length.
 *
 * @warning Only call this when you know that specified amount of data is
 *	    available! Otherwise you will get an exception!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 */
 - (void)readIntoBuffer: (void *)buffer exactLength: (size_t)length;


#ifdef OF_HAVE_SOCKETS
/**
 * @brief Asynchronously reads *at most* size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read.
 *		 The buffer must not be freed before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length;

/**
 * @brief Asynchronously reads *at most* size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use







|
<







281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read.
 *		 The buffer must not be freed before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 */
- (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length;


/**
 * @brief Asynchronously reads *at most* size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use
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
 *		 The buffer must not be freed before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @param runLoopMode The run loop mode in which to perform the async read
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode;

/**
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:, this method does not call the
 * method when less than the specified length has been read - instead, it waits
 * until it got exactly the specified length, the stream has ended or an
 * exception occurred.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length;

/**
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:, this method does not call the
 * method when less than the specified length has been read - instead, it waits
 * until it got exactly the specified length, the stream has ended or an
 * exception occurred.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 * @param runLoopMode The run loop mode in which to perform the async read
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode;

# ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously reads *at most* ref size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.







|

















|
<




















|







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
 *		 The buffer must not be freed before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @param runLoopMode The run loop mode in which to perform the async read
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (OFRunLoopMode)runLoopMode;

/**
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:, this method does not call the
 * method when less than the specified length has been read - instead, it waits
 * until it got exactly the specified length, the stream has ended or an
 * exception occurred.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 */
- (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length;


/**
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:, this method does not call the
 * method when less than the specified length has been read - instead, it waits
 * until it got exactly the specified length, the stream has ended or an
 * exception occurred.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 * @param runLoopMode The run loop mode in which to perform the async read
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (OFRunLoopMode)runLoopMode;

# ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously reads *at most* ref size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
 *		If the block returns true, it will be called again with the same
 *		buffer and maximum length when more data has been received. If
 *		you want the next block in the queue to handle the data
 *		received next, you need to return false from the block.
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		      block: (of_stream_async_read_block_t)block;

/**
 * @brief Asynchronously reads *at most* ref size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use







|







375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
 *		If the block returns true, it will be called again with the same
 *		buffer and maximum length when more data has been received. If
 *		you want the next block in the queue to handle the data
 *		received next, you need to return false from the block.
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		      block: (OFStreamAsyncReadBlock)block;

/**
 * @brief Asynchronously reads *at most* ref size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
 *		If the block returns true, it will be called again with the same
 *		buffer and maximum length when more data has been received. If
 *		you want the next block in the queue to handle the data
 *		received next, you need to return false from the block.
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
		      block: (of_stream_async_read_block_t)block;

/**
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke
 * the block when less than the specified length has been read - instead, it







|
|







405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
 *		If the block returns true, it will be called again with the same
 *		buffer and maximum length when more data has been received. If
 *		you want the next block in the queue to handle the data
 *		received next, you need to return false from the block.
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (OFRunLoopMode)runLoopMode
		      block: (OFStreamAsyncReadBlock)block;

/**
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke
 * the block when less than the specified length has been read - instead, it
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
 *		If the block returns true, it will be called again with the same
 *		buffer and exact length when more data has been received. If
 *		you want the next block in the queue to handle the data
 *		received next, you need to return false from the block.
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		      block: (of_stream_async_read_block_t)block;

/**
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke
 * the block when less than the specified length has been read - instead, it







|







431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
 *		If the block returns true, it will be called again with the same
 *		buffer and exact length when more data has been received. If
 *		you want the next block in the queue to handle the data
 *		received next, you need to return false from the block.
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		      block: (OFStreamAsyncReadBlock)block;

/**
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke
 * the block when less than the specified length has been read - instead, it
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
 *		If the block returns true, it will be called again with the same
 *		buffer and exact length when more data has been received. If
 *		you want the next block in the queue to handle the data
 *		received next, you need to return false from the block.
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
		      block: (of_stream_async_read_block_t)block;
# endif
#endif

/**
 * @brief Reads a uint8_t from the stream.
 *
 * @warning Only call this when you know that enough data is available!







|
|







457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
 *		If the block returns true, it will be called again with the same
 *		buffer and exact length when more data has been received. If
 *		you want the next block in the queue to handle the data
 *		received next, you need to return false from the block.
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (OFRunLoopMode)runLoopMode
		      block: (OFStreamAsyncReadBlock)block;
# endif
#endif

/**
 * @brief Reads a uint8_t from the stream.
 *
 * @warning Only call this when you know that enough data is available!
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
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @return A double from the stream in the native endianess
 */
- (double)readBigEndianDouble;

/**
 * @brief Reads the specified number of uint16_ts from the stream which are
 *	  encoded in big endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 uint16_ts
 * @param count The number of uint16_ts to read
 * @return The number of bytes read
 */
- (size_t)readBigEndianInt16sIntoBuffer: (uint16_t *)buffer
				  count: (size_t)count;

/**
 * @brief Reads the specified number of uint32_ts from the stream which are
 *	  encoded in big endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 uint32_ts
 * @param count The number of uint32_ts to read
 * @return The number of bytes read
 */
- (size_t)readBigEndianInt32sIntoBuffer: (uint32_t *)buffer
				  count: (size_t)count;

/**
 * @brief Reads the specified number of uint64_ts from the stream which are
 *	  encoded in big endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 uint64_ts
 * @param count The number of uint64_ts to read
 * @return The number of bytes read
 */
- (size_t)readBigEndianInt64sIntoBuffer: (uint64_t *)buffer
				  count: (size_t)count;

/**
 * @brief Reads the specified number of floats from the stream which are encoded
 *	  in big endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 floats
 * @param count The number of floats to read
 * @return The number of bytes read
 */
- (size_t)readBigEndianFloatsIntoBuffer: (float *)buffer
				  count: (size_t)count;

/**
 * @brief Reads the specified number of doubles from the stream which are
 *	  encoded in big endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 doubles
 * @param count The number of doubles to read
 * @return The number of bytes read
 */
- (size_t)readBigEndianDoublesIntoBuffer: (double *)buffer
				   count: (size_t)count;

/**
 * @brief Reads a uint16_t from the stream which is encoded in little endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @return A uint16_t from the stream in native endianess







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







522
523
524
525
526
527
528











































































529
530
531
532
533
534
535
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @return A double from the stream in the native endianess
 */
- (double)readBigEndianDouble;












































































/**
 * @brief Reads a uint16_t from the stream which is encoded in little endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @return A uint16_t from the stream in native endianess
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
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @return A double from the stream in the native endianess
 */
- (double)readLittleEndianDouble;

/**
 * @brief Reads the specified number of uint16_ts from the stream which are
 *	  encoded in little endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 uint16_ts
 * @param count The number of uint16_ts to read
 * @return The number of bytes read
 */
- (size_t)readLittleEndianInt16sIntoBuffer: (uint16_t *)buffer
				     count: (size_t)count;

/**
 * @brief Reads the specified number of uint32_ts from the stream which are
 *	  encoded in little endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 uint32_ts
 * @param count The number of uint32_ts to read
 * @return The number of bytes read
 */
- (size_t)readLittleEndianInt32sIntoBuffer: (uint32_t *)buffer
				     count: (size_t)count;

/**
 * @brief Reads the specified number of uint64_ts from the stream which are
 *	  encoded in little endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 uint64_ts
 * @param count The number of uint64_ts to read
 * @return The number of bytes read
 */
- (size_t)readLittleEndianInt64sIntoBuffer: (uint64_t *)buffer
				     count: (size_t)count;

/**
 * @brief Reads the specified number of floats from the stream which are
 *	  encoded in little endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 floats
 * @param count The number of floats to read
 * @return The number of bytes read
 */
- (size_t)readLittleEndianFloatsIntoBuffer: (float *)buffer
				     count: (size_t)count;

/**
 * @brief Reads the specified number of doubles from the stream which are
 *	  encoded in little endian.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param buffer A buffer of sufficient size to store the specified number of
 *		 doubles
 * @param count The number of doubles to read
 * @return The number of bytes read
 */
- (size_t)readLittleEndianDoublesIntoBuffer: (double *)buffer
				      count: (size_t)count;

/**
 * @brief Reads the specified number of items with an item size of 1 from the
 *	  stream and returns them as OFData.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *







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







572
573
574
575
576
577
578











































































579
580
581
582
583
584
585
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @return A double from the stream in the native endianess
 */
- (double)readLittleEndianDouble;












































































/**
 * @brief Reads the specified number of items with an item size of 1 from the
 *	  stream and returns them as OFData.
 *
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param itemSize The size of each item
 * @param count The number of items to read
 * @return OFData with count items.
 */
- (OFData *)readDataWithItemSize: (size_t)itemSize
			   count: (size_t)count;

/**
 * @brief Returns OFData with all the remaining data of the stream.
 *
 * @return OFData with an item size of 1 with all the data of the stream until
 *	   the end of the stream is reached.
 */







|
<







595
596
597
598
599
600
601
602

603
604
605
606
607
608
609
 * @warning Only call this when you know that enough data is available!
 *	    Otherwise you will get an exception!
 *
 * @param itemSize The size of each item
 * @param count The number of items to read
 * @return OFData with count items.
 */
- (OFData *)readDataWithItemSize: (size_t)itemSize count: (size_t)count;


/**
 * @brief Returns OFData with all the remaining data of the stream.
 *
 * @return OFData with an item size of 1 with all the data of the stream until
 *	   the end of the stream is reached.
 */
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
 *	    Otherwise you will get an exception!
 *
 * @param encoding The encoding of the string to read from the stream
 * @param length The length (in bytes) of the string to read from the stream
 * @return A string with the specified length
 */
- (OFString *)readStringWithLength: (size_t)length
			  encoding: (of_string_encoding_t)encoding;

/**
 * @brief Reads until a newline, `\0` or end of stream occurs.
 *
 * @return The line that was read, autoreleased, or `nil` if the end of the
 *	   stream has been reached.
 */
- (nullable OFString *)readLine;

/**
 * @brief Reads with the specified encoding until a newline, `\0` or end of
 *	  stream occurs.
 *
 * @param encoding The encoding used by the stream
 * @return The line that was read, autoreleased, or `nil` if the end of the
 *	   stream has been reached.
 */
- (nullable OFString *)readLineWithEncoding: (of_string_encoding_t)encoding;

#ifdef OF_HAVE_SOCKETS
/**
 * @brief Asynchronously reads until a newline, `\0`, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 */
- (void)asyncReadLine;

/**
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding;

/**
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param runLoopMode The run loop mode in which to perform the async read
 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
		      runLoopMode: (of_run_loop_mode_t)runLoopMode;

# ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously reads until a newline, `\0`, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again when the next
 *		line has been received. If you want the next block in the queue
 *		to handle the next line, you need to return false from the
 *		block.
 */
- (void)asyncReadLineWithBlock: (of_stream_async_read_line_block_t)block;

/**
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again when the next
 *		line has been received. If you want the next block in the queue
 *		to handle the next line, you need to return false from the
 *		block.
 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
			    block: (of_stream_async_read_line_block_t)block;

/**
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param runLoopMode The run loop mode in which to perform the async read
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again when the next
 *		line has been received. If you want the next block in the queue
 *		to handle the next line, you need to return false from the
 *		block.
 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
		      runLoopMode: (of_run_loop_mode_t)runLoopMode
			    block: (of_stream_async_read_line_block_t)block;
# endif
#endif

/**
 * @brief Tries to read a line from the stream (see @ref readLine) and returns
 *	  `nil` if no complete line has been received yet.
 *







|

















|




















|











|
|















|















|
|
















|
|
|







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
 *	    Otherwise you will get an exception!
 *
 * @param encoding The encoding of the string to read from the stream
 * @param length The length (in bytes) of the string to read from the stream
 * @return A string with the specified length
 */
- (OFString *)readStringWithLength: (size_t)length
			  encoding: (OFStringEncoding)encoding;

/**
 * @brief Reads until a newline, `\0` or end of stream occurs.
 *
 * @return The line that was read, autoreleased, or `nil` if the end of the
 *	   stream has been reached.
 */
- (nullable OFString *)readLine;

/**
 * @brief Reads with the specified encoding until a newline, `\0` or end of
 *	  stream occurs.
 *
 * @param encoding The encoding used by the stream
 * @return The line that was read, autoreleased, or `nil` if the end of the
 *	   stream has been reached.
 */
- (nullable OFString *)readLineWithEncoding: (OFStringEncoding)encoding;

#ifdef OF_HAVE_SOCKETS
/**
 * @brief Asynchronously reads until a newline, `\0`, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 */
- (void)asyncReadLine;

/**
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 */
- (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding;

/**
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param runLoopMode The run loop mode in which to perform the async read
 */
- (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding
		      runLoopMode: (OFRunLoopMode)runLoopMode;

# ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously reads until a newline, `\0`, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again when the next
 *		line has been received. If you want the next block in the queue
 *		to handle the next line, you need to return false from the
 *		block.
 */
- (void)asyncReadLineWithBlock: (OFStreamAsyncReadLineBlock)block;

/**
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again when the next
 *		line has been received. If you want the next block in the queue
 *		to handle the next line, you need to return false from the
 *		block.
 */
- (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding
			    block: (OFStreamAsyncReadLineBlock)block;

/**
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param runLoopMode The run loop mode in which to perform the async read
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again when the next
 *		line has been received. If you want the next block in the queue
 *		to handle the next line, you need to return false from the
 *		block.
 */
- (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding
		      runLoopMode: (OFRunLoopMode)runLoopMode
			    block: (OFStreamAsyncReadLineBlock)block;
# endif
#endif

/**
 * @brief Tries to read a line from the stream (see @ref readLine) and returns
 *	  `nil` if no complete line has been received yet.
 *
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
 *	  @ref readLineWithEncoding:) and returns `nil` if no complete line has
 *	  been received yet.
 *
 * @param encoding The encoding used by the stream
 * @return The line that was read, autoreleased, or `nil` if the line is not
 *	   complete yet
 */
- (nullable OFString *)tryReadLineWithEncoding: (of_string_encoding_t)encoding;

/**
 * @brief Reads until the specified string or `\0` is found or the end of
 *	  stream occurs.
 *
 * @param delimiter The delimiter
 * @return The line that was read, autoreleased, or `nil` if the end of the







|







763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
 *	  @ref readLineWithEncoding:) and returns `nil` if no complete line has
 *	  been received yet.
 *
 * @param encoding The encoding used by the stream
 * @return The line that was read, autoreleased, or `nil` if the line is not
 *	   complete yet
 */
- (nullable OFString *)tryReadLineWithEncoding: (OFStringEncoding)encoding;

/**
 * @brief Reads until the specified string or `\0` is found or the end of
 *	  stream occurs.
 *
 * @param delimiter The delimiter
 * @return The line that was read, autoreleased, or `nil` if the end of the
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
 *
 * @param delimiter The delimiter
 * @param encoding The encoding used by the stream
 * @return The line that was read, autoreleased, or `nil` if the end of the
 *	   stream has been reached.
 */
- (nullable OFString *)readTillDelimiter: (OFString *)delimiter
				encoding: (of_string_encoding_t)encoding;

/**
 * @brief Tries to reads until the specified string or `\0` is found or the end
 *	  of stream (see @ref readTillDelimiter:) and returns `nil` if not
 *	  enough data has been received yet.
 *
 * @param delimiter The delimiter







|







785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
 *
 * @param delimiter The delimiter
 * @param encoding The encoding used by the stream
 * @return The line that was read, autoreleased, or `nil` if the end of the
 *	   stream has been reached.
 */
- (nullable OFString *)readTillDelimiter: (OFString *)delimiter
				encoding: (OFStringEncoding)encoding;

/**
 * @brief Tries to reads until the specified string or `\0` is found or the end
 *	  of stream (see @ref readTillDelimiter:) and returns `nil` if not
 *	  enough data has been received yet.
 *
 * @param delimiter The delimiter
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
 *
 * @param delimiter The delimiter
 * @param encoding The encoding used by the stream
 * @return The line that was read, autoreleased, or `nil` if the end of the
 *	   stream has been reached.
 */
- (nullable OFString *)tryReadTillDelimiter: (OFString *)delimiter
				   encoding: (of_string_encoding_t)encoding;

/**
 * @brief Writes everything in the write buffer to the stream.




 */
- (void)flushWriteBuffer;

/**
 * @brief Writes from a buffer into the stream.







 *
 * @param buffer The buffer from which the data is written into the stream
 * @param length The length of the data that should be written
 * @return The number of bytes written. This can only differ from the specified
 *	   length in non-blocking mode.
 */
- (size_t)writeBuffer: (const void *)buffer
	       length: (size_t)length;

#ifdef OF_HAVE_SOCKETS
/**
 * @brief Asynchronously writes data into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!







|



>
>
>
>

|



>
>
>
>
>
>
>



<
<

|
<







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
 *
 * @param delimiter The delimiter
 * @param encoding The encoding used by the stream
 * @return The line that was read, autoreleased, or `nil` if the end of the
 *	   stream has been reached.
 */
- (nullable OFString *)tryReadTillDelimiter: (OFString *)delimiter
				   encoding: (OFStringEncoding)encoding;

/**
 * @brief Writes everything in the write buffer to the stream.
 *
 * @return Whether the write buffer was flushed entirely. On non-blocking
 *	   sockets, this can return `false` if flushing the write buffer in its
 *	   entirety would block.
 */
- (bool)flushWriteBuffer;

/**
 * @brief Writes from a buffer into the stream.
 *
 * In non-blocking mode, if less than the specified length could be written, an
 * @ref OFWriteFailedException is thrown with @ref OFWriteFailedException#errNo
 * being set to `EWOULDBLOCK` or `EAGAIN` (you need to check for both, as they
 * are not the same on some systems) and
 * @ref OFWriteFailedException#bytesWritten being set to the number of bytes
 * that were written, if any.
 *
 * @param buffer The buffer from which the data is written into the stream
 * @param length The length of the data that should be written


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


#ifdef OF_HAVE_SOCKETS
/**
 * @brief Asynchronously writes data into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param data The data which is written into the stream
 * @param runLoopMode The run loop mode in which to perform the async write
 */
- (void)asyncWriteData: (OFData *)data
	   runLoopMode: (of_run_loop_mode_t)runLoopMode;

/**
 * @brief Asynchronously writes a string in UTF-8 encoding into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *







|







856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param data The data which is written into the stream
 * @param runLoopMode The run loop mode in which to perform the async write
 */
- (void)asyncWriteData: (OFData *)data
	   runLoopMode: (OFRunLoopMode)runLoopMode;

/**
 * @brief Asynchronously writes a string in UTF-8 encoding into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param encoding The encoding in which the string should be written to the
 *		   stream
 */
- (void)asyncWriteString: (OFString *)string
		encoding: (of_string_encoding_t)encoding;

/**
 * @brief Asynchronously writes a string in the specified encoding into the
 *	  stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param encoding The encoding in which the string should be written to the
 *		   stream
 * @param runLoopMode The run loop mode in which to perform the async write
 */
- (void)asyncWriteString: (OFString *)string
		encoding: (of_string_encoding_t)encoding
	     runLoopMode: (of_run_loop_mode_t)runLoopMode;

# ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously writes data into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param data The data which is written into the stream
 * @param block The block to call when the data has been written. It should
 *		return the data for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteData: (OFData *)data
		 block: (of_stream_async_write_data_block_t)block;

/**
 * @brief Asynchronously writes data into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param data The data which is written into the stream
 * @param runLoopMode The run loop mode in which to perform the async write
 * @param block The block to call when the data has been written. It should
 *		return the data for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteData: (OFData *)data
	   runLoopMode: (of_run_loop_mode_t)runLoopMode
		 block: (of_stream_async_write_data_block_t)block;

/**
 * @brief Asynchronously writes a string into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param block The block to call when the string has been written. It should
 *		return the string for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteString: (OFString *)string
		   block: (of_stream_async_write_string_block_t)block;

/**
 * @brief Asynchronously writes a string in the specified encoding into the
 *	  stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param encoding The encoding in which the string should be written to the
 *		   stream
 * @param block The block to call when the string has been written. It should
 *		return the string for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteString: (OFString *)string
		encoding: (of_string_encoding_t)encoding
		   block: (of_stream_async_write_string_block_t)block;

/**
 * @brief Asynchronously writes a string in the specified encoding into the
 *	  stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param encoding The encoding in which the string should be written to the
 *		   stream
 * @param runLoopMode The run loop mode in which to perform the async write
 * @param block The block to call when the string has been written. It should
 *		return the string for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteString: (OFString *)string
		encoding: (of_string_encoding_t)encoding
	     runLoopMode: (of_run_loop_mode_t)runLoopMode
		   block: (of_stream_async_write_string_block_t)block;
# endif
#endif

/**
 * @brief Writes a uint8_t into the stream.


 *
 * @param int8 A uint8_t
 */
- (void)writeInt8: (uint8_t)int8;

/**
 * @brief Writes a uint16_t into the stream, encoded in big endian.


 *
 * @param int16 A uint16_t
 */
- (void)writeBigEndianInt16: (uint16_t)int16;

/**
 * @brief Writes a uint32_t into the stream, encoded in big endian.
 *


 * @param int32 A uint32_t
 */
- (void)writeBigEndianInt32: (uint32_t)int32;

/**
 * @brief Writes a uint64_t into the stream, encoded in big endian.
 *


 * @param int64 A uint64_t
 */
- (void)writeBigEndianInt64: (uint64_t)int64;

/**
 * @brief Writes a float into the stream, encoded in big endian.


 *
 * @param float_ A float
 */
- (void)writeBigEndianFloat: (float)float_;

/**
 * @brief Writes a double into the stream, encoded in big endian.
 *


 * @param double_ A double
 */
- (void)writeBigEndianDouble: (double)double_;

/**
 * @brief Writes the specified number of uint16_ts into the stream, encoded in
 *	  big endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of uint16_ts to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeBigEndianInt16s: (const uint16_t *)buffer
			 count: (size_t)count;

/**
 * @brief Writes the specified number of uint32_ts into the stream, encoded in
 *	  big endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of uint32_ts to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeBigEndianInt32s: (const uint32_t *)buffer
			 count: (size_t)count;

/**
 * @brief Writes the specified number of uint64_ts into the stream, encoded in
 *	  big endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of uint64_ts to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeBigEndianInt64s: (const uint64_t *)buffer
			 count: (size_t)count;

/**
 * @brief Writes the specified number of floats into the stream, encoded in big
 *	  endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of floats to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeBigEndianFloats: (const float *)buffer
			 count: (size_t)count;

/**
 * @brief Writes the specified number of doubles into the stream, encoded in
 *	  big endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of doubles to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeBigEndianDoubles: (const double *)buffer
			  count: (size_t)count;

/**
 * @brief Writes a uint16_t into the stream, encoded in little endian.
 *
 * @param int16 A uint16_t
 */
- (void)writeLittleEndianInt16: (uint16_t)int16;

/**
 * @brief Writes a uint32_t into the stream, encoded in little endian.
 *


 * @param int32 A uint32_t
 */
- (void)writeLittleEndianInt32: (uint32_t)int32;

/**
 * @brief Writes a uint64_t into the stream, encoded in little endian.
 *


 * @param int64 A uint64_t
 */
- (void)writeLittleEndianInt64: (uint64_t)int64;

/**
 * @brief Writes a float into the stream, encoded in little endian.


 *
 * @param float_ A float
 */
- (void)writeLittleEndianFloat: (float)float_;

/**
 * @brief Writes a double into the stream, encoded in little endian.
 *


 * @param double_ A double
 */
- (void)writeLittleEndianDouble: (double)double_;

/**
 * @brief Writes the specified number of uint16_ts into the stream, encoded in
 *	  little endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of uint16_ts to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeLittleEndianInt16s: (const uint16_t *)buffer
			    count: (size_t)count;

/**
 * @brief Writes the specified number of uint32_ts into the stream, encoded in
 *	  little endian.
 *
 * @param count The number of uint32_ts to write
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @return The number of bytes written to the stream
 */
- (size_t)writeLittleEndianInt32s: (const uint32_t *)buffer
			    count: (size_t)count;

/**
 * @brief Writes the specified number of uint64_ts into the stream, encoded in
 *	  little endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of uint64_ts to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeLittleEndianInt64s: (const uint64_t *)buffer
			    count: (size_t)count;

/**
 * @brief Writes the specified number of floats into the stream, encoded in
 *	  little endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of floats to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeLittleEndianFloats: (const float *)buffer
			    count: (size_t)count;

/**
 * @brief Writes the specified number of doubles into the stream, encoded in
 *	  little endian.
 *
 * @param buffer The buffer from which the data is written to the stream after
 *		 it has been byte swapped if necessary
 * @param count The number of doubles to write
 * @return The number of bytes written to the stream
 */
- (size_t)writeLittleEndianDoubles: (const double *)buffer
			     count: (size_t)count;

/**
 * @brief Writes OFData into the stream.
 *
 * @param data The OFData to write into the stream
 * @return The number of bytes written
 */
- (size_t)writeData: (OFData *)data;

/**
 * @brief Writes a string into the stream, without the trailing zero.
 *


 * @param string The string from which the data is written to the stream
 * @return The number of bytes written
 */
- (size_t)writeString: (OFString *)string;

/**
 * @brief Writes a string into the stream in the specified encoding, without
 *	  the trailing zero.
 *


 * @param string The string from which the data is written to the stream
 * @param encoding The encoding in which to write the string to the stream
 * @return The number of bytes written
 */
- (size_t)writeString: (OFString *)string
	     encoding: (of_string_encoding_t)encoding;

/**
 * @brief Writes a string into the stream with a trailing newline.
 *


 * @param string The string from which the data is written to the stream
 * @return The number of bytes written
 */
- (size_t)writeLine: (OFString *)string;

/**
 * @brief Writes a string into the stream in the specified encoding with a
 *	  trailing newline.
 *


 * @param string The string from which the data is written to the stream
 * @param encoding The encoding in which to write the string to the stream
 * @return The number of bytes written
 */
- (size_t)writeLine: (OFString *)string
	   encoding: (of_string_encoding_t)encoding;

/**
 * @brief Writes a formatted string into the stream.
 *
 * See printf for the format syntax. As an addition, `%@` is available as
 * format specifier for objects, `%C` for `of_unichar_t` and `%S` for
 * `const of_unichar_t *`.
 *


 * @param format A string used as format
 * @return The number of bytes written
 */
- (size_t)writeFormat: (OFConstantString *)format, ...;

/**
 * @brief Writes a formatted string into the stream.
 *
 * See printf for the format syntax. As an addition, `%@` is available as
 * format specifier for objects, `%C` for `of_unichar_t` and `%S` for
 * `const of_unichar_t *`.


 *
 * @param format A string used as format
 * @param arguments The arguments used in the format string
 * @return The number of bytes written
 */
- (size_t)writeFormat: (OFConstantString *)format
	    arguments: (va_list)arguments;

#ifdef OF_HAVE_SOCKETS
/**
 * @brief Cancels all pending asynchronous requests on the stream.
 */
- (void)cancelAsyncRequests;
#endif







|














|
|














|














|
|













|
















|
|

















|
|
|





>
>







>
>








>
>







>
>






>
>








>
>





<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<

<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<








>
>







>
>






>
>








>
>





<
<
<
<
<
<
<
<
<
<
|
<
<
<

<
<
<
<
<
<
<
|
<
<
<

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

<

|




>
>

<

|





>
>


<

|
<




>
>

<

|





>
>


<

|
<





|
|

>
>

<

|





|
|
>
>



<

|
<







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
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param encoding The encoding in which the string should be written to the
 *		   stream
 */
- (void)asyncWriteString: (OFString *)string
		encoding: (OFStringEncoding)encoding;

/**
 * @brief Asynchronously writes a string in the specified encoding into the
 *	  stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param encoding The encoding in which the string should be written to the
 *		   stream
 * @param runLoopMode The run loop mode in which to perform the async write
 */
- (void)asyncWriteString: (OFString *)string
		encoding: (OFStringEncoding)encoding
	     runLoopMode: (OFRunLoopMode)runLoopMode;

# ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously writes data into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param data The data which is written into the stream
 * @param block The block to call when the data has been written. It should
 *		return the data for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteData: (OFData *)data
		 block: (OFStreamAsyncWriteDataBlock)block;

/**
 * @brief Asynchronously writes data into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param data The data which is written into the stream
 * @param runLoopMode The run loop mode in which to perform the async write
 * @param block The block to call when the data has been written. It should
 *		return the data for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteData: (OFData *)data
	   runLoopMode: (OFRunLoopMode)runLoopMode
		 block: (OFStreamAsyncWriteDataBlock)block;

/**
 * @brief Asynchronously writes a string into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param block The block to call when the string has been written. It should
 *		return the string for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteString: (OFString *)string
		   block: (OFStreamAsyncWriteStringBlock)block;

/**
 * @brief Asynchronously writes a string in the specified encoding into the
 *	  stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param encoding The encoding in which the string should be written to the
 *		   stream
 * @param block The block to call when the string has been written. It should
 *		return the string for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteString: (OFString *)string
		encoding: (OFStringEncoding)encoding
		   block: (OFStreamAsyncWriteStringBlock)block;

/**
 * @brief Asynchronously writes a string in the specified encoding into the
 *	  stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param string The string which is written into the stream
 * @param encoding The encoding in which the string should be written to the
 *		   stream
 * @param runLoopMode The run loop mode in which to perform the async write
 * @param block The block to call when the string has been written. It should
 *		return the string for the next write with the same callback or
 *		nil if it should not repeat.
 */
- (void)asyncWriteString: (OFString *)string
		encoding: (OFStringEncoding)encoding
	     runLoopMode: (OFRunLoopMode)runLoopMode
		   block: (OFStreamAsyncWriteStringBlock)block;
# endif
#endif

/**
 * @brief Writes a uint8_t into the stream.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param int8 A uint8_t
 */
- (void)writeInt8: (uint8_t)int8;

/**
 * @brief Writes a uint16_t into the stream, encoded in big endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param int16 A uint16_t
 */
- (void)writeBigEndianInt16: (uint16_t)int16;

/**
 * @brief Writes a uint32_t into the stream, encoded in big endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param int32 A uint32_t
 */
- (void)writeBigEndianInt32: (uint32_t)int32;

/**
 * @brief Writes a uint64_t into the stream, encoded in big endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param int64 A uint64_t
 */
- (void)writeBigEndianInt64: (uint64_t)int64;

/**
 * @brief Writes a float into the stream, encoded in big endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param float_ A float
 */
- (void)writeBigEndianFloat: (float)float_;

/**
 * @brief Writes a double into the stream, encoded in big endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param double_ A double
 */
- (void)writeBigEndianDouble: (double)double_;

/**




































 * @brief Writes a uint16_t into the stream, encoded in little endian.

 *







 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.














 *
 * @param int16 A uint16_t
 */
- (void)writeLittleEndianInt16: (uint16_t)int16;

/**
 * @brief Writes a uint32_t into the stream, encoded in little endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param int32 A uint32_t
 */
- (void)writeLittleEndianInt32: (uint32_t)int32;

/**
 * @brief Writes a uint64_t into the stream, encoded in little endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param int64 A uint64_t
 */
- (void)writeLittleEndianInt64: (uint64_t)int64;

/**
 * @brief Writes a float into the stream, encoded in little endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param float_ A float
 */
- (void)writeLittleEndianFloat: (float)float_;

/**
 * @brief Writes a double into the stream, encoded in little endian.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param double_ A double
 */
- (void)writeLittleEndianDouble: (double)double_;

/**










 * @brief Writes OFData into the stream.



 *







 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.



 *



































 * @param data The OFData to write into the stream

 */
- (void)writeData: (OFData *)data;

/**
 * @brief Writes a string into the stream, without the trailing zero.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param string The string from which the data is written to the stream

 */
- (void)writeString: (OFString *)string;

/**
 * @brief Writes a string into the stream in the specified encoding, without
 *	  the trailing zero.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param string The string from which the data is written to the stream
 * @param encoding The encoding in which to write the string to the stream

 */
- (void)writeString: (OFString *)string encoding: (OFStringEncoding)encoding;


/**
 * @brief Writes a string into the stream with a trailing newline.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param string The string from which the data is written to the stream

 */
- (void)writeLine: (OFString *)string;

/**
 * @brief Writes a string into the stream in the specified encoding with a
 *	  trailing newline.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param string The string from which the data is written to the stream
 * @param encoding The encoding in which to write the string to the stream

 */
- (void)writeLine: (OFString *)string encoding: (OFStringEncoding)encoding;


/**
 * @brief Writes a formatted string into the stream.
 *
 * See printf for the format syntax. As an addition, `%@` is available as
 * format specifier for objects, `%C` for `OFUnichar` and `%S` for
 * `const OFUnichar *`.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param format A string used as format

 */
- (void)writeFormat: (OFConstantString *)format, ...;

/**
 * @brief Writes a formatted string into the stream.
 *
 * See printf for the format syntax. As an addition, `%@` is available as
 * format specifier for objects, `%C` for `OFUnichar` and `%S` for
 * `const OFUnichar *`.
 *
 * In non-blocking mode, the behavior is the same as @ref writeBuffer:length:.
 *
 * @param format A string used as format
 * @param arguments The arguments used in the format string

 */
- (void)writeFormat: (OFConstantString *)format arguments: (va_list)arguments;


#ifdef OF_HAVE_SOCKETS
/**
 * @brief Cancels all pending asynchronous requests on the stream.
 */
- (void)cancelAsyncRequests;
#endif
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
 *
 * If the stream is seekable, a seek operation will discard any data which was
 * unread.
 *
 * @param buffer The buffer to unread
 * @param length The length of the buffer to unread
 */
- (void)unreadFromBuffer: (const void *)buffer
		  length: (size_t)length;

/**
 * @brief Closes the stream.
 *
 * @note If you override this, make sure to call `[super close]`!
 */
- (void)close;

/**
 * @brief Performs a lowlevel read.
 *
 * @warning Do not call this directly!
 *
 * @note Override this method with your actual read implementation when
 *	 subclassing!
 *
 * @param buffer The buffer for the data to read
 * @param length The length of the buffer
 * @return The number of bytes read
 */
- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length;

/**
 * @brief Performs a lowlevel write.
 *
 * @warning Do not call this directly!
 *
 * @note Override this method with your actual write implementation when
 *	 subclassing!
 *
 * @param buffer The buffer with the data to write
 * @param length The length of the data to write
 * @return The number of bytes written
 */
- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length;

/**
 * @brief Returns whether the lowlevel is at the end of the stream.
 *
 * @warning Do not call this directly!
 *
 * @note Override this method with your actual end of stream checking







|
<




















|
<













|
<







1186
1187
1188
1189
1190
1191
1192
1193

1194
1195
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
1223
1224
1225
1226
1227
1228

1229
1230
1231
1232
1233
1234
1235
 *
 * If the stream is seekable, a seek operation will discard any data which was
 * unread.
 *
 * @param buffer The buffer to unread
 * @param length The length of the buffer to unread
 */
- (void)unreadFromBuffer: (const void *)buffer length: (size_t)length;


/**
 * @brief Closes the stream.
 *
 * @note If you override this, make sure to call `[super close]`!
 */
- (void)close;

/**
 * @brief Performs a lowlevel read.
 *
 * @warning Do not call this directly!
 *
 * @note Override this method with your actual read implementation when
 *	 subclassing!
 *
 * @param buffer The buffer for the data to read
 * @param length The length of the buffer
 * @return The number of bytes read
 */
- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length;


/**
 * @brief Performs a lowlevel write.
 *
 * @warning Do not call this directly!
 *
 * @note Override this method with your actual write implementation when
 *	 subclassing!
 *
 * @param buffer The buffer with the data to write
 * @param length The length of the data to write
 * @return The number of bytes written
 */
- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length;


/**
 * @brief Returns whether the lowlevel is at the end of the stream.
 *
 * @warning Do not call this directly!
 *
 * @note Override this method with your actual end of stream checking

Modified src/OFStream.m from [b34f9f0d7d] to [7df47f7053].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#ifdef OF_HAVE_SOCKETS
# import "socket_helpers.h"
#endif

#include "platform.h"

#if !defined(OF_WINDOWS) && !defined(OF_MORPHOS)
# include <signal.h>
#endif

#import "OFStream.h"
#import "OFStream+Private.h"

#import "OFData.h"
#import "OFKernelEventObserver.h"
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"



#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFSetOptionFailedException.h"
#import "OFTruncatedDataException.h"
#import "OFWriteFailedException.h"

#import "of_asprintf.h"

#define MIN_READ_SIZE 512

@implementation OFStream
@synthesize buffersWrites = _buffersWrites;
@synthesize of_waitingForDelimiter = _waitingForDelimiter, delegate = _delegate;

#if defined(SIGPIPE) && defined(SIG_IGN)
+ (void)initialize







<
<
<
<








>




>
>
>












<
|
<







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
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif





#include "platform.h"

#if !defined(OF_WINDOWS) && !defined(OF_MORPHOS)
# include <signal.h>
#endif

#import "OFStream.h"
#import "OFStream+Private.h"
#import "OFASPrintF.h"
#import "OFData.h"
#import "OFKernelEventObserver.h"
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"
#ifdef OF_HAVE_SOCKETS
# import "OFSocket+Private.h"
#endif
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFSetOptionFailedException.h"
#import "OFTruncatedDataException.h"
#import "OFWriteFailedException.h"


#define minReadSize 512


@implementation OFStream
@synthesize buffersWrites = _buffersWrites;
@synthesize of_waitingForDelimiter = _waitingForDelimiter, delegate = _delegate;

#if defined(SIGPIPE) && defined(SIG_IGN)
+ (void)initialize
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
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
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
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}









- (bool)lowlevelIsAtEndOfStream
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	OF_UNRECOGNIZED_SELECTOR
}

- (id)copy
{
	return [self retain];
}

- (bool)isAtEndOfStream
{
	if (_readBufferLength > 0)
		return false;

	return [self lowlevelIsAtEndOfStream];
}

- (size_t)readIntoBuffer: (void *)buffer
		  length: (size_t)length
{
	if (_readBufferLength == 0) {
		/*
		 * For small sizes, it is cheaper to read more and cache the
		 * remainder - even if that means more copying of data - than
		 * to do a syscall for every read.
		 */
		if (length < MIN_READ_SIZE) {
			char tmp[MIN_READ_SIZE], *readBuffer;
			size_t bytesRead;

			bytesRead = [self
			    lowlevelReadIntoBuffer: tmp
					    length: MIN_READ_SIZE];

			if (bytesRead > length) {
				memcpy(buffer, tmp, length);

				readBuffer = [self allocMemoryWithSize:
				    bytesRead - length];

				memcpy(readBuffer, tmp + length,
				    bytesRead - length);

				_readBuffer = _readBufferMemory = readBuffer;
				_readBufferLength = bytesRead - length;

				return length;
			} else {
				memcpy(buffer, tmp, bytesRead);
				return bytesRead;
			}
		}

		return [self lowlevelReadIntoBuffer: buffer
					     length: length];
	}

	if (length >= _readBufferLength) {
		size_t ret = _readBufferLength;
		memcpy(buffer, _readBuffer, _readBufferLength);

		[self freeMemory: _readBufferMemory];
		_readBuffer = _readBufferMemory = NULL;
		_readBufferLength = 0;

		return ret;
	} else {
		memcpy(buffer, _readBuffer, length);

		_readBuffer += length;
		_readBufferLength -= length;

		return length;
	}
}

- (void)readIntoBuffer: (void *)buffer
	   exactLength: (size_t)length
{
	size_t readLength = 0;

	while (readLength < length) {
		if (self.atEndOfStream)
			@throw [OFTruncatedDataException exception];

		readLength += [self readIntoBuffer: (char *)buffer + readLength
					    length: length - readLength];
	}
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
{
	[self asyncReadIntoBuffer: buffer
			   length: length
		      runLoopMode: of_run_loop_mode_default];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				     length: length
				       mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
				      block: NULL
# endif
				   delegate: _delegate];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
{
	[self asyncReadIntoBuffer: buffer
		      exactLength: length
		      runLoopMode: of_run_loop_mode_default];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				exactLength: length
				       mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
				      block: NULL
# endif
				   delegate: _delegate];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		      block: (of_stream_async_read_block_t)block
{
	[self asyncReadIntoBuffer: buffer
			   length: length
		      runLoopMode: of_run_loop_mode_default
			    block: block];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
		      block: (of_stream_async_read_block_t)block
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				     length: length
				       mode: runLoopMode
				      block: block
				   delegate: nil];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		      block: (of_stream_async_read_block_t)block
{
	[self asyncReadIntoBuffer: buffer
		      exactLength: length
		      runLoopMode: of_run_loop_mode_default
			    block: block];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
		      block: (of_stream_async_read_block_t)block
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				exactLength: length
				       mode: runLoopMode
				      block: block
				   delegate: nil];
}
# endif
#endif

- (uint8_t)readInt8
{
	uint8_t ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 1];

	return ret;
}

- (uint16_t)readBigEndianInt16
{
	uint16_t ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 2];

	return OF_BSWAP16_IF_LE(ret);
}

- (uint32_t)readBigEndianInt32
{
	uint32_t ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 4];

	return OF_BSWAP32_IF_LE(ret);
}

- (uint64_t)readBigEndianInt64
{
	uint64_t ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 8];

	return OF_BSWAP64_IF_LE(ret);
}

- (float)readBigEndianFloat
{
	float ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 4];

	return OF_BSWAP_FLOAT_IF_LE(ret);
}

- (double)readBigEndianDouble
{
	double ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 8];

	return OF_BSWAP_DOUBLE_IF_LE(ret);
}

- (size_t)readBigEndianInt16sIntoBuffer: (uint16_t *)buffer
				  count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint16_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint16_t);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifndef OF_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP16(buffer[i]);
#endif

	return size;
}

- (size_t)readBigEndianInt32sIntoBuffer: (uint32_t *)buffer
				  count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint32_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint32_t);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifndef OF_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP32(buffer[i]);
#endif

	return size;
}

- (size_t)readBigEndianInt64sIntoBuffer: (uint64_t *)buffer
				  count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint64_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint64_t);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifndef OF_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP64(buffer[i]);
#endif

	return size;
}

- (size_t)readBigEndianFloatsIntoBuffer: (float *)buffer
				  count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(float))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(float);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifndef OF_FLOAT_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP_FLOAT(buffer[i]);
#endif

	return size;
}

- (size_t)readBigEndianDoublesIntoBuffer: (double *)buffer
				   count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(double))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(double);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifndef OF_FLOAT_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP_DOUBLE(buffer[i]);
#endif

	return size;
}

- (uint16_t)readLittleEndianInt16
{
	uint16_t ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 2];

	return OF_BSWAP16_IF_BE(ret);
}

- (uint32_t)readLittleEndianInt32
{
	uint32_t ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 4];

	return OF_BSWAP32_IF_BE(ret);
}

- (uint64_t)readLittleEndianInt64
{
	uint64_t ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 8];

	return OF_BSWAP64_IF_BE(ret);
}

- (float)readLittleEndianFloat
{
	float ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 4];

	return OF_BSWAP_FLOAT_IF_BE(ret);
}

- (double)readLittleEndianDouble
{
	double ret;

	[self readIntoBuffer: (char *)&ret
		 exactLength: 8];

	return OF_BSWAP_DOUBLE_IF_BE(ret);
}

- (size_t)readLittleEndianInt16sIntoBuffer: (uint16_t *)buffer
				     count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint16_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint16_t);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifdef OF_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP16(buffer[i]);
#endif

	return size;
}

- (size_t)readLittleEndianInt32sIntoBuffer: (uint32_t *)buffer
				     count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint32_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint32_t);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifdef OF_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP32(buffer[i]);
#endif

	return size;
}

- (size_t)readLittleEndianInt64sIntoBuffer: (uint64_t *)buffer
				     count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint64_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint64_t);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifdef OF_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP64(buffer[i]);
#endif

	return size;
}

- (size_t)readLittleEndianFloatsIntoBuffer: (float *)buffer
				     count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(float))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(float);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifdef OF_FLOAT_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP_FLOAT(buffer[i]);
#endif

	return size;
}

- (size_t)readLittleEndianDoublesIntoBuffer: (double *)buffer
				      count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(double))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(double);

	[self readIntoBuffer: buffer
		 exactLength: size];

#ifdef OF_FLOAT_BIG_ENDIAN
	for (size_t i = 0; i < count; i++)
		buffer[i] = OF_BSWAP_DOUBLE(buffer[i]);
#endif

	return size;
}

- (OFData *)readDataWithCount: (size_t)count
{
	return [self readDataWithItemSize: 1
				    count: count];
}

- (OFData *)readDataWithItemSize: (size_t)itemSize
			   count: (size_t)count
{
	OFData *ret;
	char *buffer;

	if OF_UNLIKELY (count > SIZE_MAX / itemSize)
		@throw [OFOutOfRangeException exception];

	buffer = of_malloc(count, itemSize);
	@try {
		[self readIntoBuffer: buffer
			 exactLength: count * itemSize];

		ret = [OFData dataWithItemsNoCopy: buffer
					 itemSize: itemSize
					    count: count

				     freeWhenDone: true];
	} @catch (id e) {
		of_free(buffer);
		@throw e;
	}

	return ret;
}

- (OFData *)readDataUntilEndOfStream
{
	OFMutableData *data = [OFMutableData data];
	size_t pageSize = [OFSystemInfo pageSize];
	char *buffer = [self allocMemoryWithSize: pageSize];

	@try {
		while (!self.atEndOfStream) {
			size_t length;

			length = [self readIntoBuffer: buffer
					       length: pageSize];
			[data addItems: buffer
				 count: length];
		}
	} @finally {
		[self freeMemory: buffer];
	}

	[data makeImmutable];

	return data;
}

- (OFString *)readStringWithLength: (size_t)length
{
	return [self readStringWithLength: length
				 encoding: OF_STRING_ENCODING_UTF_8];
}

- (OFString *)readStringWithLength: (size_t)length
			  encoding: (of_string_encoding_t)encoding
{
	OFString *ret;
	char *buffer = [self allocMemoryWithSize: length + 1];
	buffer[length] = 0;

	@try {
		[self readIntoBuffer: buffer
			 exactLength: length];

		ret = [OFString stringWithCString: buffer
					 encoding: encoding];
	} @finally {
		[self freeMemory: buffer];
	}

	return ret;
}

- (OFString *)tryReadLineWithEncoding: (of_string_encoding_t)encoding
{
	size_t pageSize, bufferLength, retLength;
	char *retCString, *buffer, *readBuffer;
	OFString *ret;

	/* Look if there's a line or \0 in our buffer */
	if (!_waitingForDelimiter && _readBuffer != NULL) {
		for (size_t i = 0; i < _readBufferLength; i++) {
			if OF_UNLIKELY (_readBuffer[i] == '\n' ||
			    _readBuffer[i] == '\0') {
				retLength = i;

				if (i > 0 && _readBuffer[i - 1] == '\r')
					retLength--;

				ret = [OFString stringWithCString: _readBuffer
							 encoding: encoding
							   length: retLength];

				_readBuffer += i + 1;
				_readBufferLength -= i + 1;

				_waitingForDelimiter = false;
				return ret;
			}
		}
	}

	/* Read and see if we got a newline or \0 */
	pageSize = [OFSystemInfo pageSize];
	buffer = [self allocMemoryWithSize: pageSize];

	@try {
		if ([self lowlevelIsAtEndOfStream]) {


			if (_readBuffer == NULL) {
				_waitingForDelimiter = false;
				return nil;
			}

			retLength = _readBufferLength;

			if (retLength > 0 && _readBuffer[retLength - 1] == '\r')
				retLength--;

			ret = [OFString stringWithCString: _readBuffer
						 encoding: encoding
						   length: retLength];

			[self freeMemory: _readBufferMemory];
			_readBuffer = _readBufferMemory = NULL;
			_readBufferLength = 0;

			_waitingForDelimiter = false;
			return ret;
		}

		bufferLength = [self lowlevelReadIntoBuffer: buffer
						     length: pageSize];

		/* Look if there's a newline or \0 */
		for (size_t i = 0; i < bufferLength; i++) {
			if OF_UNLIKELY (buffer[i] == '\n' ||
			    buffer[i] == '\0') {
				retLength = _readBufferLength + i;
				retCString = [self
				    allocMemoryWithSize: retLength];

				if (_readBuffer != NULL)
					memcpy(retCString, _readBuffer,
					    _readBufferLength);
				memcpy(retCString + _readBufferLength,
				    buffer, i);

				if (retLength > 0 &&
				    retCString[retLength - 1] == '\r')
					retLength--;

				@try {
					char *rcs = retCString;
					size_t rl = retLength;

					ret = [OFString
					    stringWithCString: rcs
						     encoding: encoding
						       length: rl];
				} @catch (id e) {
					if (bufferLength > 0) {
						/*
						 * Append data to _readBuffer
						 * to prevent loss of data.
						 */
						readBuffer = [self
						    allocMemoryWithSize:
						    _readBufferLength +
						    bufferLength];

						memcpy(readBuffer, _readBuffer,
						    _readBufferLength);
						memcpy(readBuffer +
						    _readBufferLength,
						    buffer, bufferLength);

						[self freeMemory:
						    _readBufferMemory];
						_readBuffer = readBuffer;
						_readBufferMemory = readBuffer;
						_readBufferLength +=
						    bufferLength;
					}

					@throw e;
				} @finally {
					[self freeMemory: retCString];
				}

				readBuffer = [self
				    allocMemoryWithSize: bufferLength - i - 1];
				if (readBuffer != NULL)
					memcpy(readBuffer, buffer + i + 1,
					    bufferLength - i - 1);

				[self freeMemory: _readBufferMemory];
				_readBuffer = _readBufferMemory = readBuffer;
				_readBufferLength = bufferLength - i - 1;

				_waitingForDelimiter = false;
				return ret;
			}
		}

		/* There was no newline or \0 */
		if (bufferLength > 0) {
			readBuffer = [self allocMemoryWithSize:
			    _readBufferLength + bufferLength];

			memcpy(readBuffer, _readBuffer, _readBufferLength);
			memcpy(readBuffer + _readBufferLength,
			    buffer, bufferLength);

			[self freeMemory: _readBufferMemory];
			_readBuffer = _readBufferMemory = readBuffer;
			_readBufferLength += bufferLength;
		}
	} @finally {
		[self freeMemory: buffer];
	}

	_waitingForDelimiter = true;
	return nil;
}

- (OFString *)readLine
{
	return [self readLineWithEncoding: OF_STRING_ENCODING_UTF_8];
}

- (OFString *)readLineWithEncoding: (of_string_encoding_t)encoding
{
	OFString *line = nil;

	while ((line = [self tryReadLineWithEncoding: encoding]) == nil)
		if (self.atEndOfStream)
			return nil;

	return line;
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncReadLine
{
	[self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8
			    runLoopMode: of_run_loop_mode_default];
}

- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
{
	[self asyncReadLineWithEncoding: encoding
			    runLoopMode: of_run_loop_mode_default];
}

- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
		      runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadLineForStream: stream
				       encoding: encoding
					   mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
					  block: NULL
# endif
				       delegate: _delegate];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncReadLineWithBlock: (of_stream_async_read_line_block_t)block
{
	[self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8
			    runLoopMode: of_run_loop_mode_default
				  block: block];
}

- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
			    block: (of_stream_async_read_line_block_t)block
{
	[self asyncReadLineWithEncoding: encoding
			    runLoopMode: of_run_loop_mode_default
				  block: block];
}

- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
		      runLoopMode: (of_run_loop_mode_t)runLoopMode
			    block: (of_stream_async_read_line_block_t)block
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadLineForStream: stream
				       encoding: encoding
					   mode: runLoopMode
					  block: block
				       delegate: nil];
}
# endif
#endif

- (OFString *)tryReadLine
{
	return [self tryReadLineWithEncoding: OF_STRING_ENCODING_UTF_8];
}

- (OFString *)tryReadTillDelimiter: (OFString *)delimiter
			  encoding: (of_string_encoding_t)encoding
{
	const char *delimiterCString;
	size_t j, delimiterLength, pageSize, bufferLength, retLength;
	char *retCString, *buffer, *readBuffer;
	OFString *ret;

	delimiterCString = [delimiter cStringWithEncoding: encoding];
	delimiterLength = [delimiter cStringLengthWithEncoding: encoding];
	j = 0;

	if (delimiterLength == 0)







>
>
>
>
>
>
>
>






|
<




|
<

















|
<







|
|


|
<
|




<
|
>













|
<






|














|
<













|
<



|




|














|
<



|




|

















|



|





|
|














|



|





|
|

















<
|
<
<






<
|
<
|
<





<
|
<
|
<





<
|
<
|
<





<
|
<
|
<





<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





<
|
<
|
<





<
|
<
|
<





<
|
<
|
<





<
|
<
|
<





<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




|
<


|
<







|

|
<
<

<

>


|










|



|
<
|
<
|
<


|



<






|



|


|



|
<
<
|
<

|





|

|
|







|



















|



>
>














|














|
|
<












<
<
<

|

|






|
<

|







<
|








|


|
|




|










|
|





|




|








|


|













|
|


|


|


|
|














|

|
|



|
|


|



|
|
|















|



|


|
|







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
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
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	OFFreeMemory(_readBufferMemory);
	OFFreeMemory(_writeBuffer);

	[super dealloc];
}

- (bool)lowlevelIsAtEndOfStream
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{
	OF_UNRECOGNIZED_SELECTOR
}

- (id)copy
{
	return [self retain];
}

- (bool)isAtEndOfStream
{
	if (_readBufferLength > 0)
		return false;

	return [self lowlevelIsAtEndOfStream];
}

- (size_t)readIntoBuffer: (void *)buffer length: (size_t)length

{
	if (_readBufferLength == 0) {
		/*
		 * For small sizes, it is cheaper to read more and cache the
		 * remainder - even if that means more copying of data - than
		 * to do a syscall for every read.
		 */
		if (length < minReadSize) {
			char tmp[minReadSize], *readBuffer;
			size_t bytesRead;

			bytesRead = [self lowlevelReadIntoBuffer: tmp

							  length: minReadSize];

			if (bytesRead > length) {
				memcpy(buffer, tmp, length);


				readBuffer = OFAllocMemory(bytesRead - length,
				    1);
				memcpy(readBuffer, tmp + length,
				    bytesRead - length);

				_readBuffer = _readBufferMemory = readBuffer;
				_readBufferLength = bytesRead - length;

				return length;
			} else {
				memcpy(buffer, tmp, bytesRead);
				return bytesRead;
			}
		}

		return [self lowlevelReadIntoBuffer: buffer length: length];

	}

	if (length >= _readBufferLength) {
		size_t ret = _readBufferLength;
		memcpy(buffer, _readBuffer, _readBufferLength);

		OFFreeMemory(_readBufferMemory);
		_readBuffer = _readBufferMemory = NULL;
		_readBufferLength = 0;

		return ret;
	} else {
		memcpy(buffer, _readBuffer, length);

		_readBuffer += length;
		_readBufferLength -= length;

		return length;
	}
}

- (void)readIntoBuffer: (void *)buffer exactLength: (size_t)length

{
	size_t readLength = 0;

	while (readLength < length) {
		if (self.atEndOfStream)
			@throw [OFTruncatedDataException exception];

		readLength += [self readIntoBuffer: (char *)buffer + readLength
					    length: length - readLength];
	}
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length

{
	[self asyncReadIntoBuffer: buffer
			   length: length
		      runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (OFRunLoopMode)runLoopMode
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				     length: length
				       mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
				      block: NULL
# endif
				   delegate: _delegate];
}

- (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length

{
	[self asyncReadIntoBuffer: buffer
		      exactLength: length
		      runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (OFRunLoopMode)runLoopMode
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				exactLength: length
				       mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
				      block: NULL
# endif
				   delegate: _delegate];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		      block: (OFStreamAsyncReadBlock)block
{
	[self asyncReadIntoBuffer: buffer
			   length: length
		      runLoopMode: OFDefaultRunLoopMode
			    block: block];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (OFRunLoopMode)runLoopMode
		      block: (OFStreamAsyncReadBlock)block
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				     length: length
				       mode: runLoopMode
				      block: block
				   delegate: nil];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		      block: (OFStreamAsyncReadBlock)block
{
	[self asyncReadIntoBuffer: buffer
		      exactLength: length
		      runLoopMode: OFDefaultRunLoopMode
			    block: block];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (OFRunLoopMode)runLoopMode
		      block: (OFStreamAsyncReadBlock)block
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				exactLength: length
				       mode: runLoopMode
				      block: block
				   delegate: nil];
}
# endif
#endif

- (uint8_t)readInt8
{
	uint8_t ret;

	[self readIntoBuffer: (char *)&ret exactLength: 1];


	return ret;
}

- (uint16_t)readBigEndianInt16
{
	uint16_t ret;

	[self readIntoBuffer: (char *)&ret exactLength: 2];

	return OFFromBigEndian16(ret);

}

- (uint32_t)readBigEndianInt32
{
	uint32_t ret;

	[self readIntoBuffer: (char *)&ret exactLength: 4];

	return OFFromBigEndian32(ret);

}

- (uint64_t)readBigEndianInt64
{
	uint64_t ret;

	[self readIntoBuffer: (char *)&ret exactLength: 8];

	return OFFromBigEndian64(ret);

}

- (float)readBigEndianFloat
{
	float ret;

	[self readIntoBuffer: (char *)&ret exactLength: 4];

	return OFFromBigEndianFloat(ret);

}

- (double)readBigEndianDouble
{
	double ret;

	[self readIntoBuffer: (char *)&ret exactLength: 8];























	return OFFromBigEndianDouble(ret);




















































































}

- (uint16_t)readLittleEndianInt16
{
	uint16_t ret;

	[self readIntoBuffer: (char *)&ret exactLength: 2];

	return OFFromLittleEndian16(ret);

}

- (uint32_t)readLittleEndianInt32
{
	uint32_t ret;

	[self readIntoBuffer: (char *)&ret exactLength: 4];

	return OFFromLittleEndian32(ret);

}

- (uint64_t)readLittleEndianInt64
{
	uint64_t ret;

	[self readIntoBuffer: (char *)&ret exactLength: 8];

	return OFFromLittleEndian64(ret);

}

- (float)readLittleEndianFloat
{
	float ret;

	[self readIntoBuffer: (char *)&ret exactLength: 4];

	return OFFromLittleEndianFloat(ret);

}

- (double)readLittleEndianDouble
{
	double ret;

	[self readIntoBuffer: (char *)&ret exactLength: 8];























	return OFFromLittleEndianDouble(ret);




















































































}

- (OFData *)readDataWithCount: (size_t)count
{
	return [self readDataWithItemSize: 1 count: count];

}

- (OFData *)readDataWithItemSize: (size_t)itemSize count: (size_t)count

{
	OFData *ret;
	char *buffer;

	if OF_UNLIKELY (count > SIZE_MAX / itemSize)
		@throw [OFOutOfRangeException exception];

	buffer = OFAllocMemory(count, itemSize);
	@try {
		[self readIntoBuffer: buffer exactLength: count * itemSize];


		ret = [OFData dataWithItemsNoCopy: buffer

					    count: count
					 itemSize: itemSize
				     freeWhenDone: true];
	} @catch (id e) {
		OFFreeMemory(buffer);
		@throw e;
	}

	return ret;
}

- (OFData *)readDataUntilEndOfStream
{
	OFMutableData *data = [OFMutableData data];
	size_t pageSize = [OFSystemInfo pageSize];
	char *buffer = OFAllocMemory(1, pageSize);

	@try {
		while (!self.atEndOfStream) {
			size_t length =

			    [self readIntoBuffer: buffer length: pageSize];

			[data addItems: buffer count: length];

		}
	} @finally {
		OFFreeMemory(buffer);
	}

	[data makeImmutable];

	return data;
}

- (OFString *)readStringWithLength: (size_t)length
{
	return [self readStringWithLength: length
				 encoding: OFStringEncodingUTF8];
}

- (OFString *)readStringWithLength: (size_t)length
			  encoding: (OFStringEncoding)encoding
{
	OFString *ret;
	char *buffer = OFAllocMemory(length + 1, 1);
	buffer[length] = 0;

	@try {
		[self readIntoBuffer: buffer exactLength: length];


		ret = [OFString stringWithCString: buffer encoding: encoding];

	} @finally {
		OFFreeMemory(buffer);
	}

	return ret;
}

- (OFString *)tryReadLineWithEncoding: (OFStringEncoding)encoding
{
	size_t pageSize, bufferLength;
	char *buffer, *readBuffer;
	OFString *ret;

	/* Look if there's a line or \0 in our buffer */
	if (!_waitingForDelimiter && _readBuffer != NULL) {
		for (size_t i = 0; i < _readBufferLength; i++) {
			if OF_UNLIKELY (_readBuffer[i] == '\n' ||
			    _readBuffer[i] == '\0') {
				size_t retLength = i;

				if (i > 0 && _readBuffer[i - 1] == '\r')
					retLength--;

				ret = [OFString stringWithCString: _readBuffer
							 encoding: encoding
							   length: retLength];

				_readBuffer += i + 1;
				_readBufferLength -= i + 1;

				_waitingForDelimiter = false;
				return ret;
			}
		}
	}

	/* Read and see if we got a newline or \0 */
	pageSize = [OFSystemInfo pageSize];
	buffer = OFAllocMemory(1, pageSize);

	@try {
		if ([self lowlevelIsAtEndOfStream]) {
			size_t retLength;

			if (_readBuffer == NULL) {
				_waitingForDelimiter = false;
				return nil;
			}

			retLength = _readBufferLength;

			if (retLength > 0 && _readBuffer[retLength - 1] == '\r')
				retLength--;

			ret = [OFString stringWithCString: _readBuffer
						 encoding: encoding
						   length: retLength];

			OFFreeMemory(_readBufferMemory);
			_readBuffer = _readBufferMemory = NULL;
			_readBufferLength = 0;

			_waitingForDelimiter = false;
			return ret;
		}

		bufferLength = [self lowlevelReadIntoBuffer: buffer
						     length: pageSize];

		/* Look if there's a newline or \0 */
		for (size_t i = 0; i < bufferLength; i++) {
			if OF_UNLIKELY (buffer[i] == '\n' ||
			    buffer[i] == '\0') {
				size_t retLength = _readBufferLength + i;
				char *retCString = OFAllocMemory(retLength, 1);


				if (_readBuffer != NULL)
					memcpy(retCString, _readBuffer,
					    _readBufferLength);
				memcpy(retCString + _readBufferLength,
				    buffer, i);

				if (retLength > 0 &&
				    retCString[retLength - 1] == '\r')
					retLength--;

				@try {



					ret = [OFString
					    stringWithCString: retCString
						     encoding: encoding
						       length: retLength];
				} @catch (id e) {
					if (bufferLength > 0) {
						/*
						 * Append data to _readBuffer
						 * to prevent loss of data.
						 */
						readBuffer = OFAllocMemory(

						    _readBufferLength +
						    bufferLength, 1);

						memcpy(readBuffer, _readBuffer,
						    _readBufferLength);
						memcpy(readBuffer +
						    _readBufferLength,
						    buffer, bufferLength);


						OFFreeMemory(_readBufferMemory);
						_readBuffer = readBuffer;
						_readBufferMemory = readBuffer;
						_readBufferLength +=
						    bufferLength;
					}

					@throw e;
				} @finally {
					OFFreeMemory(retCString);
				}

				readBuffer = OFAllocMemory(bufferLength - i - 1,
				    1);
				if (readBuffer != NULL)
					memcpy(readBuffer, buffer + i + 1,
					    bufferLength - i - 1);

				OFFreeMemory(_readBufferMemory);
				_readBuffer = _readBufferMemory = readBuffer;
				_readBufferLength = bufferLength - i - 1;

				_waitingForDelimiter = false;
				return ret;
			}
		}

		/* There was no newline or \0 */
		if (bufferLength > 0) {
			readBuffer = OFAllocMemory(
			    _readBufferLength + bufferLength, 1);

			memcpy(readBuffer, _readBuffer, _readBufferLength);
			memcpy(readBuffer + _readBufferLength,
			    buffer, bufferLength);

			OFFreeMemory(_readBufferMemory);
			_readBuffer = _readBufferMemory = readBuffer;
			_readBufferLength += bufferLength;
		}
	} @finally {
		OFFreeMemory(buffer);
	}

	_waitingForDelimiter = true;
	return nil;
}

- (OFString *)readLine
{
	return [self readLineWithEncoding: OFStringEncodingUTF8];
}

- (OFString *)readLineWithEncoding: (OFStringEncoding)encoding
{
	OFString *line = nil;

	while ((line = [self tryReadLineWithEncoding: encoding]) == nil)
		if (self.atEndOfStream)
			return nil;

	return line;
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncReadLine
{
	[self asyncReadLineWithEncoding: OFStringEncodingUTF8
			    runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding
{
	[self asyncReadLineWithEncoding: encoding
			    runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding
		      runLoopMode: (OFRunLoopMode)runLoopMode
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadLineForStream: stream
				       encoding: encoding
					   mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
					  block: NULL
# endif
				       delegate: _delegate];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncReadLineWithBlock: (OFStreamAsyncReadLineBlock)block
{
	[self asyncReadLineWithEncoding: OFStringEncodingUTF8
			    runLoopMode: OFDefaultRunLoopMode
				  block: block];
}

- (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding
			    block: (OFStreamAsyncReadLineBlock)block
{
	[self asyncReadLineWithEncoding: encoding
			    runLoopMode: OFDefaultRunLoopMode
				  block: block];
}

- (void)asyncReadLineWithEncoding: (OFStringEncoding)encoding
		      runLoopMode: (OFRunLoopMode)runLoopMode
			    block: (OFStreamAsyncReadLineBlock)block
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadLineForStream: stream
				       encoding: encoding
					   mode: runLoopMode
					  block: block
				       delegate: nil];
}
# endif
#endif

- (OFString *)tryReadLine
{
	return [self tryReadLineWithEncoding: OFStringEncodingUTF8];
}

- (OFString *)tryReadTillDelimiter: (OFString *)delimiter
			  encoding: (OFStringEncoding)encoding
{
	const char *delimiterCString;
	size_t j, delimiterLength, pageSize, bufferLength;
	char *buffer, *readBuffer;
	OFString *ret;

	delimiterCString = [delimiter cStringWithEncoding: encoding];
	delimiterLength = [delimiter cStringLengthWithEncoding: encoding];
	j = 0;

	if (delimiterLength == 0)
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
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
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687

1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
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
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
				return ret;
			}
		}
	}

	/* Read and see if we got a delimiter or \0 */
	pageSize = [OFSystemInfo pageSize];
	buffer = [self allocMemoryWithSize: pageSize];

	@try {
		if ([self lowlevelIsAtEndOfStream]) {
			if (_readBuffer == NULL) {
				_waitingForDelimiter = false;
				return nil;
			}

			ret = [OFString stringWithCString: _readBuffer
						 encoding: encoding
						   length: _readBufferLength];

			[self freeMemory: _readBufferMemory];
			_readBuffer = _readBufferMemory = NULL;
			_readBufferLength = 0;

			_waitingForDelimiter = false;
			return ret;
		}

		bufferLength = [self lowlevelReadIntoBuffer: buffer
						     length: pageSize];

		/* Look if there's a delimiter or \0 */
		for (size_t i = 0; i < bufferLength; i++) {
			if (buffer[i] != delimiterCString[j++])
				j = 0;

			if (j == delimiterLength || buffer[i] == '\0') {



				if (buffer[i] == '\0')
					delimiterLength = 1;

				retLength = _readBufferLength + i + 1 -
				    delimiterLength;
				retCString = [self
				    allocMemoryWithSize: retLength];

				if (_readBuffer != NULL &&
				    _readBufferLength <= retLength)
					memcpy(retCString, _readBuffer,
					    _readBufferLength);
				else if (_readBuffer != NULL)
					memcpy(retCString, _readBuffer,
					    retLength);
				if (i >= delimiterLength)
					memcpy(retCString + _readBufferLength,
					    buffer, i + 1 - delimiterLength);

				@try {
					char *rcs = retCString;
					size_t rl = retLength;

					ret = [OFString
					    stringWithCString: rcs
						     encoding: encoding
						       length: rl];
				} @catch (id e) {
					if (bufferLength > 0) {
						/*
						 * Append data to _readBuffer
						 * to prevent loss of data.
						 */
						readBuffer = [self
						    allocMemoryWithSize:
						    _readBufferLength +
						    bufferLength];

						memcpy(readBuffer, _readBuffer,
						    _readBufferLength);
						memcpy(readBuffer +
						    _readBufferLength,
						    buffer, bufferLength);

						[self freeMemory:
						    _readBufferMemory];
						_readBuffer = readBuffer;
						_readBufferMemory = readBuffer;
						_readBufferLength +=
						    bufferLength;
					}

					@throw e;
				} @finally {
					[self freeMemory: retCString];
				}

				readBuffer = [self allocMemoryWithSize:
				    bufferLength - i - 1];

				if (readBuffer != NULL)
					memcpy(readBuffer, buffer + i + 1,
					    bufferLength - i - 1);

				[self freeMemory: _readBufferMemory];
				_readBuffer = _readBufferMemory = readBuffer;
				_readBufferLength = bufferLength - i - 1;

				_waitingForDelimiter = false;
				return ret;
			}
		}

		/* Neither the delimiter nor \0 was found */
		if (bufferLength > 0) {
			readBuffer = [self allocMemoryWithSize:
			    _readBufferLength + bufferLength];

			memcpy(readBuffer, _readBuffer, _readBufferLength);
			memcpy(readBuffer + _readBufferLength,
			    buffer, bufferLength);

			[self freeMemory: _readBufferMemory];
			_readBuffer = _readBufferMemory = readBuffer;
			_readBufferLength += bufferLength;
		}
	} @finally {
		[self freeMemory: buffer];
	}

	_waitingForDelimiter = true;
	return nil;
}


- (OFString *)readTillDelimiter: (OFString *)delimiter
{
	return [self readTillDelimiter: delimiter
			      encoding: OF_STRING_ENCODING_UTF_8];
}

- (OFString *)readTillDelimiter: (OFString *)delimiter
		       encoding: (of_string_encoding_t)encoding
{
	OFString *ret = nil;

	while ((ret = [self tryReadTillDelimiter: delimiter
					encoding: encoding]) == nil)
		if (self.atEndOfStream)
			return nil;

	return ret;
}

- (OFString *)tryReadTillDelimiter: (OFString *)delimiter
{
	return [self tryReadTillDelimiter: delimiter
				 encoding: OF_STRING_ENCODING_UTF_8];
}

- (void)flushWriteBuffer
{


	if (_writeBuffer == NULL)
		return;

	[self lowlevelWriteBuffer: _writeBuffer
			   length: _writeBufferLength];





	[self freeMemory: _writeBuffer];
	_writeBuffer = NULL;
	_writeBufferLength = 0;
}


- (size_t)writeBuffer: (const void *)buffer















	       length: (size_t)length
{
	if (!_buffersWrites) {
		size_t bytesWritten = [self lowlevelWriteBuffer: buffer
							 length: length];

		if (_canBlock && bytesWritten < length)
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: length
				   bytesWritten: bytesWritten
					  errNo: 0];

		return bytesWritten;
	} else {


		_writeBuffer = [self resizeMemory: _writeBuffer

					     size: _writeBufferLength + length];
		memcpy(_writeBuffer + _writeBufferLength, buffer, length);
		_writeBufferLength += length;

		return length;
	}
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncWriteData: (OFData *)data
{
	[self asyncWriteData: data
		 runLoopMode: of_run_loop_mode_default];
}

- (void)asyncWriteData: (OFData *)data
	   runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
					data: data
					mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
				       block: NULL
# endif
				    delegate: _delegate];
}

- (void)asyncWriteString: (OFString *)string
{
	[self asyncWriteString: string
		      encoding: OF_STRING_ENCODING_UTF_8
		   runLoopMode: of_run_loop_mode_default];
}

- (void)asyncWriteString: (OFString *)string
		encoding: (of_string_encoding_t)encoding
{
	[self asyncWriteString: string
		      encoding: encoding
		   runLoopMode: of_run_loop_mode_default];
}

- (void)asyncWriteString: (OFString *)string
		encoding: (of_string_encoding_t)encoding
	     runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
				      string: string
				    encoding: encoding
					mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
				       block: NULL
# endif
				    delegate: _delegate];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncWriteData: (OFData *)data
		 block: (of_stream_async_write_data_block_t)block
{
	[self asyncWriteData: data
		 runLoopMode: of_run_loop_mode_default
		       block: block];
}

- (void)asyncWriteData: (OFData *)data
	   runLoopMode: (of_run_loop_mode_t)runLoopMode
		 block: (of_stream_async_write_data_block_t)block
{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
					data: data
					mode: runLoopMode
				       block: block
				    delegate: nil];
}

- (void)asyncWriteString: (OFString *)string
		   block: (of_stream_async_write_string_block_t)block
{
	[self asyncWriteString: string
		      encoding: OF_STRING_ENCODING_UTF_8
		   runLoopMode: of_run_loop_mode_default
			 block: block];
}

- (void)asyncWriteString: (OFString *)string
		encoding: (of_string_encoding_t)encoding
		   block: (of_stream_async_write_string_block_t)block
{
	[self asyncWriteString: string
		      encoding: encoding
		   runLoopMode: of_run_loop_mode_default
			 block: block];
}

- (void)asyncWriteString: (OFString *)string
		encoding: (of_string_encoding_t)encoding
	     runLoopMode: (of_run_loop_mode_t)runLoopMode
		   block: (of_stream_async_write_string_block_t)block
{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
				      string: string
				    encoding: encoding
					mode: runLoopMode
				       block: block
				    delegate: nil];
}
# endif
#endif

- (void)writeInt8: (uint8_t)int8
{
	[self writeBuffer: (char *)&int8
		   length: 1];
}

- (void)writeBigEndianInt16: (uint16_t)int16
{
	int16 = OF_BSWAP16_IF_LE(int16);

	[self writeBuffer: (char *)&int16
		   length: 2];
}

- (void)writeBigEndianInt32: (uint32_t)int32
{
	int32 = OF_BSWAP32_IF_LE(int32);

	[self writeBuffer: (char *)&int32
		   length: 4];
}

- (void)writeBigEndianInt64: (uint64_t)int64
{
	int64 = OF_BSWAP64_IF_LE(int64);

	[self writeBuffer: (char *)&int64
		   length: 8];
}

- (void)writeBigEndianFloat: (float)float_
{
	float_ = OF_BSWAP_FLOAT_IF_LE(float_);

	[self writeBuffer: (char *)&float_
		   length: 4];
}

- (void)writeBigEndianDouble: (double)double_
{
	double_ = OF_BSWAP_DOUBLE_IF_LE(double_);

	[self writeBuffer: (char *)&double_
		   length: 8];
}

- (size_t)writeBigEndianInt16s: (const uint16_t *)buffer
			 count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint16_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint16_t);

#ifdef OF_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	uint16_t *tmp = [self allocMemoryWithSize: sizeof(uint16_t)
					    count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP16(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeBigEndianInt32s: (const uint32_t *)buffer
			 count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint32_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint32_t);

#ifdef OF_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	uint32_t *tmp = [self allocMemoryWithSize: sizeof(uint32_t)
					    count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP32(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeBigEndianInt64s: (const uint64_t *)buffer
			 count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint64_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint64_t);

#ifdef OF_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	uint64_t *tmp = [self allocMemoryWithSize: sizeof(uint64_t)
					    count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP64(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeBigEndianFloats: (const float *)buffer
			 count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(float))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(float);

#ifdef OF_FLOAT_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	float *tmp = [self allocMemoryWithSize: sizeof(float)
					 count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP_FLOAT(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeBigEndianDoubles: (const double *)buffer
			  count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(double))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(double);

#ifdef OF_FLOAT_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	double *tmp = [self allocMemoryWithSize: sizeof(double)
					  count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP_DOUBLE(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (void)writeLittleEndianInt16: (uint16_t)int16
{
	int16 = OF_BSWAP16_IF_BE(int16);

	[self writeBuffer: (char *)&int16
		   length: 2];
}

- (void)writeLittleEndianInt32: (uint32_t)int32
{
	int32 = OF_BSWAP32_IF_BE(int32);

	[self writeBuffer: (char *)&int32
		   length: 4];
}

- (void)writeLittleEndianInt64: (uint64_t)int64
{
	int64 = OF_BSWAP64_IF_BE(int64);

	[self writeBuffer: (char *)&int64
		   length: 8];
}

- (void)writeLittleEndianFloat: (float)float_
{
	float_ = OF_BSWAP_FLOAT_IF_BE(float_);

	[self writeBuffer: (char *)&float_
		   length: 4];
}

- (void)writeLittleEndianDouble: (double)double_
{
	double_ = OF_BSWAP_DOUBLE_IF_BE(double_);

	[self writeBuffer: (char *)&double_
		   length: 8];
}

- (size_t)writeLittleEndianInt16s: (const uint16_t *)buffer
			    count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint16_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint16_t);

#ifndef OF_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	uint16_t *tmp = [self allocMemoryWithSize: sizeof(uint16_t)
					    count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP16(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeLittleEndianInt32s: (const uint32_t *)buffer
			    count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint32_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint32_t);

#ifndef OF_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	uint32_t *tmp = [self allocMemoryWithSize: sizeof(uint32_t)
					    count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP32(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeLittleEndianInt64s: (const uint64_t *)buffer
			    count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(uint64_t))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(uint64_t);

#ifndef OF_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	uint64_t *tmp = [self allocMemoryWithSize: sizeof(uint64_t)
					    count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP64(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeLittleEndianFloats: (const float *)buffer
			    count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(float))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(float);

#ifndef OF_FLOAT_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	float *tmp = [self allocMemoryWithSize: sizeof(float)
					 count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP_FLOAT(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeLittleEndianDoubles: (const double *)buffer
			     count: (size_t)count
{
	size_t size;

	if OF_UNLIKELY (count > SIZE_MAX / sizeof(double))
		@throw [OFOutOfRangeException exception];

	size = count * sizeof(double);

#ifndef OF_FLOAT_BIG_ENDIAN
	[self writeBuffer: buffer
		   length: size];
#else
	double *tmp = [self allocMemoryWithSize: sizeof(double)
					  count: count];

	@try {
		for (size_t i = 0; i < count; i++)
			tmp[i] = OF_BSWAP_DOUBLE(buffer[i]);

		[self writeBuffer: tmp
			   length: size];
	} @finally {
		[self freeMemory: tmp];
	}
#endif

	return size;
}

- (size_t)writeData: (OFData *)data
{
	void *pool;
	size_t length;

	if (data == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();
	length = data.count * data.itemSize;


	[self writeBuffer: data.items
		   length: length];

	objc_autoreleasePoolPop(pool);

	return length;
}

- (size_t)writeString: (OFString *)string
{
	return [self writeString: string
			encoding: OF_STRING_ENCODING_UTF_8];
}

- (size_t)writeString: (OFString *)string
	     encoding: (of_string_encoding_t)encoding
{
	void *pool;
	size_t length;

	if (string == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();
	length = [string cStringLengthWithEncoding: encoding];

	[self writeBuffer: [string cStringWithEncoding: encoding]
		   length: length];

	objc_autoreleasePoolPop(pool);

	return length;
}

- (size_t)writeLine: (OFString *)string
{
	return [self writeLine: string
		      encoding: OF_STRING_ENCODING_UTF_8];
}

- (size_t)writeLine: (OFString *)string
	   encoding: (of_string_encoding_t)encoding
{
	size_t stringLength = [string cStringLengthWithEncoding: encoding];
	char *buffer;

	buffer = [self allocMemoryWithSize: stringLength + 1];

	@try {
		memcpy(buffer, [string cStringWithEncoding: encoding],
		    stringLength);
		buffer[stringLength] = '\n';

		[self writeBuffer: buffer
			   length: stringLength + 1];
	} @finally {
		[self freeMemory: buffer];
	}

	return stringLength + 1;
}

- (size_t)writeFormat: (OFConstantString *)format, ...
{
	va_list arguments;
	size_t ret;

	va_start(arguments, format);
	ret = [self writeFormat: format
		      arguments: arguments];
	va_end(arguments);

	return ret;
}

- (size_t)writeFormat: (OFConstantString *)format
	    arguments: (va_list)arguments
{
	char *UTF8String;
	int length;

	if (format == nil)
		@throw [OFInvalidArgumentException exception];

	if ((length = of_vasprintf(&UTF8String, format.UTF8String,
	    arguments)) == -1)
		@throw [OFInvalidFormatException exception];

	@try {
		[self writeBuffer: UTF8String
			   length: length];
	} @finally {
		free(UTF8String);
	}

	return length;
}

- (bool)hasDataInReadBuffer
{
	return (_readBufferLength > 0);
}








|












|
















>
>
>





|
<













<
<
<

|

|






|
<

|







<
|








|


<
|
>




|










|
|





|




|










|



|














|


|

>
>

|

|
|

>
>
>
>
|
|
|
|
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|





|





<
<

>
>
|
>
|


<
<






|
<


|
<
















|
|



|



|



|
|















|
<


|




|
|












|


|
|




|
|



|




|
|
|
















|
<




<
|
|
<




<
|
|
<




<
|
|
<




<
|
|
<




<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




<
|
|
<




<
|
|
<




<
|
|
<




<
|
|
<




<
|
|
<


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|








<

>
|
<


|
<
|
<
|

|
<


|
<














|
<
|
<
|

|
<


|
<




|






|
<

|

|
<
|
<
|


<


|
<

|
<
|
<
|
<







|




|
<



<
<







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

1183
1184
1185


1186
1187
1188
1189
1190
1191
1192
				return ret;
			}
		}
	}

	/* Read and see if we got a delimiter or \0 */
	pageSize = [OFSystemInfo pageSize];
	buffer = OFAllocMemory(1, pageSize);

	@try {
		if ([self lowlevelIsAtEndOfStream]) {
			if (_readBuffer == NULL) {
				_waitingForDelimiter = false;
				return nil;
			}

			ret = [OFString stringWithCString: _readBuffer
						 encoding: encoding
						   length: _readBufferLength];

			OFFreeMemory(_readBufferMemory);
			_readBuffer = _readBufferMemory = NULL;
			_readBufferLength = 0;

			_waitingForDelimiter = false;
			return ret;
		}

		bufferLength = [self lowlevelReadIntoBuffer: buffer
						     length: pageSize];

		/* Look if there's a delimiter or \0 */
		for (size_t i = 0; i < bufferLength; i++) {
			if (buffer[i] != delimiterCString[j++])
				j = 0;

			if (j == delimiterLength || buffer[i] == '\0') {
				size_t retLength;
				char *retCString;

				if (buffer[i] == '\0')
					delimiterLength = 1;

				retLength = _readBufferLength + i + 1 -
				    delimiterLength;
				retCString = OFAllocMemory(retLength, 1);


				if (_readBuffer != NULL &&
				    _readBufferLength <= retLength)
					memcpy(retCString, _readBuffer,
					    _readBufferLength);
				else if (_readBuffer != NULL)
					memcpy(retCString, _readBuffer,
					    retLength);
				if (i >= delimiterLength)
					memcpy(retCString + _readBufferLength,
					    buffer, i + 1 - delimiterLength);

				@try {



					ret = [OFString
					    stringWithCString: retCString
						     encoding: encoding
						       length: retLength];
				} @catch (id e) {
					if (bufferLength > 0) {
						/*
						 * Append data to _readBuffer
						 * to prevent loss of data.
						 */
						readBuffer = OFAllocMemory(

						    _readBufferLength +
						    bufferLength, 1);

						memcpy(readBuffer, _readBuffer,
						    _readBufferLength);
						memcpy(readBuffer +
						    _readBufferLength,
						    buffer, bufferLength);


						OFFreeMemory(_readBufferMemory);
						_readBuffer = readBuffer;
						_readBufferMemory = readBuffer;
						_readBufferLength +=
						    bufferLength;
					}

					@throw e;
				} @finally {
					OFFreeMemory(retCString);
				}


				readBuffer = OFAllocMemory(bufferLength - i - 1,
				    1);
				if (readBuffer != NULL)
					memcpy(readBuffer, buffer + i + 1,
					    bufferLength - i - 1);

				OFFreeMemory(_readBufferMemory);
				_readBuffer = _readBufferMemory = readBuffer;
				_readBufferLength = bufferLength - i - 1;

				_waitingForDelimiter = false;
				return ret;
			}
		}

		/* Neither the delimiter nor \0 was found */
		if (bufferLength > 0) {
			readBuffer = OFAllocMemory(
			    _readBufferLength + bufferLength, 1);

			memcpy(readBuffer, _readBuffer, _readBufferLength);
			memcpy(readBuffer + _readBufferLength,
			    buffer, bufferLength);

			OFFreeMemory(_readBufferMemory);
			_readBuffer = _readBufferMemory = readBuffer;
			_readBufferLength += bufferLength;
		}
	} @finally {
		OFFreeMemory(buffer);
	}

	_waitingForDelimiter = true;
	return nil;
}


- (OFString *)readTillDelimiter: (OFString *)delimiter
{
	return [self readTillDelimiter: delimiter
			      encoding: OFStringEncodingUTF8];
}

- (OFString *)readTillDelimiter: (OFString *)delimiter
		       encoding: (OFStringEncoding)encoding
{
	OFString *ret = nil;

	while ((ret = [self tryReadTillDelimiter: delimiter
					encoding: encoding]) == nil)
		if (self.atEndOfStream)
			return nil;

	return ret;
}

- (OFString *)tryReadTillDelimiter: (OFString *)delimiter
{
	return [self tryReadTillDelimiter: delimiter
				 encoding: OFStringEncodingUTF8];
}

- (bool)flushWriteBuffer
{
	size_t bytesWritten;

	if (_writeBuffer == NULL)
		return true;

	bytesWritten = [self lowlevelWriteBuffer: _writeBuffer
					  length: _writeBufferLength];

	if (bytesWritten == 0)
		return false;

	if (bytesWritten == _writeBufferLength) {
		OFFreeMemory(_writeBuffer);
		_writeBuffer = NULL;
		_writeBufferLength = 0;

		return true;
	}

	OFEnsure(bytesWritten <= _writeBufferLength);

	memmove(_writeBuffer, _writeBuffer + bytesWritten,
	    _writeBufferLength - bytesWritten);
	_writeBufferLength -= bytesWritten;
	@try {
		_writeBuffer = OFResizeMemory(_writeBuffer,
		    _writeBufferLength, 1);
	} @catch (OFOutOfMemoryException *e) {
		/* We don't care, as we only made it smaller. */
	}

	return false;
}

- (void)writeBuffer: (const void *)buffer length: (size_t)length
{
	if (!_buffersWrites) {
		size_t bytesWritten = [self lowlevelWriteBuffer: buffer
							 length: length];

		if (bytesWritten < length)
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: length
				   bytesWritten: bytesWritten
					  errNo: 0];


	} else {
		if (SIZE_MAX - _writeBufferLength < length)
			@throw [OFOutOfRangeException exception];

		_writeBuffer = OFResizeMemory(_writeBuffer,
		    _writeBufferLength + length, 1);
		memcpy(_writeBuffer + _writeBufferLength, buffer, length);
		_writeBufferLength += length;


	}
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncWriteData: (OFData *)data
{
	[self asyncWriteData: data runLoopMode: OFDefaultRunLoopMode];

}

- (void)asyncWriteData: (OFData *)data runLoopMode: (OFRunLoopMode)runLoopMode

{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
					data: data
					mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
				       block: NULL
# endif
				    delegate: _delegate];
}

- (void)asyncWriteString: (OFString *)string
{
	[self asyncWriteString: string
		      encoding: OFStringEncodingUTF8
		   runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncWriteString: (OFString *)string
		encoding: (OFStringEncoding)encoding
{
	[self asyncWriteString: string
		      encoding: encoding
		   runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncWriteString: (OFString *)string
		encoding: (OFStringEncoding)encoding
	     runLoopMode: (OFRunLoopMode)runLoopMode
{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
				      string: string
				    encoding: encoding
					mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
				       block: NULL
# endif
				    delegate: _delegate];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncWriteData: (OFData *)data block: (OFStreamAsyncWriteDataBlock)block

{
	[self asyncWriteData: data
		 runLoopMode: OFDefaultRunLoopMode
		       block: block];
}

- (void)asyncWriteData: (OFData *)data
	   runLoopMode: (OFRunLoopMode)runLoopMode
		 block: (OFStreamAsyncWriteDataBlock)block
{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
					data: data
					mode: runLoopMode
				       block: block
				    delegate: nil];
}

- (void)asyncWriteString: (OFString *)string
		   block: (OFStreamAsyncWriteStringBlock)block
{
	[self asyncWriteString: string
		      encoding: OFStringEncodingUTF8
		   runLoopMode: OFDefaultRunLoopMode
			 block: block];
}

- (void)asyncWriteString: (OFString *)string
		encoding: (OFStringEncoding)encoding
		   block: (OFStreamAsyncWriteStringBlock)block
{
	[self asyncWriteString: string
		      encoding: encoding
		   runLoopMode: OFDefaultRunLoopMode
			 block: block];
}

- (void)asyncWriteString: (OFString *)string
		encoding: (OFStringEncoding)encoding
	     runLoopMode: (OFRunLoopMode)runLoopMode
		   block: (OFStreamAsyncWriteStringBlock)block
{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
				      string: string
				    encoding: encoding
					mode: runLoopMode
				       block: block
				    delegate: nil];
}
# endif
#endif

- (void)writeInt8: (uint8_t)int8
{
	[self writeBuffer: (char *)&int8 length: 1];

}

- (void)writeBigEndianInt16: (uint16_t)int16
{

	int16 = OFToBigEndian16(int16);
	[self writeBuffer: (char *)&int16 length: 2];

}

- (void)writeBigEndianInt32: (uint32_t)int32
{

	int32 = OFToBigEndian32(int32);
	[self writeBuffer: (char *)&int32 length: 4];

}

- (void)writeBigEndianInt64: (uint64_t)int64
{

	int64 = OFToBigEndian64(int64);
	[self writeBuffer: (char *)&int64 length: 8];

}

- (void)writeBigEndianFloat: (float)float_
{

	float_ = OFToBigEndianFloat(float_);
	[self writeBuffer: (char *)&float_ length: 4];

}

- (void)writeBigEndianDouble: (double)double_
{

	double_ = OFToBigEndianDouble(double_);
	[self writeBuffer: (char *)&double_ length: 8];




























































































































































}

- (void)writeLittleEndianInt16: (uint16_t)int16
{

	int16 = OFToLittleEndian16(int16);
	[self writeBuffer: (char *)&int16 length: 2];

}

- (void)writeLittleEndianInt32: (uint32_t)int32
{

	int32 = OFToLittleEndian32(int32);
	[self writeBuffer: (char *)&int32 length: 4];

}

- (void)writeLittleEndianInt64: (uint64_t)int64
{

	int64 = OFToLittleEndian64(int64);
	[self writeBuffer: (char *)&int64 length: 8];

}

- (void)writeLittleEndianFloat: (float)float_
{

	float_ = OFToLittleEndianFloat(float_);
	[self writeBuffer: (char *)&float_ length: 4];

}

- (void)writeLittleEndianDouble: (double)double_
{

	double_ = OFToLittleEndianDouble(double_);
	[self writeBuffer: (char *)&double_ length: 8];

}




























































































































































- (void)writeData: (OFData *)data
{
	void *pool;
	size_t length;

	if (data == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();


	length = data.count * data.itemSize;
	[self writeBuffer: data.items length: length];


	objc_autoreleasePoolPop(pool);
}



- (void)writeString: (OFString *)string
{
	[self writeString: string encoding: OFStringEncodingUTF8];

}

- (void)writeString: (OFString *)string encoding: (OFStringEncoding)encoding

{
	void *pool;
	size_t length;

	if (string == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();
	length = [string cStringLengthWithEncoding: encoding];

	[self writeBuffer: [string cStringWithEncoding: encoding]
		   length: length];

	objc_autoreleasePoolPop(pool);
}



- (void)writeLine: (OFString *)string
{
	[self writeLine: string encoding: OFStringEncodingUTF8];

}

- (void)writeLine: (OFString *)string encoding: (OFStringEncoding)encoding

{
	size_t stringLength = [string cStringLengthWithEncoding: encoding];
	char *buffer;

	buffer = OFAllocMemory(stringLength + 1, 1);

	@try {
		memcpy(buffer, [string cStringWithEncoding: encoding],
		    stringLength);
		buffer[stringLength] = '\n';

		[self writeBuffer: buffer length: stringLength + 1];

	} @finally {
		OFFreeMemory(buffer);
	}
}



- (void)writeFormat: (OFConstantString *)format, ...
{
	va_list arguments;


	va_start(arguments, format);
	[self writeFormat: format arguments: arguments];

	va_end(arguments);
}



- (void)writeFormat: (OFConstantString *)format arguments: (va_list)arguments

{
	char *UTF8String;
	int length;

	if (format == nil)
		@throw [OFInvalidArgumentException exception];

	if ((length = OFVASPrintF(&UTF8String, format.UTF8String,
	    arguments)) == -1)
		@throw [OFInvalidFormatException exception];

	@try {
		[self writeBuffer: UTF8String length: length];

	} @finally {
		free(UTF8String);
	}


}

- (bool)hasDataInReadBuffer
{
	return (_readBufferLength > 0);
}

1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
	OF_UNRECOGNIZED_SELECTOR
}

#ifdef OF_HAVE_SOCKETS
- (void)cancelAsyncRequests
{
	[OFRunLoop of_cancelAsyncRequestsForObject: self
					      mode: of_run_loop_mode_default];
}
#endif

- (void)unreadFromBuffer: (const void *)buffer
		  length: (size_t)length
{
	char *readBuffer;

	if (length > SIZE_MAX - _readBufferLength)
		@throw [OFOutOfRangeException exception];

	readBuffer = [self allocMemoryWithSize: _readBufferLength + length];
	memcpy(readBuffer, buffer, length);
	memcpy(readBuffer + length, _readBuffer, _readBufferLength);

	[self freeMemory: _readBufferMemory];
	_readBuffer = _readBufferMemory = readBuffer;
	_readBufferLength += length;
}

- (void)close
{
	[self freeMemory: _readBufferMemory];
	_readBuffer = _readBufferMemory = NULL;
	_readBufferLength = 0;

	[self freeMemory: _writeBuffer];
	_writeBuffer = NULL;
	_writeBufferLength = 0;
	_buffersWrites = false;

	_waitingForDelimiter = false;
}
@end







|



|
<






|



|






|



|







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
	OF_UNRECOGNIZED_SELECTOR
}

#ifdef OF_HAVE_SOCKETS
- (void)cancelAsyncRequests
{
	[OFRunLoop of_cancelAsyncRequestsForObject: self
					      mode: OFDefaultRunLoopMode];
}
#endif

- (void)unreadFromBuffer: (const void *)buffer length: (size_t)length

{
	char *readBuffer;

	if (length > SIZE_MAX - _readBufferLength)
		@throw [OFOutOfRangeException exception];

	readBuffer = OFAllocMemory(_readBufferLength + length, 1);
	memcpy(readBuffer, buffer, length);
	memcpy(readBuffer + length, _readBuffer, _readBufferLength);

	OFFreeMemory(_readBufferMemory);
	_readBuffer = _readBufferMemory = readBuffer;
	_readBufferLength += length;
}

- (void)close
{
	OFFreeMemory(_readBufferMemory);
	_readBuffer = _readBufferMemory = NULL;
	_readBufferLength = 0;

	OFFreeMemory(_writeBuffer);
	_writeBuffer = NULL;
	_writeBufferLength = 0;
	_buffersWrites = false;

	_waitingForDelimiter = false;
}
@end

Modified src/OFStreamSocket+Private.h from [c1cf7136f2] to [a23e5c50d7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFStreamSocket.h from [12e4e63247] to [5c1a8b3304].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFStream.h"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFStreamSocket;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket accepted a connection.
 *
 * @param acceptedSocket The socket which has been accepted
 * @param exception An exception which occurred while accepting the socket or
 *		    `nil` on success
 * @return A bool whether the same block should be used for the next incoming
 *	   connection
 */
typedef bool (^of_stream_socket_async_accept_block_t)(
    OFStreamSocket *acceptedSocket, id _Nullable exception);
#endif

/**
 * @protocol OFStreamSocketDelegate OFStreamSocket.h ObjFW/OFStreamSocket.h
 *
 * A delegate for OFStreamSocket.
 */

<
<
|














<
|

















|
|







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


 * 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 "OFStream.h"

#import "OFSocket.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFStreamSocket;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket accepted a connection.
 *
 * @param acceptedSocket The socket which has been accepted
 * @param exception An exception which occurred while accepting the socket or
 *		    `nil` on success
 * @return A bool whether the same block should be used for the next incoming
 *	   connection
 */
typedef bool (^OFStreamSocketAsyncAcceptBlock)(OFStreamSocket *acceptedSocket,
    id _Nullable exception);
#endif

/**
 * @protocol OFStreamSocketDelegate OFStreamSocket.h ObjFW/OFStreamSocket.h
 *
 * A delegate for OFStreamSocket.
 */
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
 * @class OFStreamSocket OFStreamSocket.h ObjFW/OFStreamSocket.h
 *
 * @brief A class which provides methods to create and use stream sockets.
 */
@interface OFStreamSocket: OFStream <OFReadyForReadingObserving,
    OFReadyForWritingObserving>
{
	of_socket_t _socket;
	bool _atEndOfStream, _listening;
	of_socket_address_t _remoteAddress;
	OF_RESERVE_IVARS(OFStreamSocket, 4)
}

/**
 * @brief Whether the socket is a listening socket.
 */
@property (readonly, nonatomic, getter=isListening) bool listening;

/**
 * @brief The remote address.
 *
 * @note This only works for accepted sockets!
 */
@property (readonly, nonatomic) const of_socket_address_t *remoteAddress;

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







|

|













|







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
 * @class OFStreamSocket OFStreamSocket.h ObjFW/OFStreamSocket.h
 *
 * @brief A class which provides methods to create and use stream sockets.
 */
@interface OFStreamSocket: OFStream <OFReadyForReadingObserving,
    OFReadyForWritingObserving>
{
	OFSocketHandle _socket;
	bool _atEndOfStream, _listening;
	OFSocketAddress _remoteAddress;
	OF_RESERVE_IVARS(OFStreamSocket, 4)
}

/**
 * @brief Whether the socket is a listening socket.
 */
@property (readonly, nonatomic, getter=isListening) bool listening;

/**
 * @brief The remote address.
 *
 * @note This only works for accepted sockets!
 */
@property (readonly, nonatomic) const OFSocketAddress *remoteAddress;

/**
 * @brief The delegate for asynchronous operations on the socket.
 *
 * @note The delegate is retained for as long as asynchronous operations are
 *	 still ongoing.
 */
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
- (void)asyncAccept;

/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param runLoopMode The run loop mode in which to perform the async accept
 */
- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param block The block to execute when a new connection has been accepted.
 *		Returns whether the next incoming connection should be accepted
 *		by the specified block as well.
 */
- (void)asyncAcceptWithBlock: (of_stream_socket_async_accept_block_t)block;

/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param runLoopMode The run loop mode in which to perform the async accept
 * @param block The block to execute when a new connection has been accepted.
 *		Returns whether the next incoming connection should be accepted
 *		by the specified block as well.
 */
- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
			     block: (of_stream_socket_async_accept_block_t)
					block;
#endif
@end

OF_ASSUME_NONNULL_END







|









|









|
<
|




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
- (void)asyncAccept;

/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param runLoopMode The run loop mode in which to perform the async accept
 */
- (void)asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param block The block to execute when a new connection has been accepted.
 *		Returns whether the next incoming connection should be accepted
 *		by the specified block as well.
 */
- (void)asyncAcceptWithBlock: (OFStreamSocketAsyncAcceptBlock)block;

/**
 * @brief Asynchronously accept an incoming connection.
 *
 * @param runLoopMode The run loop mode in which to perform the async accept
 * @param block The block to execute when a new connection has been accepted.
 *		Returns whether the next incoming connection should be accepted
 *		by the specified block as well.
 */
- (void)asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode

			     block: (OFStreamSocketAsyncAcceptBlock)block;
#endif
@end

OF_ASSUME_NONNULL_END

Modified src/OFStreamSocket.m from [2df2daa037] to [f14a71e66a].

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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */






#define __NO_EXT_QNX

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <string.h>

#import "OFStreamSocket.h"
#import "OFStreamSocket+Private.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"


#import "OFAcceptFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFListenFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFWriteFailedException.h"

#import "socket_helpers.h"

@implementation OFStreamSocket
@dynamic delegate;
@synthesize listening = _listening;

+ (void)initialize
{
	if (self != [OFStreamSocket class])
		return;

	if (!of_socket_init())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)socket
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	@try {
		if (self.class == [OFStreamSocket class]) {
			[self doesNotRecognizeSelector: _cmd];
			abort();
		}

		_socket = INVALID_SOCKET;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_socket != INVALID_SOCKET)
		[self close];

	[super dealloc];
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	ssize_t ret;

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

#ifndef OF_WINDOWS
	if ((ret = recv(_socket, buffer, length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
#else
	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((ret = recv(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
#endif

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

#ifndef OF_WINDOWS
	ssize_t bytesWritten;

	if (length > SSIZE_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = send(_socket, (void *)buffer, length, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: of_socket_errno()];
#else
	int bytesWritten;

	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = send(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: of_socket_errno()];
#endif

	return (size_t)bytesWritten;
}

#if defined(OF_WINDOWS) || defined(OF_AMIGAOS)
- (void)setCanBlock: (bool)canBlock
{
# ifdef OF_WINDOWS
	u_long v = canBlock;
# else
	char v = canBlock;
# endif

	if (ioctlsocket(_socket, FIONBIO, &v) == SOCKET_ERROR)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	_canBlock = canBlock;
}
#endif

- (int)fileDescriptorForReading
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == INVALID_SOCKET)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (int)fileDescriptorForWriting
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == INVALID_SOCKET)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

#ifndef OF_WII
- (int)of_socketError
{
	int errNo;
	socklen_t len = sizeof(errNo);

	if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char *)&errNo,
	    &len) != 0)
		return of_socket_errno();

	return errNo;
}
#endif

- (void)listen
{
	[self listenWithBacklog: SOMAXCONN];
}

- (void)listenWithBacklog: (int)backlog
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (listen(_socket, backlog) == -1)
		@throw [OFListenFailedException
		    exceptionWithSocket: self
				backlog: backlog
				  errNo: of_socket_errno()];

	_listening = true;
}

- (instancetype)accept
{
	OFStreamSocket *client = [[[[self class] alloc] init] autorelease];
#if (!defined(HAVE_PACCEPT) && !defined(HAVE_ACCEPT4)) || !defined(SOCK_CLOEXEC)
# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
# endif
#endif

	client->_remoteAddress.length =
	    (socklen_t)sizeof(client->_remoteAddress.sockaddr);

#if defined(HAVE_PACCEPT) && defined(SOCK_CLOEXEC)
	if ((client->_socket = paccept(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length, NULL, SOCK_CLOEXEC)) ==
	    INVALID_SOCKET)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: of_socket_errno()];
#elif defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
	if ((client->_socket = accept4(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length, SOCK_CLOEXEC)) == INVALID_SOCKET)

		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: of_socket_errno()];
#else
	if ((client->_socket = accept(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length)) == INVALID_SOCKET)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: of_socket_errno()];

# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(client->_socket, F_GETFD, 0)) != -1)
		fcntl(client->_socket, F_SETFD, flags | FD_CLOEXEC);
# endif
#endif

	assert(client->_remoteAddress.length <=
	    (socklen_t)sizeof(client->_remoteAddress.sockaddr));

	switch (client->_remoteAddress.sockaddr.sockaddr.sa_family) {
	case AF_INET:
		client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPV4;
		break;
#ifdef OF_HAVE_IPV6
	case AF_INET6:
		client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPV6;
		break;
#endif
#ifdef OF_HAVE_IPX
	case AF_IPX:
		client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPX;





		break;
#endif
	default:
		client->_remoteAddress.family =
		    OF_SOCKET_ADDRESS_FAMILY_UNKNOWN;
		break;
	}

	return client;
}

- (void)asyncAccept
{
	[self asyncAcceptWithRunLoopMode: of_run_loop_mode_default];
}

- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
{
	[OFRunLoop of_addAsyncAcceptForSocket: self
					 mode: runLoopMode
					block: NULL
				     delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncAcceptWithBlock: (of_stream_socket_async_accept_block_t)block
{
	[self asyncAcceptWithRunLoopMode: of_run_loop_mode_default
				   block: block];
}

- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
			     block: (of_stream_socket_async_accept_block_t)block
{
	[OFRunLoop of_addAsyncAcceptForSocket: self
					 mode: runLoopMode
					block: block
				     delegate: nil];
}
#endif

- (const of_socket_address_t *)remoteAddress
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_remoteAddress.length == 0)
		@throw [OFInvalidArgumentException exception];

	if (_remoteAddress.length > (socklen_t)sizeof(_remoteAddress.sockaddr))
		@throw [OFOutOfRangeException exception];

	return &_remoteAddress;
}

- (void)close
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	_listening = false;
	memset(&_remoteAddress, 0, sizeof(_remoteAddress));

	closesocket(_socket);
	_socket = INVALID_SOCKET;

	_atEndOfStream = false;

	[super close];
}
@end

<
<
|













>
>
>
>
>

|
<









>












<
<









|



















|










|







|





|
<



|







|








|








|
<

|













|











|









|

|





|










|














|

















|












|






|




















|


|



|
>


|



|


|












|



|




|
>
>
>
>
>



|
<








|


|








|

|
<


|
|








|

|













|






|






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


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

#ifndef _XOPEN_SOURCE_EXTENDED
# define _XOPEN_SOURCE_EXTENDED
#endif
#define __NO_EXT_QNX
#define _HPUX_ALT_XOPEN_SOCKET_API


#include <assert.h>
#include <errno.h>
#include <string.h>

#import "OFStreamSocket.h"
#import "OFStreamSocket+Private.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFSocket+Private.h"

#import "OFAcceptFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFListenFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFWriteFailedException.h"



@implementation OFStreamSocket
@dynamic delegate;
@synthesize listening = _listening;

+ (void)initialize
{
	if (self != [OFStreamSocket class])
		return;

	if (!OFSocketInit())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)socket
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	@try {
		if (self.class == [OFStreamSocket class]) {
			[self doesNotRecognizeSelector: _cmd];
			abort();
		}

		_socket = OFInvalidSocketHandle;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_socket != OFInvalidSocketHandle)
		[self close];

	[super dealloc];
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	ssize_t ret;

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

#ifndef OF_WINDOWS
	if ((ret = recv(_socket, buffer, length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: OFSocketErrNo()];
#else
	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((ret = recv(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: OFSocketErrNo()];
#endif

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

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

#ifndef OF_WINDOWS
	ssize_t bytesWritten;

	if (length > SSIZE_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = send(_socket, (void *)buffer, length, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: OFSocketErrNo()];
#else
	int bytesWritten;

	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((bytesWritten = send(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: OFSocketErrNo()];
#endif

	return (size_t)bytesWritten;
}

#if defined(OF_WINDOWS) || defined(OF_AMIGAOS)
- (void)setCanBlock: (bool)canBlock
{
# ifdef OF_WINDOWS
	u_long v = !canBlock;
# else
	char v = !canBlock;
# endif

	if (ioctlsocket(_socket, FIONBIO, &v) == SOCKET_ERROR)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];

	_canBlock = canBlock;
}
#endif

- (int)fileDescriptorForReading
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == OFInvalidSocketHandle)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

- (int)fileDescriptorForWriting
{
#ifndef OF_WINDOWS
	return _socket;
#else
	if (_socket == OFInvalidSocketHandle)
		return -1;

	if (_socket > INT_MAX)
		@throw [OFOutOfRangeException exception];

	return (int)_socket;
#endif
}

#ifndef OF_WII
- (int)of_socketError
{
	int errNo;
	socklen_t len = sizeof(errNo);

	if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char *)&errNo,
	    &len) != 0)
		return OFSocketErrNo();

	return errNo;
}
#endif

- (void)listen
{
	[self listenWithBacklog: SOMAXCONN];
}

- (void)listenWithBacklog: (int)backlog
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (listen(_socket, backlog) == -1)
		@throw [OFListenFailedException
		    exceptionWithSocket: self
				backlog: backlog
				  errNo: OFSocketErrNo()];

	_listening = true;
}

- (instancetype)accept
{
	OFStreamSocket *client = [[[[self class] alloc] init] autorelease];
#if (!defined(HAVE_PACCEPT) && !defined(HAVE_ACCEPT4)) || !defined(SOCK_CLOEXEC)
# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
# endif
#endif

	client->_remoteAddress.length =
	    (socklen_t)sizeof(client->_remoteAddress.sockaddr);

#if defined(HAVE_PACCEPT) && defined(SOCK_CLOEXEC)
	if ((client->_socket = paccept(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length, NULL, SOCK_CLOEXEC)) ==
	    OFInvalidSocketHandle)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: OFSocketErrNo()];
#elif defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
	if ((client->_socket = accept4(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length, SOCK_CLOEXEC)) ==
	    OFInvalidSocketHandle)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: OFSocketErrNo()];
#else
	if ((client->_socket = accept(_socket,
	    &client->_remoteAddress.sockaddr.sockaddr,
	    &client->_remoteAddress.length)) == OFInvalidSocketHandle)
		@throw [OFAcceptFailedException
		    exceptionWithSocket: self
				  errNo: OFSocketErrNo()];

# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(client->_socket, F_GETFD, 0)) != -1)
		fcntl(client->_socket, F_SETFD, flags | FD_CLOEXEC);
# endif
#endif

	assert(client->_remoteAddress.length <=
	    (socklen_t)sizeof(client->_remoteAddress.sockaddr));

	switch (client->_remoteAddress.sockaddr.sockaddr.sa_family) {
	case AF_INET:
		client->_remoteAddress.family = OFSocketAddressFamilyIPv4;
		break;
#ifdef OF_HAVE_IPV6
	case AF_INET6:
		client->_remoteAddress.family = OFSocketAddressFamilyIPv6;
		break;
#endif
#ifdef OF_HAVE_IPX
	case AF_IPX:
		client->_remoteAddress.family = OFSocketAddressFamilyIPX;
		break;
#endif
#ifdef OF_HAVE_UNIX_SOCKETS
	case AF_UNIX:
		client->_remoteAddress.family = OFSocketAddressFamilyUNIX;
		break;
#endif
	default:
		client->_remoteAddress.family = OFSocketAddressFamilyUnknown;

		break;
	}

	return client;
}

- (void)asyncAccept
{
	[self asyncAcceptWithRunLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode
{
	[OFRunLoop of_addAsyncAcceptForSocket: self
					 mode: runLoopMode
					block: NULL
				     delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncAcceptWithBlock: (OFStreamSocketAsyncAcceptBlock)block
{
	[self asyncAcceptWithRunLoopMode: OFDefaultRunLoopMode block: block];

}

- (void)asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode
			     block: (OFStreamSocketAsyncAcceptBlock)block
{
	[OFRunLoop of_addAsyncAcceptForSocket: self
					 mode: runLoopMode
					block: block
				     delegate: nil];
}
#endif

- (const OFSocketAddress *)remoteAddress
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_remoteAddress.length == 0)
		@throw [OFInvalidArgumentException exception];

	if (_remoteAddress.length > (socklen_t)sizeof(_remoteAddress.sockaddr))
		@throw [OFOutOfRangeException exception];

	return &_remoteAddress;
}

- (void)close
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	_listening = false;
	memset(&_remoteAddress, 0, sizeof(_remoteAddress));

	closesocket(_socket);
	_socket = OFInvalidSocketHandle;

	_atEndOfStream = false;

	[super close];
}
@end

Renamed and modified src/OFString+CryptoHashing.h [6628c49341] to src/OFString+CryptographicHashing.h [63c51821ee].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFString.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __cplusplus
extern "C" {
#endif
extern int _OFString_CryptoHashing_reference;
#ifdef __cplusplus
}
#endif

@interface OFString (CryptoHashing)
/**
 * @brief The MD5 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *MD5Hash;

/**
 * @brief The RIPEMD-160 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *RIPEMD160Hash;

/**
 * @brief The SHA-1 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *SHA1Hash;

/**
 * @brief The SHA-224 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *SHA224Hash;

/**
 * @brief The SHA-256 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *SHA256Hash;

/**
 * @brief The SHA-384 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *SHA384Hash;

/**
 * @brief The SHA-512 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *SHA512Hash;
@end

OF_ASSUME_NONNULL_END

<
<
|




















|




|



|




|




|




|




|




|




|



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


 * 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 "OFString.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __cplusplus
extern "C" {
#endif
extern int _OFString_CryptographicHashing_reference;
#ifdef __cplusplus
}
#endif

@interface OFString (CryptographicHashing)
/**
 * @brief The MD5 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *stringByMD5Hashing;

/**
 * @brief The RIPEMD-160 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *stringByRIPEMD160Hashing;

/**
 * @brief The SHA-1 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA1Hashing;

/**
 * @brief The SHA-224 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA224Hashing;

/**
 * @brief The SHA-256 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA256Hashing;

/**
 * @brief The SHA-384 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA384Hashing;

/**
 * @brief The SHA-512 hash of the string as a string.
 */
@property (readonly, nonatomic) OFString *stringBySHA512Hashing;
@end

OF_ASSUME_NONNULL_END

Renamed and modified src/OFString+CryptoHashing.m [9d43bb7986] to src/OFString+CryptographicHashing.m [30b5bea6f8].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"
#import "OFCryptoHash.h"
#import "OFMD5Hash.h"
#import "OFRIPEMD160Hash.h"
#import "OFSHA1Hash.h"
#import "OFSHA224Hash.h"
#import "OFSHA256Hash.h"
#import "OFSHA384Hash.h"
#import "OFSHA512Hash.h"

int _OFString_CryptoHashing_reference;

@implementation OFString (CryptoHashing)
- (OFString *)of_cryptoHashWithClass: (Class <OFCryptoHash>)class

{
	void *pool = objc_autoreleasePoolPush();
	id <OFCryptoHash> hash = [class
	    cryptoHashWithAllowsSwappableMemory: true];
	size_t digestSize = [class digestSize];
	const unsigned char *digest;
	char cString[digestSize * 2];

	[hash updateWithBuffer: self.UTF8String
			length: self.UTF8StringLength];
	digest = hash.digest;

	for (size_t i = 0; i < digestSize; i++) {
		uint8_t high, low;

		high = digest[i] >> 4;
		low  = digest[i] & 0x0F;

		cString[i * 2] = (high > 9 ? high - 10 + 'a' : high + '0');
		cString[i * 2 + 1] = (low > 9 ? low - 10 + 'a' : low + '0');
	}

	objc_autoreleasePoolPop(pool);

	return [OFString stringWithCString: cString
				  encoding: OF_STRING_ENCODING_ASCII
				    length: digestSize * 2];
}

- (OFString *)MD5Hash
{
	return [self of_cryptoHashWithClass: [OFMD5Hash class]];
}

- (OFString *)RIPEMD160Hash
{
	return [self of_cryptoHashWithClass: [OFRIPEMD160Hash class]];
}

- (OFString *)SHA1Hash
{
	return [self of_cryptoHashWithClass: [OFSHA1Hash class]];
}

- (OFString *)SHA224Hash
{
	return [self of_cryptoHashWithClass: [OFSHA224Hash class]];
}

- (OFString *)SHA256Hash
{
	return [self of_cryptoHashWithClass: [OFSHA256Hash class]];
}

- (OFString *)SHA384Hash
{
	return [self of_cryptoHashWithClass: [OFSHA384Hash class]];
}

- (OFString *)SHA512Hash
{
	return [self of_cryptoHashWithClass: [OFSHA512Hash class]];
}
@end

<
<
|
















|








|

|
|
>


|
|




|
|















|



|

|


|

|


|

|


|

|


|

|


|

|


|

|


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


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

#import "OFString.h"
#import "OFCryptographicHash.h"
#import "OFMD5Hash.h"
#import "OFRIPEMD160Hash.h"
#import "OFSHA1Hash.h"
#import "OFSHA224Hash.h"
#import "OFSHA256Hash.h"
#import "OFSHA384Hash.h"
#import "OFSHA512Hash.h"

int _OFString_CryptographicHashing_reference;

@implementation OFString (CryptographicHashing)
static OFString *
stringByHashing(Class <OFCryptographicHash> class, OFString *self)
{
	void *pool = objc_autoreleasePoolPush();
	id <OFCryptographicHash> hash =
	    [class hashWithAllowsSwappableMemory: true];
	size_t digestSize = [class digestSize];
	const unsigned char *digest;
	char cString[digestSize * 2];

	[hash updateWithBuffer: self.UTF8String length: self.UTF8StringLength];
	[hash calculate];
	digest = hash.digest;

	for (size_t i = 0; i < digestSize; i++) {
		uint8_t high, low;

		high = digest[i] >> 4;
		low  = digest[i] & 0x0F;

		cString[i * 2] = (high > 9 ? high - 10 + 'a' : high + '0');
		cString[i * 2 + 1] = (low > 9 ? low - 10 + 'a' : low + '0');
	}

	objc_autoreleasePoolPop(pool);

	return [OFString stringWithCString: cString
				  encoding: OFStringEncodingASCII
				    length: digestSize * 2];
}

- (OFString *)stringByMD5Hashing
{
	return stringByHashing([OFMD5Hash class], self);
}

- (OFString *)stringByRIPEMD160Hashing
{
	return stringByHashing([OFRIPEMD160Hash class], self);
}

- (OFString *)stringBySHA1Hashing
{
	return stringByHashing([OFSHA1Hash class], self);
}

- (OFString *)stringBySHA224Hashing
{
	return stringByHashing([OFSHA224Hash class], self);
}

- (OFString *)stringBySHA256Hashing
{
	return stringByHashing([OFSHA256Hash class], self);
}

- (OFString *)stringBySHA384Hashing
{
	return stringByHashing([OFSHA384Hash class], self);
}

- (OFString *)stringBySHA512Hashing
{
	return stringByHashing([OFSHA512Hash class], self);
}
@end

Modified src/OFString+JSONParsing.h from [ca618a0ff6] to [ace137eeef].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFString+JSONParsing.m from [b73e42773d] to [5489126d7d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
27
28
29
30
31
32
33




34
35
36
37
38
39
40
#import "OFString+JSONParsing.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFNumber.h"
#import "OFNull.h"

#import "OFInvalidJSONException.h"





int _OFString_JSONParsing_reference;

static id nextObject(const char **pointer, const char *stop, size_t *line,
    size_t depthLimit);

static void







>
>
>
>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#import "OFString+JSONParsing.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFNumber.h"
#import "OFNull.h"

#import "OFInvalidJSONException.h"

#ifndef INFINITY
# define INFINITY __builtin_inf()
#endif

int _OFString_JSONParsing_reference;

static id nextObject(const char **pointer, const char *stop, size_t *line,
    size_t depthLimit);

static void
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
		old = *pointer;

		skipWhitespaces(pointer, stop, line);
		skipComment(pointer, stop, line);
	}
}

static inline of_char16_t
parseUnicodeEscape(const char *pointer, const char *stop)
{
	of_char16_t ret = 0;

	if (pointer + 5 >= stop)
		return 0xFFFF;

	if (pointer[0] != '\\' || pointer[1] != 'u')
		return 0xFFFF;








|


|







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
		old = *pointer;

		skipWhitespaces(pointer, stop, line);
		skipComment(pointer, stop, line);
	}
}

static inline OFChar16
parseUnicodeEscape(const char *pointer, const char *stop)
{
	OFChar16 ret = 0;

	if (pointer + 5 >= stop)
		return 0xFFFF;

	if (pointer[0] != '\\' || pointer[1] != 'u')
		return 0xFFFF;

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
	char *buffer;
	size_t i = 0;
	char delimiter = **pointer;

	if (++(*pointer) + 1 >= stop)
		return nil;

	buffer = of_malloc(1, stop - *pointer);

	while (*pointer < stop) {
		/* Parse escape codes */
		if (**pointer == '\\') {
			if (++(*pointer) >= stop) {
				of_free(buffer);
				return nil;
			}

			switch (**pointer) {
			case '"':
			case '\\':
			case '/':







|





|







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
	char *buffer;
	size_t i = 0;
	char delimiter = **pointer;

	if (++(*pointer) + 1 >= stop)
		return nil;

	buffer = OFAllocMemory(stop - *pointer, 1);

	while (*pointer < stop) {
		/* Parse escape codes */
		if (**pointer == '\\') {
			if (++(*pointer) >= stop) {
				OFFreeMemory(buffer);
				return nil;
			}

			switch (**pointer) {
			case '"':
			case '\\':
			case '/':
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
				break;
			case 't':
				buffer[i++] = '\t';
				(*pointer)++;
				break;
			/* Parse Unicode escape sequence */
			case 'u':;
				of_char16_t c1, c2;
				of_unichar_t c;
				size_t l;

				c1 = parseUnicodeEscape(*pointer - 1, stop);
				if (c1 == 0xFFFF) {
					of_free(buffer);
					return nil;
				}

				/* Low surrogate */
				if ((c1 & 0xFC00) == 0xDC00) {
					of_free(buffer);
					return nil;
				}

				/* Normal character */
				if ((c1 & 0xFC00) != 0xD800) {
					l = of_string_utf8_encode(c1,
					    buffer + i);
					if (l == 0) {
						of_free(buffer);
						return nil;
					}

					i += l;
					*pointer += 5;

					break;
				}

				/*
				 * If we are still here, we only got one UTF-16
				 * surrogate and now need to get the other one
				 * in order to produce UTF-8 and not CESU-8.
				 */
				c2 = parseUnicodeEscape(*pointer + 5, stop);
				if (c2 == 0xFFFF) {
					of_free(buffer);
					return nil;
				}

				c = (((c1 & 0x3FF) << 10) |
				    (c2 & 0x3FF)) + 0x10000;

				l = of_string_utf8_encode(c, buffer + i);
				if (l == 0) {
					of_free(buffer);
					return nil;
				}

				i += l;
				*pointer += 11;

				break;







|
|




|





|





<
|

|
















|






|

|







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
				break;
			case 't':
				buffer[i++] = '\t';
				(*pointer)++;
				break;
			/* Parse Unicode escape sequence */
			case 'u':;
				OFChar16 c1, c2;
				OFUnichar c;
				size_t l;

				c1 = parseUnicodeEscape(*pointer - 1, stop);
				if (c1 == 0xFFFF) {
					OFFreeMemory(buffer);
					return nil;
				}

				/* Low surrogate */
				if ((c1 & 0xFC00) == 0xDC00) {
					OFFreeMemory(buffer);
					return nil;
				}

				/* Normal character */
				if ((c1 & 0xFC00) != 0xD800) {

					l = OFUTF8StringEncode(c1, buffer + i);
					if (l == 0) {
						OFFreeMemory(buffer);
						return nil;
					}

					i += l;
					*pointer += 5;

					break;
				}

				/*
				 * If we are still here, we only got one UTF-16
				 * surrogate and now need to get the other one
				 * in order to produce UTF-8 and not CESU-8.
				 */
				c2 = parseUnicodeEscape(*pointer + 5, stop);
				if (c2 == 0xFFFF) {
					OFFreeMemory(buffer);
					return nil;
				}

				c = (((c1 & 0x3FF) << 10) |
				    (c2 & 0x3FF)) + 0x10000;

				l = OFUTF8StringEncode(c, buffer + i);
				if (l == 0) {
					OFFreeMemory(buffer);
					return nil;
				}

				i += l;
				*pointer += 11;

				break;
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

				break;
			case '\n':
				(*pointer)++;
				(*line)++;
				break;
			default:
				of_free(buffer);
				return nil;
			}
		/* End of string found */
		} else if (**pointer == delimiter) {
			OFString *ret;

			@try {
				ret = [OFString stringWithUTF8String: buffer
							      length: i];
			} @finally {
				of_free(buffer);
			}

			(*pointer)++;

			return ret;
		/* Newlines in strings are disallowed */
		} else if (**pointer == '\n' || **pointer == '\r') {
			(*line)++;
			of_free(buffer);
			return nil;
		} else {
			buffer[i++] = **pointer;
			(*pointer)++;
		}
	}

	of_free(buffer);
	return nil;
}

static inline OFString *
parseIdentifier(const char **pointer, const char *stop)
{
	char *buffer;
	size_t i = 0;

	buffer = of_malloc(1, stop - *pointer);

	while (*pointer < stop) {
		if ((**pointer >= 'a' && **pointer <= 'z') ||
		    (**pointer >= 'A' && **pointer <= 'Z') ||
		    (**pointer >= '0' && **pointer <= '9') ||
		    **pointer == '_' || **pointer == '$' ||
		    (**pointer & 0x80)) {
			buffer[i++] = **pointer;
			(*pointer)++;
		} else if (**pointer == '\\') {
			of_char16_t c1, c2;
			of_unichar_t c;
			size_t l;

			if (++(*pointer) >= stop || **pointer != 'u') {
				of_free(buffer);
				return nil;
			}

			c1 = parseUnicodeEscape(*pointer - 1, stop);
			if (c1 == 0xFFFF) {
				of_free(buffer);
				return nil;
			}

			/* Low surrogate */
			if ((c1 & 0xFC00) == 0xDC00) {
				of_free(buffer);
				return nil;
			}

			/* Normal character */
			if ((c1 & 0xFC00) != 0xD800) {
				l = of_string_utf8_encode(c1, buffer + i);
				if (l == 0) {
					of_free(buffer);
					return nil;
				}

				i += l;
				*pointer += 5;

				continue;
			}

			/*
			 * If we are still here, we only got one UTF-16
			 * surrogate and now need to get the other one in order
			 * to produce UTF-8 and not CESU-8.
			 */
			c2 = parseUnicodeEscape(*pointer + 5, stop);
			if (c2 == 0xFFFF) {
				of_free(buffer);
				return nil;
			}

			c = (((c1 & 0x3FF) << 10) | (c2 & 0x3FF)) + 0x10000;

			l = of_string_utf8_encode(c, buffer + i);
			if (l == 0) {
				of_free(buffer);
				return nil;
			}

			i += l;
			*pointer += 11;
		} else {
			OFString *ret;

			if (i == 0 || (buffer[0] >= '0' && buffer[0] <= '9')) {
				of_free(buffer);
				return nil;
			}

			@try {
				ret = [OFString stringWithUTF8String: buffer
							      length: i];
			} @finally {
				of_free(buffer);
			}

			return ret;
		}
	}

	/*
	 * It is never possible to end with an identifier, thus we should never
	 * reach stop.
	 */
	of_free(buffer);
	return nil;
}

static inline OFMutableArray *
parseArray(const char **pointer, const char *stop, size_t *line,
    size_t depthLimit)
{







|










|








|







|









|










|
|



|





|





|





|

|
















|





|

|









|







|










|







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

				break;
			case '\n':
				(*pointer)++;
				(*line)++;
				break;
			default:
				OFFreeMemory(buffer);
				return nil;
			}
		/* End of string found */
		} else if (**pointer == delimiter) {
			OFString *ret;

			@try {
				ret = [OFString stringWithUTF8String: buffer
							      length: i];
			} @finally {
				OFFreeMemory(buffer);
			}

			(*pointer)++;

			return ret;
		/* Newlines in strings are disallowed */
		} else if (**pointer == '\n' || **pointer == '\r') {
			(*line)++;
			OFFreeMemory(buffer);
			return nil;
		} else {
			buffer[i++] = **pointer;
			(*pointer)++;
		}
	}

	OFFreeMemory(buffer);
	return nil;
}

static inline OFString *
parseIdentifier(const char **pointer, const char *stop)
{
	char *buffer;
	size_t i = 0;

	buffer = OFAllocMemory(stop - *pointer, 1);

	while (*pointer < stop) {
		if ((**pointer >= 'a' && **pointer <= 'z') ||
		    (**pointer >= 'A' && **pointer <= 'Z') ||
		    (**pointer >= '0' && **pointer <= '9') ||
		    **pointer == '_' || **pointer == '$' ||
		    (**pointer & 0x80)) {
			buffer[i++] = **pointer;
			(*pointer)++;
		} else if (**pointer == '\\') {
			OFChar16 c1, c2;
			OFUnichar c;
			size_t l;

			if (++(*pointer) >= stop || **pointer != 'u') {
				OFFreeMemory(buffer);
				return nil;
			}

			c1 = parseUnicodeEscape(*pointer - 1, stop);
			if (c1 == 0xFFFF) {
				OFFreeMemory(buffer);
				return nil;
			}

			/* Low surrogate */
			if ((c1 & 0xFC00) == 0xDC00) {
				OFFreeMemory(buffer);
				return nil;
			}

			/* Normal character */
			if ((c1 & 0xFC00) != 0xD800) {
				l = OFUTF8StringEncode(c1, buffer + i);
				if (l == 0) {
					OFFreeMemory(buffer);
					return nil;
				}

				i += l;
				*pointer += 5;

				continue;
			}

			/*
			 * If we are still here, we only got one UTF-16
			 * surrogate and now need to get the other one in order
			 * to produce UTF-8 and not CESU-8.
			 */
			c2 = parseUnicodeEscape(*pointer + 5, stop);
			if (c2 == 0xFFFF) {
				OFFreeMemory(buffer);
				return nil;
			}

			c = (((c1 & 0x3FF) << 10) | (c2 & 0x3FF)) + 0x10000;

			l = OFUTF8StringEncode(c, buffer + i);
			if (l == 0) {
				OFFreeMemory(buffer);
				return nil;
			}

			i += l;
			*pointer += 11;
		} else {
			OFString *ret;

			if (i == 0 || (buffer[0] >= '0' && buffer[0] <= '9')) {
				OFFreeMemory(buffer);
				return nil;
			}

			@try {
				ret = [OFString stringWithUTF8String: buffer
							      length: i];
			} @finally {
				OFFreeMemory(buffer);
			}

			return ret;
		}
	}

	/*
	 * It is never possible to end with an identifier, thus we should never
	 * reach stop.
	 */
	OFFreeMemory(buffer);
	return nil;
}

static inline OFMutableArray *
parseArray(const char **pointer, const char *stop, size_t *line,
    size_t depthLimit)
{
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513

		(*pointer)++;

		object = nextObject(pointer, stop, line, depthLimit);
		if (object == nil)
			return nil;

		[dictionary setObject: object
			       forKey: key];

		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer >= stop)
			return nil;

		if (**pointer == ',') {
			(*pointer)++;







|
<







499
500
501
502
503
504
505
506

507
508
509
510
511
512
513

		(*pointer)++;

		object = nextObject(pointer, stop, line, depthLimit);
		if (object == nil)
			return nil;

		[dictionary setObject: object forKey: key];


		skipWhitespacesAndComments(pointer, stop, line);
		if (*pointer >= stop)
			return nil;

		if (**pointer == ',') {
			(*pointer)++;
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
			if ((*pointer)[i] == '\n')
				(*line)++;

			break;
		}
	}

	string = [[OFString alloc] initWithUTF8String: *pointer
					       length: i];
	*pointer += i;

	@try {
		if (hasDecimal)
			number = [OFNumber numberWithDouble:
			    string.doubleValue];
		else if ([string isEqual: @"Infinity"])







|
<







544
545
546
547
548
549
550
551

552
553
554
555
556
557
558
			if ((*pointer)[i] == '\n')
				(*line)++;

			break;
		}
	}

	string = [[OFString alloc] initWithUTF8String: *pointer length: i];

	*pointer += i;

	@try {
		if (hasDecimal)
			number = [OFNumber numberWithDouble:
			    string.doubleValue];
		else if ([string isEqual: @"Infinity"])

Modified src/OFString+PathAdditions.h from [9755ca9fad] to [c69c663e20].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFString.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef OF_AMIGAOS
# define OF_PATH_CURRENT_DIRECTORY @""
#else
# define OF_PATH_CURRENT_DIRECTORY @"."
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern int _OFString_PathAdditions_reference;
#ifdef __cplusplus
}
#endif

<
<
|

















<
<
<
<
<
<







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


 * 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 "OFString.h"

OF_ASSUME_NONNULL_BEGIN







#ifdef __cplusplus
extern "C" {
#endif
extern int _OFString_PathAdditions_reference;
#ifdef __cplusplus
}
#endif

Modified src/OFString+PathAdditions.m from [ea666f96ec] to [422d7384b5].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

#if defined(OF_WINDOWS) || defined(OF_MSDOS)
# import "platform/windows/OFString+PathAdditions.m"
#elif defined(OF_AMIGAOS)
# import "platform/amiga/OFString+PathAdditions.m"
#elif defined(OF_NINTENDO_3DS) || defined(OF_WII)
# import "platform/libfat/OFString+PathAdditions.m"
#else
# import "platform/posix/OFString+PathAdditions.m"
#endif

<
<
|

















|
|

|



|

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


 * 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 "platform.h"

#if defined(OF_WINDOWS) || defined(OF_MSDOS) || defined(OF_MINT)
# import "platform/Windows/OFString+PathAdditions.m"
#elif defined(OF_AMIGAOS)
# import "platform/AmigaOS/OFString+PathAdditions.m"
#elif defined(OF_NINTENDO_3DS) || defined(OF_WII)
# import "platform/libfat/OFString+PathAdditions.m"
#else
# import "platform/POSIX/OFString+PathAdditions.m"
#endif

Modified src/OFString+PropertyListParsing.h from [de40cce278] to [c22c9f1090].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFString+PropertyListParsing.m from [3c0b0b5b63] to [4a79f533f5].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
	enumerator = [children objectEnumerator];
	while ((key = [enumerator nextObject]) &&
	    (object = [enumerator nextObject])) {
		if (key.namespace != nil || key.attributes.count != 0 ||
		    ![key.name isEqual: @"key"])
			@throw [OFInvalidFormatException exception];

		[ret setObject: parseElement(object)
			forKey: key.stringValue];
	}

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;







|
<







62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
	enumerator = [children objectEnumerator];
	while ((key = [enumerator nextObject]) &&
	    (object = [enumerator nextObject])) {
		if (key.namespace != nil || key.attributes.count != 0 ||
		    ![key.name isEqual: @"key"])
			@throw [OFInvalidFormatException exception];

		[ret setObject: parseElement(object) forKey: key.stringValue];

	}

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;

Modified src/OFString+Serialization.h from [0280f20096] to [2c9a0f8857].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFString+Serialization.m from [0b468db6b6] to [feafb9e064].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
	if (version == nil)
		@throw [OFInvalidArgumentException exception];

	if (version.unsignedLongLongValue != 1)
		@throw [OFUnsupportedVersionException
		    exceptionWithVersion: version];

	elements = [root elementsForNamespace: OF_SERIALIZATION_NS];

	if (elements.count != 1)
		@throw [OFInvalidArgumentException exception];

	object = [[elements.firstObject objectByDeserializing] retain];

	objc_autoreleasePoolPop(pool);

	return [object autorelease];
}
@end







|











50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
	if (version == nil)
		@throw [OFInvalidArgumentException exception];

	if (version.unsignedLongLongValue != 1)
		@throw [OFUnsupportedVersionException
		    exceptionWithVersion: version];

	elements = [root elementsForNamespace: OFSerializationNS];

	if (elements.count != 1)
		@throw [OFInvalidArgumentException exception];

	object = [[elements.firstObject objectByDeserializing] retain];

	objc_autoreleasePoolPop(pool);

	return [object autorelease];
}
@end

Modified src/OFString+URLEncoding.h from [becefbf01f] to [b50441492b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFString+URLEncoding.m from [0c0207b5a6] to [7c362d8c35].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

@implementation OFString (URLEncoding)
- (OFString *)stringByURLEncodingWithAllowedCharacters:
    (OFCharacterSet *)allowedCharacters
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters = self.characters;
	size_t length = self.length;
	bool (*characterIsMember)(id, SEL, of_unichar_t) =
	    (bool (*)(id, SEL, of_unichar_t))[allowedCharacters
	    methodForSelector: @selector(characterIsMember:)];

	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = characters[i];

		if (characterIsMember(allowedCharacters,
		    @selector(characterIsMember:), c))
			[ret appendCharacters: &c
				       length: 1];
		else {
			char buffer[4];
			size_t bufferLen;

			if ((bufferLen = of_string_utf8_encode(c, buffer)) == 0)
				@throw [OFInvalidEncodingException exception];

			for (size_t j = 0; j < bufferLen; j++) {
				unsigned char byte = buffer[j];
				unsigned char high = byte >> 4;
				unsigned char low = byte & 0x0F;
				char escaped[3];

				escaped[0] = '%';
				escaped[1] =
				    (high > 9 ? high - 10 + 'A' : high + '0');
				escaped[2] =
				    (low  > 9 ? low  - 10 + 'A' : low  + '0');

				[ret appendUTF8String: escaped
					       length: 3];
			}
		}
	}

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFString *)stringByURLDecoding
{
	void *pool = objc_autoreleasePoolPush();
	const char *string = self.UTF8String;
	size_t length = self.UTF8StringLength;
	char *retCString;
	char byte = 0;
	int state = 0;
	size_t i = 0;


	retCString = of_malloc(1, length + 1);

	while (length--) {
		char c = *string++;

		switch (state) {
		case 0:
			if (c == '%')







|

|
|



|



|
<




|














|
<


















>

|







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

@implementation OFString (URLEncoding)
- (OFString *)stringByURLEncodingWithAllowedCharacters:
    (OFCharacterSet *)allowedCharacters
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters = self.characters;
	size_t length = self.length;
	bool (*characterIsMember)(id, SEL, OFUnichar) =
	    (bool (*)(id, SEL, OFUnichar))[allowedCharacters
	    methodForSelector: @selector(characterIsMember:)];

	for (size_t i = 0; i < length; i++) {
		OFUnichar c = characters[i];

		if (characterIsMember(allowedCharacters,
		    @selector(characterIsMember:), c))
			[ret appendCharacters: &c length: 1];

		else {
			char buffer[4];
			size_t bufferLen;

			if ((bufferLen = OFUTF8StringEncode(c, buffer)) == 0)
				@throw [OFInvalidEncodingException exception];

			for (size_t j = 0; j < bufferLen; j++) {
				unsigned char byte = buffer[j];
				unsigned char high = byte >> 4;
				unsigned char low = byte & 0x0F;
				char escaped[3];

				escaped[0] = '%';
				escaped[1] =
				    (high > 9 ? high - 10 + 'A' : high + '0');
				escaped[2] =
				    (low  > 9 ? low  - 10 + 'A' : low  + '0');

				[ret appendUTF8String: escaped length: 3];

			}
		}
	}

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFString *)stringByURLDecoding
{
	void *pool = objc_autoreleasePoolPush();
	const char *string = self.UTF8String;
	size_t length = self.UTF8StringLength;
	char *retCString;
	char byte = 0;
	int state = 0;
	size_t i = 0;
	OFString *ret;

	retCString = OFAllocMemory(length + 1, 1);

	while (length--) {
		char c = *string++;

		switch (state) {
		case 0:
			if (c == '%')
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
			if (c >= '0' && c <= '9')
				byte += (c - '0') << shift;
			else if (c >= 'A' && c <= 'F')
				byte += (c - 'A' + 10) << shift;
			else if (c >= 'a' && c <= 'f')
				byte += (c - 'a' + 10) << shift;
			else {
				of_free(retCString);
				@throw [OFInvalidFormatException exception];
			}

			if (++state == 3) {
				retCString[i++] = byte;
				state = 0;
				byte = 0;
			}

			break;
		}
	}
	retCString[i] = '\0';

	objc_autoreleasePoolPop(pool);

	if (state != 0) {
		of_free(retCString);
		@throw [OFInvalidFormatException exception];
	}

	@try {
		retCString = of_realloc(retCString, 1, i + 1);
	} @catch (OFOutOfMemoryException *e) {
		/* We don't care if it fails, as we only made it smaller. */
	}


	return [OFString stringWithUTF8StringNoCopy: retCString
					     length: i
				       freeWhenDone: true];



}



@end







|

















|




|




>
|
|
|
>
>
>
|
>
>
>

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
			if (c >= '0' && c <= '9')
				byte += (c - '0') << shift;
			else if (c >= 'A' && c <= 'F')
				byte += (c - 'A' + 10) << shift;
			else if (c >= 'a' && c <= 'f')
				byte += (c - 'a' + 10) << shift;
			else {
				OFFreeMemory(retCString);
				@throw [OFInvalidFormatException exception];
			}

			if (++state == 3) {
				retCString[i++] = byte;
				state = 0;
				byte = 0;
			}

			break;
		}
	}
	retCString[i] = '\0';

	objc_autoreleasePoolPop(pool);

	if (state != 0) {
		OFFreeMemory(retCString);
		@throw [OFInvalidFormatException exception];
	}

	@try {
		retCString = OFResizeMemory(retCString, 1, i + 1);
	} @catch (OFOutOfMemoryException *e) {
		/* We don't care if it fails, as we only made it smaller. */
	}

	@try {
		ret = [OFString stringWithUTF8StringNoCopy: retCString
						    length: i
					      freeWhenDone: true];
	} @catch (id e) {
		OFFreeMemory(retCString);
		@throw e;
	}

	return ret;
}
@end

Modified src/OFString+XMLEscaping.h from [a998c62996] to [5e7ff84170].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFString+XMLEscaping.m from [5d08b70ba3] to [417a4985be].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
	OFString *ret;

	string = self.UTF8String;
	length = self.UTF8StringLength;

	j = 0;
	retLength = length;

	/*
	 * We can't use allocMemoryWithSize: here as it might be a @"" literal
	 */
	retCString = of_malloc(1, retLength);

	for (size_t i = 0; i < length; i++) {
		switch (string[i]) {
		case '<':
			append = "&lt;";
			appendLen = 4;
			break;







|
<
<
<
<







36
37
38
39
40
41
42
43




44
45
46
47
48
49
50
	OFString *ret;

	string = self.UTF8String;
	length = self.UTF8StringLength;

	j = 0;
	retLength = length;
	retCString = OFAllocMemory(retLength, 1);





	for (size_t i = 0; i < length; i++) {
		switch (string[i]) {
		case '<':
			append = "&lt;";
			appendLen = 4;
			break;
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
		default:
			append = NULL;
			appendLen = 0;
		}

		if (append != NULL) {
			@try {
				retCString = of_realloc(retCString, 1,
				    retLength + appendLen);
			} @catch (id e) {
				of_free(retCString);
				@throw e;
			}
			retLength += appendLen - 1;

			memcpy(retCString + j, append, appendLen);
			j += appendLen;
		} else
			retCString[j++] = string[i];
	}
	assert(j == retLength);

	objc_autoreleasePoolPop(pool);

	@try {
		ret = [OFString stringWithUTF8String: retCString
					      length: retLength];
	} @finally {
		of_free(retCString);
	}
	return ret;
}
@end







|


|

















|




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
		default:
			append = NULL;
			appendLen = 0;
		}

		if (append != NULL) {
			@try {
				retCString = OFResizeMemory(retCString, 1,
				    retLength + appendLen);
			} @catch (id e) {
				OFFreeMemory(retCString);
				@throw e;
			}
			retLength += appendLen - 1;

			memcpy(retCString + j, append, appendLen);
			j += appendLen;
		} else
			retCString[j++] = string[i];
	}
	assert(j == retLength);

	objc_autoreleasePoolPop(pool);

	@try {
		ret = [OFString stringWithUTF8String: retCString
					      length: retLength];
	} @finally {
		OFFreeMemory(retCString);
	}
	return ret;
}
@end

Modified src/OFString+XMLUnescaping.h from [0d82938925] to [f15d871a62].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 * @brief A block which is called to replace unknown XML entities in an XML
 *	  string.
 *
 * @param string The XML string which contains an unknown entity
 * @param entity The XML entity which is unknown
 * @return A replacement string for the unknown entity
 */
typedef OFString *_Nullable (^of_string_xml_unescaping_block_t)(
    OFString *string, OFString *entity);
#endif

/**
 * @protocol OFStringXMLUnescapingDelegate OFString.h ObjFW/OFString.h
 *
 * @brief A protocol that needs to be implemented by delegates for
 *	  stringByXMLUnescapingWithHandler:.







|
|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 * @brief A block which is called to replace unknown XML entities in an XML
 *	  string.
 *
 * @param string The XML string which contains an unknown entity
 * @param entity The XML entity which is unknown
 * @return A replacement string for the unknown entity
 */
typedef OFString *_Nullable (^OFStringXMLUnescapingBlock)(OFString *string,
    OFString *entity);
#endif

/**
 * @protocol OFStringXMLUnescapingDelegate OFString.h ObjFW/OFString.h
 *
 * @brief A protocol that needs to be implemented by delegates for
 *	  stringByXMLUnescapingWithHandler:.
83
84
85
86
87
88
89
90
91
92
93
94
95
#ifdef OF_HAVE_BLOCKS
/**
 * @brief Unescapes XML in the string and uses the specified block for unknown
 *	  entities.
 *
 * @param block A block which handles unknown entities
 */
- (OFString *)stringByXMLUnescapingWithBlock:
    (of_string_xml_unescaping_block_t)block;
#endif
@end

OF_ASSUME_NONNULL_END







|
<




81
82
83
84
85
86
87
88

89
90
91
92
#ifdef OF_HAVE_BLOCKS
/**
 * @brief Unescapes XML in the string and uses the specified block for unknown
 *	  entities.
 *
 * @param block A block which handles unknown entities
 */
- (OFString *)stringByXMLUnescapingWithBlock: (OFStringXMLUnescapingBlock)block;

#endif
@end

OF_ASSUME_NONNULL_END

Modified src/OFString+XMLUnescaping.m from [0954130a1e] to [dd8350c1c8].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#import "OFUnknownXMLEntityException.h"

int _OFString_XMLUnescaping_reference;

static OF_INLINE OFString *
parseNumericEntity(const char *entity, size_t length)
{
	of_unichar_t c;
	size_t i;
	char buffer[5];

	if (length == 1 || *entity != '#')
		return nil;

	c = 0;







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#import "OFUnknownXMLEntityException.h"

int _OFString_XMLUnescaping_reference;

static OF_INLINE OFString *
parseNumericEntity(const char *entity, size_t length)
{
	OFUnichar c;
	size_t i;
	char buffer[5];

	if (length == 1 || *entity != '#')
		return nil;

	c = 0;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
			if (entity[i] >= '0' && entity[i] <= '9')
				c = (c * 10) + (entity[i] - '0');
			else
				return nil;
		}
	}

	if ((i = of_string_utf8_encode(c, buffer)) == 0)
		return nil;
	buffer[i] = 0;

	return [OFString stringWithUTF8String: buffer
				       length: i];
}

static OFString *
parseEntities(OFString *self, id (*lookup)(void *, OFString *, OFString *),
    void *context)
{
	OFMutableString *ret;







|



|
<







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

72
73
74
75
76
77
78
			if (entity[i] >= '0' && entity[i] <= '9')
				c = (c * 10) + (entity[i] - '0');
			else
				return nil;
		}
	}

	if ((i = OFUTF8StringEncode(c, buffer)) == 0)
		return nil;
	buffer[i] = 0;

	return [OFString stringWithUTF8String: buffer length: i];

}

static OFString *
parseEntities(OFString *self, id (*lookup)(void *, OFString *, OFString *),
    void *context)
{
	OFMutableString *ret;
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
	length = self.UTF8StringLength;

	last = 0;
	inEntity = false;

	for (i = 0; i < length; i++) {
		if (!inEntity && string[i] == '&') {
			[ret appendUTF8String: string + last
				       length: i - last];

			last = i + 1;
			inEntity = true;
		} else if (inEntity && string[i] == ';') {
			const char *entity = string + last;
			size_t entityLength = i - last;

			if (entityLength == 2 && memcmp(entity, "lt", 2) == 0)
				[ret appendCString: "<"
					  encoding: OF_STRING_ENCODING_ASCII
					    length: 1];
			else if (entityLength == 2 &&
			    memcmp(entity, "gt", 2) == 0)
				[ret appendCString: ">"
					  encoding: OF_STRING_ENCODING_ASCII
					    length: 1];
			else if (entityLength == 4 &&
			    memcmp(entity, "quot", 4) == 0)
				[ret appendCString: "\""
					  encoding: OF_STRING_ENCODING_ASCII
					    length: 1];
			else if (entityLength == 4 &&
			    memcmp(entity, "apos", 4) == 0)
				[ret appendCString: "'"
					  encoding: OF_STRING_ENCODING_ASCII
					    length: 1];
			else if (entityLength == 3 &&
			    memcmp(entity, "amp", 3) == 0)
				[ret appendCString: "&"
					  encoding: OF_STRING_ENCODING_ASCII
					    length: 1];
			else if (entity[0] == '#') {
				void *pool2;
				OFString *tmp;

				pool2 = objc_autoreleasePoolPush();
				tmp = parseNumericEntity(entity,







|
<
<








|




|




|




|




|







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
	length = self.UTF8StringLength;

	last = 0;
	inEntity = false;

	for (i = 0; i < length; i++) {
		if (!inEntity && string[i] == '&') {
			[ret appendUTF8String: string + last length: i - last];


			last = i + 1;
			inEntity = true;
		} else if (inEntity && string[i] == ';') {
			const char *entity = string + last;
			size_t entityLength = i - last;

			if (entityLength == 2 && memcmp(entity, "lt", 2) == 0)
				[ret appendCString: "<"
					  encoding: OFStringEncodingASCII
					    length: 1];
			else if (entityLength == 2 &&
			    memcmp(entity, "gt", 2) == 0)
				[ret appendCString: ">"
					  encoding: OFStringEncodingASCII
					    length: 1];
			else if (entityLength == 4 &&
			    memcmp(entity, "quot", 4) == 0)
				[ret appendCString: "\""
					  encoding: OFStringEncodingASCII
					    length: 1];
			else if (entityLength == 4 &&
			    memcmp(entity, "apos", 4) == 0)
				[ret appendCString: "'"
					  encoding: OFStringEncodingASCII
					    length: 1];
			else if (entityLength == 3 &&
			    memcmp(entity, "amp", 3) == 0)
				[ret appendCString: "&"
					  encoding: OFStringEncodingASCII
					    length: 1];
			else if (entity[0] == '#') {
				void *pool2;
				OFString *tmp;

				pool2 = objc_autoreleasePoolPush();
				tmp = parseNumericEntity(entity,
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
			inEntity = false;
		}
	}

	if (inEntity)
		@throw [OFInvalidFormatException exception];

	[ret appendUTF8String: string + last
		       length: i - last];

	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

static id
lookupUsingDelegate(void *context, OFString *self, OFString *entity)
{
	id <OFStringXMLUnescapingDelegate> delegate = context;

	if (delegate == nil)
		return nil;

	return [delegate        string: self
	    containsUnknownEntityNamed: entity];
}

#ifdef OF_HAVE_BLOCKS
static id
lookupUsingBlock(void *context, OFString *self, OFString *entity)
{
	of_string_xml_unescaping_block_t block = context;

	if (block == NULL)
		return nil;

	return block(self, entity);
}
#endif







|
<
<















<
|






|







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
			inEntity = false;
		}
	}

	if (inEntity)
		@throw [OFInvalidFormatException exception];

	[ret appendUTF8String: string + last length: i - last];


	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

static id
lookupUsingDelegate(void *context, OFString *self, OFString *entity)
{
	id <OFStringXMLUnescapingDelegate> delegate = context;

	if (delegate == nil)
		return nil;


	return [delegate string: self containsUnknownEntityNamed: entity];
}

#ifdef OF_HAVE_BLOCKS
static id
lookupUsingBlock(void *context, OFString *self, OFString *entity)
{
	OFStringXMLUnescapingBlock block = context;

	if (block == NULL)
		return nil;

	return block(self, entity);
}
#endif
216
217
218
219
220
221
222
223
224
225
226
227
228
229
- (OFString *)stringByXMLUnescapingWithDelegate:
    (id <OFStringXMLUnescapingDelegate>)delegate
{
	return parseEntities(self, lookupUsingDelegate, delegate);
}

#ifdef OF_HAVE_BLOCKS
- (OFString *)stringByXMLUnescapingWithBlock:
    (of_string_xml_unescaping_block_t)block
{
	return parseEntities(self, lookupUsingBlock, block);
}
#endif
@end







|
<





208
209
210
211
212
213
214
215

216
217
218
219
220
- (OFString *)stringByXMLUnescapingWithDelegate:
    (id <OFStringXMLUnescapingDelegate>)delegate
{
	return parseEntities(self, lookupUsingDelegate, delegate);
}

#ifdef OF_HAVE_BLOCKS
- (OFString *)stringByXMLUnescapingWithBlock: (OFStringXMLUnescapingBlock)block

{
	return parseEntities(self, lookupUsingBlock, block);
}
#endif
@end

Modified src/OFString.h from [b31ea8640a] to [b34719db3c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@class OFConstantString;
@class OFString;
#else
typedef void OFString;
#endif

#if defined(__cplusplus) && __cplusplus >= 201103L
typedef char16_t of_char16_t;
typedef char32_t of_char32_t;
#else
typedef uint_least16_t of_char16_t;
typedef uint_least32_t of_char32_t;
#endif
typedef of_char32_t of_unichar_t;

/**
 * @brief The encoding of a string.
 */
typedef enum of_string_encoding_t {
	/*
	 * UTF-8 *has* to be 0, so that if the current @ref OFLocale is
	 * `nil`, `[OFLocale encoding]` returns UTF-8.
	 */
	/** UTF-8 */
	OF_STRING_ENCODING_UTF_8,
	/** ASCII */
	OF_STRING_ENCODING_ASCII,
	/** ISO 8859-1 */
	OF_STRING_ENCODING_ISO_8859_1,
	/** ISO 8859-2 */
	OF_STRING_ENCODING_ISO_8859_2,
	/** ISO 8859-3 */
	OF_STRING_ENCODING_ISO_8859_3,
	/** ISO 8859-15 */
	OF_STRING_ENCODING_ISO_8859_15,
	/** Windows-1251 */
	OF_STRING_ENCODING_WINDOWS_1251,
	/** Windows-1252 */
	OF_STRING_ENCODING_WINDOWS_1252,
	/** Codepage 437 */
	OF_STRING_ENCODING_CODEPAGE_437,
	/** Codepage 850 */
	OF_STRING_ENCODING_CODEPAGE_850,
	/** Codepage 858 */
	OF_STRING_ENCODING_CODEPAGE_858,
	/** Mac OS Roman */
	OF_STRING_ENCODING_MAC_ROMAN,
	/** KOI8-R */
	OF_STRING_ENCODING_KOI8_R,
	/** KOI8-U */
	OF_STRING_ENCODING_KOI8_U,
	/** Try to automatically detect the encoding */
	OF_STRING_ENCODING_AUTODETECT = 0xFF
} of_string_encoding_t;






enum {

	OF_STRING_SEARCH_BACKWARDS = 1,

	OF_STRING_SKIP_EMPTY	   = 2
};










#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block for enumerating the lines of a string.
 *
 * @param line The current line
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration
 */
typedef void (^of_string_line_enumeration_block_t)(OFString *line, bool *stop);
#endif

#ifdef __OBJC__
@class OFArray OF_GENERIC(ObjectType);
@class OFCharacterSet;
@class OFURL;








|
|

|
|

|




|





|

|

|

|

|

|

|

|

|

|

|

|

|

|

|
|

>
>
>
>
>
|
>
|
>
|
<
>
>
>
>
>
>
>
>
>









|







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
@class OFConstantString;
@class OFString;
#else
typedef void OFString;
#endif

#if defined(__cplusplus) && __cplusplus >= 201103L
typedef char16_t OFChar16;
typedef char32_t OFChar32;
#else
typedef uint_least16_t OFChar16;
typedef uint_least32_t OFChar32;
#endif
typedef OFChar32 OFUnichar;

/**
 * @brief The encoding of a string.
 */
typedef enum {
	/*
	 * UTF-8 *has* to be 0, so that if the current @ref OFLocale is
	 * `nil`, `[OFLocale encoding]` returns UTF-8.
	 */
	/** UTF-8 */
	OFStringEncodingUTF8,
	/** ASCII */
	OFStringEncodingASCII,
	/** ISO 8859-1 */
	OFStringEncodingISO8859_1,
	/** ISO 8859-2 */
	OFStringEncodingISO8859_2,
	/** ISO 8859-3 */
	OFStringEncodingISO8859_3,
	/** ISO 8859-15 */
	OFStringEncodingISO8859_15,
	/** Windows-1251 */
	OFStringEncodingWindows1251,
	/** Windows-1252 */
	OFStringEncodingWindows1252,
	/** Codepage 437 */
	OFStringEncodingCodepage437,
	/** Codepage 850 */
	OFStringEncodingCodepage850,
	/** Codepage 858 */
	OFStringEncodingCodepage858,
	/** Mac OS Roman */
	OFStringEncodingMacRoman,
	/** KOI8-R */
	OFStringEncodingKOI8R,
	/** KOI8-U */
	OFStringEncodingKOI8U,
	/** Try to automatically detect the encoding */
	OFStringEncodingAutodetect = 0xFF
} OFStringEncoding;

/**
 * @brief Options for searching in strings.
 *
 * This is a bit mask.
 */
typedef enum {
	/** Search backwards in the string */
	OFStringSearchBackwards = 1
} OFStringSearchOptions;


/**
 * @brief Options for separating strings.
 *
 * This is a bit mask.
 */
typedef enum {
	/** Skip empty components */
	OFStringSkipEmptyComponents = 1
} OFStringSeparationOptions;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block for enumerating the lines of a string.
 *
 * @param line The current line
 * @param stop A pointer to a variable that can be set to true to stop the
 *	       enumeration
 */
typedef void (^OFStringLineEnumerationBlock)(OFString *line, bool *stop);
#endif

#ifdef __OBJC__
@class OFArray OF_GENERIC(ObjectType);
@class OFCharacterSet;
@class OFURL;

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
/**
 * @brief The string as an array of Unicode characters.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 */
@property (readonly, nonatomic) const of_unichar_t *characters
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The string in UTF-16 encoding with native byte order.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 */
@property (readonly, nonatomic) const of_char16_t *UTF16String
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The length of the string in UTF-16 characters.
 */
@property (readonly, nonatomic) size_t UTF16StringLength;

/**
 * @brief The string in UTF-32 encoding with native byte order.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 */
@property (readonly, nonatomic) const of_char32_t *UTF32String
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The string with leading whitespaces deleted.
 */
@property (readonly, nonatomic) OFString *stringByDeletingLeadingWhitespaces;








|









|














|







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
/**
 * @brief The string as an array of Unicode characters.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 */
@property (readonly, nonatomic) const OFUnichar *characters
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The string in UTF-16 encoding with native byte order.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 */
@property (readonly, nonatomic) const OFChar16 *UTF16String
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The length of the string in UTF-16 characters.
 */
@property (readonly, nonatomic) size_t UTF16StringLength;

/**
 * @brief The string in UTF-32 encoding with native byte order.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 */
@property (readonly, nonatomic) const OFChar32 *UTF32String
    OF_RETURNS_INNER_POINTER;

/**
 * @brief The string with leading whitespaces deleted.
 */
@property (readonly, nonatomic) OFString *stringByDeletingLeadingWhitespaces;

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
+ (instancetype)stringWithUTF8String: (const char *)UTF8String
			      length: (size_t)UTF8StringLength;

/**
 * @brief Creates a new OFString from a UTF-8 encoded C string without copying
 *	  the string, if possible.
 *
 * If initialization fails for whatever reason, the passed C string is free'd
 * if `freeWhenDone` is true.
 *
 * @note OFMutableString always creates a copy!
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @param freeWhenDone Whether to free the C string when the OFString gets
 *		       deallocated
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF8StringNoCopy: (char *)UTF8String
			      freeWhenDone: (bool)freeWhenDone;

/**
 * @brief Creates a new OFString from a UTF-8 encoded C string with the
 *	  specified length without copying the string, if possible.
 *
 * If initialization fails for whatever reason, the passed C string is free'd
 * if `freeWhenDone` is true.
 *
 * @note OFMutableString always creates a copy!
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @param UTF8StringLength The length of the UTF-8 encoded C string
 * @param freeWhenDone Whether to free the C string when the OFString gets
 *		       deallocated







|
|















|
|







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
+ (instancetype)stringWithUTF8String: (const char *)UTF8String
			      length: (size_t)UTF8StringLength;

/**
 * @brief Creates a new OFString from a UTF-8 encoded C string without copying
 *	  the string, if possible.
 *
 * If initialization fails for whatever reason, the passed C string is *not*
 * free'd if `freeWhenDone` is true.
 *
 * @note OFMutableString always creates a copy!
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @param freeWhenDone Whether to free the C string when the OFString gets
 *		       deallocated
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF8StringNoCopy: (char *)UTF8String
			      freeWhenDone: (bool)freeWhenDone;

/**
 * @brief Creates a new OFString from a UTF-8 encoded C string with the
 *	  specified length without copying the string, if possible.
 *
 * If initialization fails for whatever reason, the passed C string is *not*
 * free'd if `freeWhenDone` is true.
 *
 * @note OFMutableString always creates a copy!
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @param UTF8StringLength The length of the UTF-8 encoded C string
 * @param freeWhenDone Whether to free the C string when the OFString gets
 *		       deallocated
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
 * @brief Creates a new OFString from a C string with the specified encoding.
 *
 * @param cString A C string to initialize the OFString with
 * @param encoding The encoding of the C string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithCString: (const char *)cString
			 encoding: (of_string_encoding_t)encoding;

/**
 * @brief Creates a new OFString from a C string with the specified encoding
 *	  and length.
 *
 * @param cString A C string to initialize the OFString with
 * @param encoding The encoding of the C string
 * @param cStringLength The length of the C string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithCString: (const char *)cString
			 encoding: (of_string_encoding_t)encoding
			   length: (size_t)cStringLength;

/**
 * @brief Creates a new OFString from OFData with the specified encoding.
 *
 * @param data OFData with the contents of the string
 * @param encoding The encoding in which the string is stored in the OFData
 * @return An new autoreleased OFString
 */
+ (instancetype)stringWithData: (OFData *)data
		      encoding: (of_string_encoding_t)encoding;

/**
 * @brief Creates a new OFString from another string.
 *
 * @param string A string to initialize the OFString with
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithString: (OFString *)string;

/**
 * @brief Creates a new OFString from a Unicode string with the specified
 *	  length.
 *
 * @param characters An array of Unicode characters
 * @param length The length of the Unicode character array
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithCharacters: (const of_unichar_t *)characters
			      length: (size_t)length;

/**
 * @brief Creates a new OFString from a UTF-16 encoded string.
 *
 * @param string The UTF-16 string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF16String: (const of_char16_t *)string;

/**
 * @brief Creates a new OFString from a UTF-16 encoded string with the
 *	  specified length.
 *
 * @param string The UTF-16 string
 * @param length The length of the UTF-16 string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF16String: (const of_char16_t *)string
			       length: (size_t)length;

/**
 * @brief Creates a new OFString from a UTF-16 encoded string, assuming the
 *	  specified byte order if no byte order mark is found.
 *
 * @param string The UTF-16 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF16String: (const of_char16_t *)string
			    byteOrder: (of_byte_order_t)byteOrder;

/**
 * @brief Creates a new OFString from a UTF-16 encoded string with the
 *	  specified length, assuming the specified byte order if no byte order
 *	  mark is found.
 *
 * @param string The UTF-16 string
 * @param length The length of the UTF-16 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF16String: (const of_char16_t *)string
			       length: (size_t)length
			    byteOrder: (of_byte_order_t)byteOrder;

/**
 * @brief Creates a new OFString from a UTF-32 encoded string.
 *
 * @param string The UTF-32 string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF32String: (const of_char32_t *)string;

/**
 * @brief Creates a new OFString from a UTF-32 encoded string with the
 *	  specified length.
 *
 * @param string The UTF-32 string
 * @param length The length of the UTF-32 string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF32String: (const of_char32_t *)string
			       length: (size_t)length;

/**
 * @brief Creates a new OFString from a UTF-32 encoded string, assuming the
 *	  specified byte order if no byte order mark is found.
 *
 * @param string The UTF-32 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF32String: (const of_char32_t *)string
			    byteOrder: (of_byte_order_t)byteOrder;

/**
 * @brief Creates a new OFString from a UTF-32 encoded string with the
 *	  specified length, assuming the specified byte order if no byte order
 *	  mark is found.
 *
 * @param string The UTF-32 string
 * @param length The length of the UTF-32 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF32String: (const of_char32_t *)string
			       length: (size_t)length
			    byteOrder: (of_byte_order_t)byteOrder;

/**
 * @brief Creates a new OFString from a format string.
 *
 * See printf for the format syntax. As an addition, `%@` is available as
 * format specifier for objects, `%C` for `of_unichar_t` and `%S` for
 * `const of_unichar_t *`.
 *
 * @param format A string used as format to initialize the OFString
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithFormat: (OFConstantString *)format, ...;

# ifdef OF_HAVE_FILES







|











|










|

















|








|









|










|
|











|

|







|









|










|
|











|

|





|
|







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
 * @brief Creates a new OFString from a C string with the specified encoding.
 *
 * @param cString A C string to initialize the OFString with
 * @param encoding The encoding of the C string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithCString: (const char *)cString
			 encoding: (OFStringEncoding)encoding;

/**
 * @brief Creates a new OFString from a C string with the specified encoding
 *	  and length.
 *
 * @param cString A C string to initialize the OFString with
 * @param encoding The encoding of the C string
 * @param cStringLength The length of the C string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithCString: (const char *)cString
			 encoding: (OFStringEncoding)encoding
			   length: (size_t)cStringLength;

/**
 * @brief Creates a new OFString from OFData with the specified encoding.
 *
 * @param data OFData with the contents of the string
 * @param encoding The encoding in which the string is stored in the OFData
 * @return An new autoreleased OFString
 */
+ (instancetype)stringWithData: (OFData *)data
		      encoding: (OFStringEncoding)encoding;

/**
 * @brief Creates a new OFString from another string.
 *
 * @param string A string to initialize the OFString with
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithString: (OFString *)string;

/**
 * @brief Creates a new OFString from a Unicode string with the specified
 *	  length.
 *
 * @param characters An array of Unicode characters
 * @param length The length of the Unicode character array
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithCharacters: (const OFUnichar *)characters
			      length: (size_t)length;

/**
 * @brief Creates a new OFString from a UTF-16 encoded string.
 *
 * @param string The UTF-16 string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF16String: (const OFChar16 *)string;

/**
 * @brief Creates a new OFString from a UTF-16 encoded string with the
 *	  specified length.
 *
 * @param string The UTF-16 string
 * @param length The length of the UTF-16 string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
			       length: (size_t)length;

/**
 * @brief Creates a new OFString from a UTF-16 encoded string, assuming the
 *	  specified byte order if no byte order mark is found.
 *
 * @param string The UTF-16 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
			    byteOrder: (OFByteOrder)byteOrder;

/**
 * @brief Creates a new OFString from a UTF-16 encoded string with the
 *	  specified length, assuming the specified byte order if no byte order
 *	  mark is found.
 *
 * @param string The UTF-16 string
 * @param length The length of the UTF-16 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
			       length: (size_t)length
			    byteOrder: (OFByteOrder)byteOrder;

/**
 * @brief Creates a new OFString from a UTF-32 encoded string.
 *
 * @param string The UTF-32 string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF32String: (const OFChar32 *)string;

/**
 * @brief Creates a new OFString from a UTF-32 encoded string with the
 *	  specified length.
 *
 * @param string The UTF-32 string
 * @param length The length of the UTF-32 string
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
			       length: (size_t)length;

/**
 * @brief Creates a new OFString from a UTF-32 encoded string, assuming the
 *	  specified byte order if no byte order mark is found.
 *
 * @param string The UTF-32 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
			    byteOrder: (OFByteOrder)byteOrder;

/**
 * @brief Creates a new OFString from a UTF-32 encoded string with the
 *	  specified length, assuming the specified byte order if no byte order
 *	  mark is found.
 *
 * @param string The UTF-32 string
 * @param length The length of the UTF-32 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
			       length: (size_t)length
			    byteOrder: (OFByteOrder)byteOrder;

/**
 * @brief Creates a new OFString from a format string.
 *
 * 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 string used as format to initialize the OFString
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithFormat: (OFConstantString *)format, ...;

# ifdef OF_HAVE_FILES
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
 *	  specified encoding.
 *
 * @param path The path to the file
 * @param encoding The encoding of the file
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithContentsOfFile: (OFString *)path
				encoding: (of_string_encoding_t)encoding;
# endif

# if defined(OF_HAVE_FILES) || defined(OF_HAVE_SOCKETS)
/**
 * @brief Creates a new OFString with the contents of the specified URL.
 *
 * If the URL's scheme is file, it tries UTF-8 encoding.
 *
 * If the URL's scheme is http(s), it tries to detect the encoding from the HTTP
 * headers. If it could not detect the encoding using the HTTP headers, it tries







|


<







525
526
527
528
529
530
531
532
533
534

535
536
537
538
539
540
541
 *	  specified encoding.
 *
 * @param path The path to the file
 * @param encoding The encoding of the file
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithContentsOfFile: (OFString *)path
				encoding: (OFStringEncoding)encoding;
# endif


/**
 * @brief Creates a new OFString with the contents of the specified URL.
 *
 * If the URL's scheme is file, it tries UTF-8 encoding.
 *
 * If the URL's scheme is http(s), it tries to detect the encoding from the HTTP
 * headers. If it could not detect the encoding using the HTTP headers, it tries
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
 *	  specified encoding.
 *
 * @param URL The URL to the contents for the string
 * @param encoding The encoding to assume
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithContentsOfURL: (OFURL *)URL
			       encoding: (of_string_encoding_t)encoding;
# endif

/**
 * @brief Initializes an already allocated OFString from a UTF-8 encoded C
 *	  string.
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @return An initialized OFString







|
<







551
552
553
554
555
556
557
558

559
560
561
562
563
564
565
 *	  specified encoding.
 *
 * @param URL The URL to the contents for the string
 * @param encoding The encoding to assume
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithContentsOfURL: (OFURL *)URL
			       encoding: (OFStringEncoding)encoding;


/**
 * @brief Initializes an already allocated OFString from a UTF-8 encoded C
 *	  string.
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @return An initialized OFString
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
- (instancetype)initWithUTF8String: (const char *)UTF8String
			    length: (size_t)UTF8StringLength;

/**
 * @brief Initializes an already allocated OFString from an UTF-8 encoded C
 *	  string without copying the string, if possible.
 *
 * If initialization fails for whatever reason, the passed C string is free'd
 * if `freeWhenDone` is true.
 *
 * @note OFMutableString always creates a copy!
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @param freeWhenDone Whether to free the C string when it is not needed
 *		       anymore
 * @return An initialized OFString
 */
- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
			    freeWhenDone: (bool)freeWhenDone;

/**
 * @brief Initializes an already allocated OFString from an UTF-8 encoded C
 *	  string with the specified length without copying the string, if
 *	  possible.
 *
 * If initialization fails for whatever reason, the passed C string is free'd
 * if `freeWhenDone` is true.
 *
 * @note OFMutableString always creates a copy!
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @param UTF8StringLength The length of the UTF-8 encoded C string
 * @param freeWhenDone Whether to free the C string when it is not needed
 *		       anymore







|
|
















|
|







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
- (instancetype)initWithUTF8String: (const char *)UTF8String
			    length: (size_t)UTF8StringLength;

/**
 * @brief Initializes an already allocated OFString from an UTF-8 encoded C
 *	  string without copying the string, if possible.
 *
 * If initialization fails for whatever reason, the passed C string is *not*
 * free'd if `freeWhenDone` is true.
 *
 * @note OFMutableString always creates a copy!
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @param freeWhenDone Whether to free the C string when it is not needed
 *		       anymore
 * @return An initialized OFString
 */
- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
			    freeWhenDone: (bool)freeWhenDone;

/**
 * @brief Initializes an already allocated OFString from an UTF-8 encoded C
 *	  string with the specified length without copying the string, if
 *	  possible.
 *
 * If initialization fails for whatever reason, the passed C string is *not*
 * free'd if `freeWhenDone` is true.
 *
 * @note OFMutableString always creates a copy!
 *
 * @param UTF8String A UTF-8 encoded C string to initialize the OFString with
 * @param UTF8StringLength The length of the UTF-8 encoded C string
 * @param freeWhenDone Whether to free the C string when it is not needed
 *		       anymore
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
761
762
763
764
765
766
767
768
769
770
771
772
 *	  specified encoding.
 *
 * @param cString A C string to initialize the OFString with
 * @param encoding The encoding of the C string
 * @return An initialized OFString
 */
- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding;

/**
 * @brief Initializes an already allocated OFString from a C string with the
 *	  specified encoding and length.
 *
 * @param cString A C string to initialize the OFString with
 * @param encoding The encoding of the C string
 * @param cStringLength The length of the C string
 * @return An initialized OFString
 */
- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
			 length: (size_t)cStringLength;

/**
 * @brief Initializes an already allocated OFString from OFData with the
 *	  specified encoding.
 *
 * @param data OFData with the contents of the string
 * @param encoding The encoding in which the string is stored in the OFData
 * @return An initialized OFString
 */
- (instancetype)initWithData: (OFData *)data
		    encoding: (of_string_encoding_t)encoding;

/**
 * @brief Initializes an already allocated OFString with another string.
 *
 * @param string A string to initialize the OFString with
 * @return An initialized OFString
 */
- (instancetype)initWithString: (OFString *)string;

/**
 * @brief Initializes an already allocated OFString with a Unicode string with
 *	  the specified length.
 *
 * @param characters An array of Unicode characters
 * @param length The length of the Unicode character array
 * @return An initialized OFString
 */
- (instancetype)initWithCharacters: (const of_unichar_t *)characters
			    length: (size_t)length;

/**
 * @brief Initializes an already allocated OFString with a UTF-16 string.
 *
 * @param string The UTF-16 string
 * @return An initialized OFString
 */
- (instancetype)initWithUTF16String: (const of_char16_t *)string;

/**
 * @brief Initializes an already allocated OFString with a UTF-16 string with
 *	  the specified length.
 *
 * @param string The UTF-16 string
 * @param length The length of the UTF-16 string
 * @return An initialized OFString
 */
- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length;

/**
 * @brief Initializes an already allocated OFString with a UTF-16 string,
 *	  assuming the specified byte order if no byte order mark is found.
 *
 * @param string The UTF-16 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return An initialized OFString
 */
- (instancetype)initWithUTF16String: (const of_char16_t *)string
			  byteOrder: (of_byte_order_t)byteOrder;

/**
 * @brief Initializes an already allocated OFString with a UTF-16 string with
 *	  the specified length, assuming the specified byte order if no byte
 *	  order mark is found.
 *
 * @param string The UTF-16 string
 * @param length The length of the UTF-16 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return An initialized OFString
 */
- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder;

/**
 * @brief Initializes an already allocated OFString with a UTF-32 string.
 *
 * @param string The UTF-32 string
 * @return An initialized OFString
 */
- (instancetype)initWithUTF32String: (const of_char32_t *)string;

/**
 * @brief Initializes an already allocated OFString with a UTF-32 string with
 *	  the specified length
 *
 * @param string The UTF-32 string
 * @param length The length of the UTF-32 string
 * @return An initialized OFString
 */
- (instancetype)initWithUTF32String: (const of_char32_t *)string
			     length: (size_t)length;

/**
 * @brief Initializes an already allocated OFString with a UTF-32 string,
 *	  assuming the specified byte order if no byte order mark is found.
 *
 * @param string The UTF-32 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return An initialized OFString
 */
- (instancetype)initWithUTF32String: (const of_char32_t *)string
			  byteOrder: (of_byte_order_t)byteOrder;

/**
 * @brief Initializes an already allocated OFString with a UTF-32 string with
 *	  the specified length, assuming the specified byte order if no byte
 *	  order mark is found.
 *
 * @param string The UTF-32 string
 * @param length The length of the UTF-32 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return An initialized OFString
 */
- (instancetype)initWithUTF32String: (const of_char32_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder;

/**
 * @brief Initializes an already allocated OFString with a format string.
 *
 * See printf for the format syntax. As an addition, `%@` is available as
 * format specifier for objects, `%C` for `of_unichar_t` and `%S` for
 * `const of_unichar_t *`.
 *
 * @param format A string used as format to initialize the OFString
 * @return An initialized OFString
 */
- (instancetype)initWithFormat: (OFConstantString *)format, ...;

/**
 * @brief Initializes an already allocated OFString with a format string.
 *
 * See printf for the format syntax. As an addition, `%@` is available as
 * format specifier for objects, `%C` for `of_unichar_t` and `%S` for
 * `const of_unichar_t *`.
 *
 * @param format A string used as format to initialize the OFString
 * @param arguments The arguments used in the format string
 * @return An initialized OFString
 */
- (instancetype)initWithFormat: (OFConstantString *)format
		     arguments: (va_list)arguments;







|











|











|

















|








|









|










|
|











|

|







|









|










|
|











|

|





|
|










|
|







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
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
 *	  specified encoding.
 *
 * @param cString A C string to initialize the OFString with
 * @param encoding The encoding of the C string
 * @return An initialized OFString
 */
- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding;

/**
 * @brief Initializes an already allocated OFString from a C string with the
 *	  specified encoding and length.
 *
 * @param cString A C string to initialize the OFString with
 * @param encoding The encoding of the C string
 * @param cStringLength The length of the C string
 * @return An initialized OFString
 */
- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
			 length: (size_t)cStringLength;

/**
 * @brief Initializes an already allocated OFString from OFData with the
 *	  specified encoding.
 *
 * @param data OFData with the contents of the string
 * @param encoding The encoding in which the string is stored in the OFData
 * @return An initialized OFString
 */
- (instancetype)initWithData: (OFData *)data
		    encoding: (OFStringEncoding)encoding;

/**
 * @brief Initializes an already allocated OFString with another string.
 *
 * @param string A string to initialize the OFString with
 * @return An initialized OFString
 */
- (instancetype)initWithString: (OFString *)string;

/**
 * @brief Initializes an already allocated OFString with a Unicode string with
 *	  the specified length.
 *
 * @param characters An array of Unicode characters
 * @param length The length of the Unicode character array
 * @return An initialized OFString
 */
- (instancetype)initWithCharacters: (const OFUnichar *)characters
			    length: (size_t)length;

/**
 * @brief Initializes an already allocated OFString with a UTF-16 string.
 *
 * @param string The UTF-16 string
 * @return An initialized OFString
 */
- (instancetype)initWithUTF16String: (const OFChar16 *)string;

/**
 * @brief Initializes an already allocated OFString with a UTF-16 string with
 *	  the specified length.
 *
 * @param string The UTF-16 string
 * @param length The length of the UTF-16 string
 * @return An initialized OFString
 */
- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length;

/**
 * @brief Initializes an already allocated OFString with a UTF-16 string,
 *	  assuming the specified byte order if no byte order mark is found.
 *
 * @param string The UTF-16 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return An initialized OFString
 */
- (instancetype)initWithUTF16String: (const OFChar16 *)string
			  byteOrder: (OFByteOrder)byteOrder;

/**
 * @brief Initializes an already allocated OFString with a UTF-16 string with
 *	  the specified length, assuming the specified byte order if no byte
 *	  order mark is found.
 *
 * @param string The UTF-16 string
 * @param length The length of the UTF-16 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return An initialized OFString
 */
- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder;

/**
 * @brief Initializes an already allocated OFString with a UTF-32 string.
 *
 * @param string The UTF-32 string
 * @return An initialized OFString
 */
- (instancetype)initWithUTF32String: (const OFChar32 *)string;

/**
 * @brief Initializes an already allocated OFString with a UTF-32 string with
 *	  the specified length
 *
 * @param string The UTF-32 string
 * @param length The length of the UTF-32 string
 * @return An initialized OFString
 */
- (instancetype)initWithUTF32String: (const OFChar32 *)string
			     length: (size_t)length;

/**
 * @brief Initializes an already allocated OFString with a UTF-32 string,
 *	  assuming the specified byte order if no byte order mark is found.
 *
 * @param string The UTF-32 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return An initialized OFString
 */
- (instancetype)initWithUTF32String: (const OFChar32 *)string
			  byteOrder: (OFByteOrder)byteOrder;

/**
 * @brief Initializes an already allocated OFString with a UTF-32 string with
 *	  the specified length, assuming the specified byte order if no byte
 *	  order mark is found.
 *
 * @param string The UTF-32 string
 * @param length The length of the UTF-32 string
 * @param byteOrder The byte order to assume if there is no byte order mark
 * @return An initialized OFString
 */
- (instancetype)initWithUTF32String: (const OFChar32 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder;

/**
 * @brief Initializes an already allocated OFString with a format string.
 *
 * 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 string used as format to initialize the OFString
 * @return An initialized OFString
 */
- (instancetype)initWithFormat: (OFConstantString *)format, ...;

/**
 * @brief Initializes an already allocated OFString with a format string.
 *
 * 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 string used as format to initialize the OFString
 * @param arguments The arguments used in the format string
 * @return An initialized OFString
 */
- (instancetype)initWithFormat: (OFConstantString *)format
		     arguments: (va_list)arguments;
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
 *	  specified file in the specified encoding.
 *
 * @param path The path to the file
 * @param encoding The encoding of the file
 * @return An initialized OFString
 */
- (instancetype)initWithContentsOfFile: (OFString *)path
			      encoding: (of_string_encoding_t)encoding;
# endif

/**
 * @brief Initializes an already allocated OFString with the contents of the
 *	  specified URL.
 *
 * If the URL's scheme is file, it tries UTF-8 encoding.







|







797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
 *	  specified file in the specified encoding.
 *
 * @param path The path to the file
 * @param encoding The encoding of the file
 * @return An initialized OFString
 */
- (instancetype)initWithContentsOfFile: (OFString *)path
			      encoding: (OFStringEncoding)encoding;
# endif

/**
 * @brief Initializes an already allocated OFString with the contents of the
 *	  specified URL.
 *
 * If the URL's scheme is file, it tries UTF-8 encoding.
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
 *	  specified URL in the specified encoding.
 *
 * @param URL The URL to the contents for the string
 * @param encoding The encoding to assume
 * @return An initialized OFString
 */
- (instancetype)initWithContentsOfURL: (OFURL *)URL
			     encoding: (of_string_encoding_t)encoding;

/**
 * @brief Writes the OFString into the specified C string with the specified
 *	  encoding.
 *
 * @param cString The C string to write into
 * @param maxLength The maximum number of bytes to write into the C string,
 *		    including the terminating zero
 * @param encoding The encoding to use for writing into the C string
 * @return The number of bytes written into the C string, without the
 *	   terminating zero
 */
- (size_t)getCString: (char *)cString
	   maxLength: (size_t)maxLength
	    encoding: (of_string_encoding_t)encoding;

/**
 * @brief Writes the OFString into the specified C string with the specified
 *	  encoding, replacing characters that cannot be represented in the
 *	  specified encoding with a question mark.
 *
 * @param cString The C string to write into
 * @param maxLength The maximum number of bytes to write into the C string,
 *		    including the terminating zero
 * @param encoding The encoding to use for writing into the C string
 * @return The number of bytes written into the C string, without the
 *	   terminating zero
 */
- (size_t)getLossyCString: (char *)cString
		maxLength: (size_t)maxLength
		 encoding: (of_string_encoding_t)encoding;

/**
 * @brief Returns the OFString as a C string in the specified encoding.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 *
 * @param encoding The encoding for the C string
 * @return The OFString as a C string in the specified encoding
 */
- (const char *)cStringWithEncoding: (of_string_encoding_t)encoding
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the OFString as a C string in the specified encoding,
 *	  replacing characters that cannot be represented in the specified
 *	  encoding with a question mark.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 *
 * @param encoding The encoding for the C string
 * @return The OFString as a C string in the specified encoding
 */
- (const char *)lossyCStringWithEncoding: (of_string_encoding_t)encoding
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the number of bytes the string needs in the specified
 *	  encoding.
 *
 * @param encoding The encoding for the string
 * @return The number of bytes the string needs in the specified encoding.
 */
- (size_t)cStringLengthWithEncoding: (of_string_encoding_t)encoding;

/**
 * @brief Compares the OFString to another OFString without caring about the





 *	  case.


 *
 * @param otherString A string to compare with
 * @return An of_comparison_result_t
 */
- (of_comparison_result_t)caseInsensitiveCompare: (OFString *)otherString;

/**
 * @brief Returns the Unicode character at the specified index.
 *
 * @param index The index of the Unicode character to return
 * @return The Unicode character at the specified index
 */
- (of_unichar_t)characterAtIndex: (size_t)index;

/**
 * @brief Copies the Unicode characters in the specified range to the specified
 *	  buffer.
 *
 * @param buffer The buffer to store the Unicode characters
 * @param range The range of the Unicode characters to copy
 */
- (void)getCharacters: (of_unichar_t *)buffer
	      inRange: (of_range_t)range;

/**
 * @brief Returns the range of the first occurrence of the string.
 *
 * @param string The string to search
 * @return The range of the first occurrence of the string or a range with
 *	   `OF_NOT_FOUND` as start position if it was not found
 */
- (of_range_t)rangeOfString: (OFString *)string;

/**
 * @brief Returns the range of the string.
 *
 * @param string The string to search
 * @param options Options modifying search behavior.@n
 *		  Possible values are:
 *		  Value                        | Description
 *		  -----------------------------|-------------------------------
 *		  `OF_STRING_SEARCH_BACKWARDS` | Search backwards in the string
 * @return The range of the first occurrence of the string or a range with
 *	   `OF_NOT_FOUND` as start position if it was not found
 */
- (of_range_t)rangeOfString: (OFString *)string
		    options: (int)options;

/**
 * @brief Returns the range of the string in the specified range.
 *
 * @param string The string to search
 * @param options Options modifying search behaviour.@n
 *		  Possible values are:
 *		  Value                        | Description
 *		  -----------------------------|-------------------------------
 *		  `OF_STRING_SEARCH_BACKWARDS` | Search backwards in the string
 * @param range The range in which to search
 * @return The range of the first occurrence of the string or a range with
 *	   `OF_NOT_FOUND` as start position if it was not found
 */
- (of_range_t)rangeOfString: (OFString *)string
		    options: (int)options
		      range: (of_range_t)range;

/**
 * @brief Returns the index of the first character from the set.
 *
 * @param characterSet The set of characters to search for
 * @return The index of the first occurrence of a character from the set or
 *	   `OF_NOT_FOUND` if it was not found
 */
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet;

/**
 * @brief Returns the index of the first character from the set.
 *
 * @param characterSet The set of characters to search for
 * @param options Options modifying search behaviour.@n
 *		  Possible values are:
 *		  Value                        | Description
 *		  -----------------------------|-------------------------------
 *		  `OF_STRING_SEARCH_BACKWARDS` | Search backwards in the string
 * @return The index of the first occurrence of a character from the set or
 *	   `OF_NOT_FOUND` if it was not found
 */
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (int)options;

/**
 * @brief Returns the index of the first character from the set.
 *
 * @param characterSet The set of characters to search for
 * @param options Options modifying search behaviour.@n
 *		  Possible values are:
 *		  Value                        | Description
 *		  -----------------------------|-------------------------------
 *		  `OF_STRING_SEARCH_BACKWARDS` | Search backwards in the string
 * @param range The range in which to search
 * @return The index of the first occurrence of a character from the set or
 *	   `OF_NOT_FOUND` if it was not found
 */
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (int)options
			    range: (of_range_t)range;

/**
 * @brief Returns whether the string contains the specified string.
 *
 * @param string The string to search
 * @return Whether the string contains the specified string
 */







|














|















|











|














|









|


|
>
>
>
>
>
|
>
>

|
|

|







|








|
<






|

|





|
<
<
<
<

|

|
|





|
<
<
<
<


|

|
|
|






|







|
<
<
<
<

|


|





|
<
<
<
<


|


|
|







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
 *	  specified URL in the specified encoding.
 *
 * @param URL The URL to the contents for the string
 * @param encoding The encoding to assume
 * @return An initialized OFString
 */
- (instancetype)initWithContentsOfURL: (OFURL *)URL
			     encoding: (OFStringEncoding)encoding;

/**
 * @brief Writes the OFString into the specified C string with the specified
 *	  encoding.
 *
 * @param cString The C string to write into
 * @param maxLength The maximum number of bytes to write into the C string,
 *		    including the terminating zero
 * @param encoding The encoding to use for writing into the C string
 * @return The number of bytes written into the C string, without the
 *	   terminating zero
 */
- (size_t)getCString: (char *)cString
	   maxLength: (size_t)maxLength
	    encoding: (OFStringEncoding)encoding;

/**
 * @brief Writes the OFString into the specified C string with the specified
 *	  encoding, replacing characters that cannot be represented in the
 *	  specified encoding with a question mark.
 *
 * @param cString The C string to write into
 * @param maxLength The maximum number of bytes to write into the C string,
 *		    including the terminating zero
 * @param encoding The encoding to use for writing into the C string
 * @return The number of bytes written into the C string, without the
 *	   terminating zero
 */
- (size_t)getLossyCString: (char *)cString
		maxLength: (size_t)maxLength
		 encoding: (OFStringEncoding)encoding;

/**
 * @brief Returns the OFString as a C string in the specified encoding.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 *
 * @param encoding The encoding for the C string
 * @return The OFString as a C string in the specified encoding
 */
- (const char *)cStringWithEncoding: (OFStringEncoding)encoding
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the OFString as a C string in the specified encoding,
 *	  replacing characters that cannot be represented in the specified
 *	  encoding with a question mark.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 *
 * @param encoding The encoding for the C string
 * @return The OFString as a C string in the specified encoding
 */
- (const char *)lossyCStringWithEncoding: (OFStringEncoding)encoding
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the number of bytes the string needs in the specified
 *	  encoding.
 *
 * @param encoding The encoding for the string
 * @return The number of bytes the string needs in the specified encoding.
 */
- (size_t)cStringLengthWithEncoding: (OFStringEncoding)encoding;

/**
 * @brief Compares the string to another string.
 *
 * @param string The string to compare the string to
 * @return The result of the comparison
 */
- (OFComparisonResult)compare: (OFString *)string;

/**
 * @brief Compares the string to another string without caring about the case.
 *
 * @param string The string to compare the string to
 * @return The result of the comparison
 */
- (OFComparisonResult)caseInsensitiveCompare: (OFString *)string;

/**
 * @brief Returns the Unicode character at the specified index.
 *
 * @param index The index of the Unicode character to return
 * @return The Unicode character at the specified index
 */
- (OFUnichar)characterAtIndex: (size_t)index;

/**
 * @brief Copies the Unicode characters in the specified range to the specified
 *	  buffer.
 *
 * @param buffer The buffer to store the Unicode characters
 * @param range The range of the Unicode characters to copy
 */
- (void)getCharacters: (OFUnichar *)buffer inRange: (OFRange)range;


/**
 * @brief Returns the range of the first occurrence of the string.
 *
 * @param string The string to search
 * @return The range of the first occurrence of the string or a range with
 *	   `OFNotFound` as start position if it was not found
 */
- (OFRange)rangeOfString: (OFString *)string;

/**
 * @brief Returns the range of the string.
 *
 * @param string The string to search
 * @param options Options modifying search behavior




 * @return The range of the first occurrence of the string or a range with
 *	   `OFNotFound` as start position if it was not found
 */
- (OFRange)rangeOfString: (OFString *)string
		 options: (OFStringSearchOptions)options;

/**
 * @brief Returns the range of the string in the specified range.
 *
 * @param string The string to search
 * @param options Options modifying search behaviour




 * @param range The range in which to search
 * @return The range of the first occurrence of the string or a range with
 *	   `OFNotFound` as start position if it was not found
 */
- (OFRange)rangeOfString: (OFString *)string
		 options: (OFStringSearchOptions)options
		   range: (OFRange)range;

/**
 * @brief Returns the index of the first character from the set.
 *
 * @param characterSet The set of characters to search for
 * @return The index of the first occurrence of a character from the set or
 *	   `OFNotFound` if it was not found
 */
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet;

/**
 * @brief Returns the index of the first character from the set.
 *
 * @param characterSet The set of characters to search for
 * @param options Options modifying search behaviour




 * @return The index of the first occurrence of a character from the set or
 *	   `OFNotFound` if it was not found
 */
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (OFStringSearchOptions)options;

/**
 * @brief Returns the index of the first character from the set.
 *
 * @param characterSet The set of characters to search for
 * @param options Options modifying search behaviour




 * @param range The range in which to search
 * @return The index of the first occurrence of a character from the set or
 *	   `OFNotFound` if it was not found
 */
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (OFStringSearchOptions)options
			    range: (OFRange)range;

/**
 * @brief Returns whether the string contains the specified string.
 *
 * @param string The string to search
 * @return Whether the string contains the specified string
 */
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036

/**
 * @brief Creates a substring with the specified range.
 *
 * @param range The range of the substring
 * @return The substring as a new autoreleased OFString
 */
- (OFString *)substringWithRange: (of_range_t)range;

/**
 * @brief The value of the string in the specified base as a `long long`.
 *
 * Leading and trailing whitespaces are ignored.
 *
 * If the string contains any non-number characters, an







|







1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037

/**
 * @brief Creates a substring with the specified range.
 *
 * @param range The range of the substring
 * @return The substring as a new autoreleased OFString
 */
- (OFString *)substringWithRange: (OFRange)range;

/**
 * @brief The value of the string in the specified base as a `long long`.
 *
 * Leading and trailing whitespaces are ignored.
 *
 * If the string contains any non-number characters, an
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
 *		    * None yet
 * @param range The range in which to replace the string
 * @return A new string with the occurrences of the specified string replaced
 */
- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
					   options: (int)options
					     range: (of_range_t)range;

/**
 * @brief Checks whether the string has the specified prefix.
 *
 * @param prefix The prefix to check for
 * @return A boolean whether the string has the specified prefix
 */







|







1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
 *		    * None yet
 * @param range The range in which to replace the string
 * @return A new string with the occurrences of the specified string replaced
 */
- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
					   options: (int)options
					     range: (OFRange)range;

/**
 * @brief Checks whether the string has the specified prefix.
 *
 * @param prefix The prefix to check for
 * @return A boolean whether the string has the specified prefix
 */
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
    componentsSeparatedByString: (OFString *)delimiter;

/**
 * @brief Separates the string into an array of strings, split by the specified
 *	  delimiter.
 *
 * @param delimiter The delimiter for separating
 * @param options Options according to which the string should be separated.@n
 *		  Possible values are:
 *		  Value                  | Description
 *		  -----------------------|----------------------
 * 		  `OF_STRING_SKIP_EMPTY` | Skip empty components
 * @return An autoreleased OFArray with the separated string
 */
- (OFArray OF_GENERIC(OFString *) *)
    componentsSeparatedByString: (OFString *)delimiter
			options: (int)options;

/**
 * @brief Separates the string into an array of strings, split by characters in
 *	  the specified set.
 *
 * @param characterSet The character set for separating
 * @return An autoreleased OFArray with the separated string
 */
- (OFArray OF_GENERIC(OFString *) *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet;

/**
 * @brief Separates the string into an array of strings, split by characters in
 *	  the specified set.
 *
 * @param characterSet The character set for separating
 * @param options Options according to which the string should be separated.@n
 *		  Possible values are:
 *		  Value                  | Description
 *		  -----------------------|----------------------
 * 		  `OF_STRING_SKIP_EMPTY` | Skip empty components
 * @return An autoreleased OFArray with the separated string
 */
- (OFArray OF_GENERIC(OFString *) *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
				 options: (int)options;

/**
 * @brief Returns the string in UTF-16 encoding with the specified byte order.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 *
 * @param byteOrder The byte order for the UTF-16 encoding
 * @return The string in UTF-16 encoding with the specified byte order
 */
- (const of_char16_t *)UTF16StringWithByteOrder: (of_byte_order_t)byteOrder
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the string in UTF-32 encoding with the specified byte order.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 *
 * @param byteOrder The byte order for the UTF-32 encoding
 * @return The string in UTF-32 encoding with the specified byte order
 */
- (const of_char32_t *)UTF32StringWithByteOrder: (of_byte_order_t)byteOrder
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the string as OFData with the specified encoding.
 *
 * @param encoding The encoding to use for the returned OFData
 * @return The string as OFData with the specified encoding
 */
- (OFData *)dataWithEncoding: (of_string_encoding_t)encoding;

# ifdef OF_HAVE_FILES
/**
 * @brief Writes the string into the specified file using UTF-8 encoding.
 *
 * @param path The path of the file to write to
 */
- (void)writeToFile: (OFString *)path;

/**
 * @brief Writes the string into the specified file using the specified
 *	  encoding.
 *
 * @param path The path of the file to write to
 * @param encoding The encoding to use to write the string into the file
 */
- (void)writeToFile: (OFString *)path
	   encoding: (of_string_encoding_t)encoding;
# endif

/**
 * @brief Writes the string to the specified URL using UTF-8 encoding.
 *
 * @param URL The URL to write to
 */
- (void)writeToURL: (OFURL *)URL;

/**
 * @brief Writes the string to the specified URL using the specified encoding.
 *
 * @param URL The URL to write to
 * @param encoding The encoding to use to write the string to the URL
 */
- (void)writeToURL: (OFURL *)URL
	  encoding: (of_string_encoding_t)encoding;

# ifdef OF_HAVE_BLOCKS
/**
 * Enumerates all lines in the receiver using the specified block.
 *
 * @brief block The block to call for each line
 */
- (void)enumerateLinesUsingBlock: (of_string_line_enumeration_block_t)block;
# endif
@end
#endif

#ifdef __cplusplus
extern "C" {
#endif











extern of_string_encoding_t of_string_parse_encoding(OFString *);






extern OFString *_Nullable of_string_name_of_encoding(of_string_encoding_t);
extern size_t of_string_utf8_encode(of_unichar_t, char *);


extern ssize_t of_string_utf8_decode(const char *, size_t, of_unichar_t *);
extern size_t of_string_utf16_length(const of_char16_t *);
extern size_t of_string_utf32_length(const of_char32_t *);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#include "OFConstantString.h"
#include "OFMutableString.h"
#ifdef __OBJC__
# import "OFString+CryptoHashing.h"
# import "OFString+JSONParsing.h"
# ifdef OF_HAVE_FILES
#  import "OFString+PathAdditions.h"
# endif
# import "OFString+PropertyListParsing.h"
# import "OFString+Serialization.h"
# import "OFString+URLEncoding.h"







|
<
<
<
<




|
















|
<
<
<
<




|











|












|








|
















|
<















|
<







|







>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
|
|
>
>
|
|
|









|







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
1182
1183
1184
1185
1186




1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
    componentsSeparatedByString: (OFString *)delimiter;

/**
 * @brief Separates the string into an array of strings, split by the specified
 *	  delimiter.
 *
 * @param delimiter The delimiter for separating
 * @param options Options according to which the string should be separated




 * @return An autoreleased OFArray with the separated string
 */
- (OFArray OF_GENERIC(OFString *) *)
    componentsSeparatedByString: (OFString *)delimiter
			options: (OFStringSeparationOptions)options;

/**
 * @brief Separates the string into an array of strings, split by characters in
 *	  the specified set.
 *
 * @param characterSet The character set for separating
 * @return An autoreleased OFArray with the separated string
 */
- (OFArray OF_GENERIC(OFString *) *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet;

/**
 * @brief Separates the string into an array of strings, split by characters in
 *	  the specified set.
 *
 * @param characterSet The character set for separating
 * @param options Options according to which the string should be separated




 * @return An autoreleased OFArray with the separated string
 */
- (OFArray OF_GENERIC(OFString *) *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
				 options: (OFStringSeparationOptions)options;

/**
 * @brief Returns the string in UTF-16 encoding with the specified byte order.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 *
 * @param byteOrder The byte order for the UTF-16 encoding
 * @return The string in UTF-16 encoding with the specified byte order
 */
- (const OFChar16 *)UTF16StringWithByteOrder: (OFByteOrder)byteOrder
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the string in UTF-32 encoding with the specified byte order.
 *
 * The result is valid until the autorelease pool is released. If you want to
 * use the result outside the scope of the current autorelease pool, you have to
 * copy it.
 *
 * @param byteOrder The byte order for the UTF-32 encoding
 * @return The string in UTF-32 encoding with the specified byte order
 */
- (const OFChar32 *)UTF32StringWithByteOrder: (OFByteOrder)byteOrder
    OF_RETURNS_INNER_POINTER;

/**
 * @brief Returns the string as OFData with the specified encoding.
 *
 * @param encoding The encoding to use for the returned OFData
 * @return The string as OFData with the specified encoding
 */
- (OFData *)dataWithEncoding: (OFStringEncoding)encoding;

# ifdef OF_HAVE_FILES
/**
 * @brief Writes the string into the specified file using UTF-8 encoding.
 *
 * @param path The path of the file to write to
 */
- (void)writeToFile: (OFString *)path;

/**
 * @brief Writes the string into the specified file using the specified
 *	  encoding.
 *
 * @param path The path of the file to write to
 * @param encoding The encoding to use to write the string into the file
 */
- (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding;

# endif

/**
 * @brief Writes the string to the specified URL using UTF-8 encoding.
 *
 * @param URL The URL to write to
 */
- (void)writeToURL: (OFURL *)URL;

/**
 * @brief Writes the string to the specified URL using the specified encoding.
 *
 * @param URL The URL to write to
 * @param encoding The encoding to use to write the string to the URL
 */
- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding;


# ifdef OF_HAVE_BLOCKS
/**
 * Enumerates all lines in the receiver using the specified block.
 *
 * @brief block The block to call for each line
 */
- (void)enumerateLinesUsingBlock: (OFStringLineEnumerationBlock)block;
# endif
@end
#endif

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Parses the specified string encoding name and returns the
 *	  OFStringEncoding for it.
 *
 * Throws @ref OFInvalidArgumentException if the specified name is not a valid
 * encoding name.
 *
 * @param name The name to parse as a string encoding
 * @return The OFStringEncoding for the specified name
 */
extern OFStringEncoding OFStringEncodingParseName(OFString *name);

/**
 * @brief Returns the name of the specified OFStringEncoding.
 *
 * @param encoding The encoding for which to return the name
 * @return The name of the specified OFStringEncoding
 */
extern OFString *_Nullable OFStringEncodingName(OFStringEncoding encoding);

extern char *_Nullable OFStrDup(const char *_Nonnull);
extern size_t OFUTF8StringEncode(OFUnichar, char *);
extern ssize_t OFUTF8StringDecode(const char *, size_t, OFUnichar *);
extern size_t OFUTF16StringLength(const OFChar16 *);
extern size_t OFUTF32StringLength(const OFChar32 *);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#include "OFConstantString.h"
#include "OFMutableString.h"
#ifdef __OBJC__
# import "OFString+CryptographicHashing.h"
# import "OFString+JSONParsing.h"
# ifdef OF_HAVE_FILES
#  import "OFString+PathAdditions.h"
# endif
# import "OFString+PropertyListParsing.h"
# import "OFString+Serialization.h"
# import "OFString+URLEncoding.h"

Modified src/OFString.m from [b2cfcd4f85] to [05a5b5b79b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
28
29
30
31
32
33
34

35
36
37
38
39
40
41
# include <locale.h>
#endif
#ifdef HAVE_XLOCALE_H
# include <xlocale.h>
#endif

#import "OFString.h"

#import "OFArray.h"
#import "OFCharacterSet.h"
#import "OFData.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
# import "OFFileManager.h"







>







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# include <locale.h>
#endif
#ifdef HAVE_XLOCALE_H
# include <xlocale.h>
#endif

#import "OFString.h"
#import "OFASPrintF.h"
#import "OFArray.h"
#import "OFCharacterSet.h"
#import "OFData.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
# import "OFFileManager.h"
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
#import "OFOpenItemFailedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFRetrieveItemAttributesFailedException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"

#import "of_asprintf.h"
#import "unicode.h"

/*
 * It seems strtod is buggy on Win32.
 * However, the MinGW version __strtod seems to be ok.
 */
#ifdef __MINGW32__
# define strtod __strtod
#endif

#ifdef OF_AMIGAOS_M68K
/* libnix has strtod, but not strtof */
# define strtof strtod
#endif





static struct {
	Class isa;
} placeholder;

#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L)
static locale_t cLocale;
#endif

@interface OFString ()
- (size_t)of_getCString: (char *)cString
	      maxLength: (size_t)maxLength
	       encoding: (of_string_encoding_t)encoding
		  lossy: (bool)lossy OF_DIRECT;
- (const char *)of_cStringWithEncoding: (of_string_encoding_t)encoding
				 lossy: (bool)lossy OF_DIRECT;
- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth;
@end

@interface OFStringPlaceholder: OFString
@end

extern bool of_unicode_to_iso_8859_2(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_iso_8859_3(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_iso_8859_15(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_windows_1251(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_windows_1252(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_codepage_437(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_codepage_850(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_codepage_858(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_mac_roman(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_koi8_r(const of_unichar_t *, unsigned char *,
    size_t, bool);
extern bool of_unicode_to_koi8_u(const of_unichar_t *, unsigned char *,
    size_t, bool);

/* References for static linking */
void
_references_to_categories_of_OFString(void)
{
	_OFString_CryptoHashing_reference = 1;
	_OFString_JSONParsing_reference = 1;
#ifdef OF_HAVE_FILES
	_OFString_PathAdditions_reference = 1;
#endif
	_OFString_PropertyListParsing_reference = 1;
	_OFString_Serialization_reference = 1;
	_OFString_URLEncoding_reference = 1;
	_OFString_XMLEscaping_reference = 1;
	_OFString_XMLUnescaping_reference = 1;
}

void
_reference_to_OFConstantString(void)
{
	[OFConstantString class];
}

of_string_encoding_t
of_string_parse_encoding(OFString *string)
{
	void *pool = objc_autoreleasePoolPush();
	of_string_encoding_t encoding;

	string = string.lowercaseString;

	if ([string isEqual: @"utf8"] || [string isEqual: @"utf-8"])
		encoding = OF_STRING_ENCODING_UTF_8;
	else if ([string isEqual: @"ascii"] || [string isEqual: @"us-ascii"])
		encoding = OF_STRING_ENCODING_ASCII;
	else if ([string isEqual: @"iso-8859-1"] ||
	    [string isEqual: @"iso_8859-1"])
		encoding = OF_STRING_ENCODING_ISO_8859_1;
	else if ([string isEqual: @"iso-8859-2"] ||
	    [string isEqual: @"iso_8859-2"])
		encoding = OF_STRING_ENCODING_ISO_8859_2;
	else if ([string isEqual: @"iso-8859-3"] ||
	    [string isEqual: @"iso_8859-3"])
		encoding = OF_STRING_ENCODING_ISO_8859_3;
	else if ([string isEqual: @"iso-8859-15"] ||
	    [string isEqual: @"iso_8859-15"])
		encoding = OF_STRING_ENCODING_ISO_8859_15;
	else if ([string isEqual: @"windows-1251"] ||
	    [string isEqual: @"cp1251"] || [string isEqual: @"cp-1251"] ||
	    [string isEqual: @"1251"])
		encoding = OF_STRING_ENCODING_WINDOWS_1251;
	else if ([string isEqual: @"windows-1252"] ||
	    [string isEqual: @"cp1252"] || [string isEqual: @"cp-1252"] ||
	    [string isEqual: @"1252"])
		encoding = OF_STRING_ENCODING_WINDOWS_1252;
	else if ([string isEqual: @"cp437"] || [string isEqual: @"cp-437"] ||
	    [string isEqual: @"ibm437"] || [string isEqual: @"437"])
		encoding = OF_STRING_ENCODING_CODEPAGE_437;
	else if ([string isEqual: @"cp850"] || [string isEqual: @"cp-850"] ||
	    [string isEqual: @"ibm850"] || [string isEqual: @"850"])
		encoding = OF_STRING_ENCODING_CODEPAGE_850;
	else if ([string isEqual: @"cp858"] || [string isEqual: @"cp-858"] ||
	    [string isEqual: @"ibm858"] || [string isEqual: @"858"])
		encoding = OF_STRING_ENCODING_CODEPAGE_858;
	else if ([string isEqual: @"macintosh"] || [string isEqual: @"mac"])
		encoding = OF_STRING_ENCODING_MAC_ROMAN;
	else if ([string isEqual: @"koi8-r"])
		encoding = OF_STRING_ENCODING_KOI8_R;
	else if ([string isEqual: @"koi8-u"])
		encoding = OF_STRING_ENCODING_KOI8_U;
	else
		@throw [OFInvalidArgumentException exception];

	objc_autoreleasePoolPop(pool);

	return encoding;
}

OFString *
of_string_name_of_encoding(of_string_encoding_t encoding)
{
	switch (encoding) {
	case OF_STRING_ENCODING_UTF_8:
		return @"UTF-8";
	case OF_STRING_ENCODING_ASCII:
		return @"ASCII";
	case OF_STRING_ENCODING_ISO_8859_1:
		return @"ISO 8859-1";
	case OF_STRING_ENCODING_ISO_8859_2:
		return @"ISO 8859-2";
	case OF_STRING_ENCODING_ISO_8859_3:
		return @"ISO 8859-3";
	case OF_STRING_ENCODING_ISO_8859_15:
		return @"ISO 8859-15";
	case OF_STRING_ENCODING_WINDOWS_1251:
		return @"Windows-1251";
	case OF_STRING_ENCODING_WINDOWS_1252:
		return @"Windows-1252";
	case OF_STRING_ENCODING_CODEPAGE_437:
		return @"Codepage 437";
	case OF_STRING_ENCODING_CODEPAGE_850:
		return @"Codepage 850";
	case OF_STRING_ENCODING_CODEPAGE_858:
		return @"Codepage 858";
	case OF_STRING_ENCODING_MAC_ROMAN:
		return @"Mac Roman";
	case OF_STRING_ENCODING_KOI8_R:
		return @"KOI8-R";
	case OF_STRING_ENCODING_KOI8_U:
		return @"KOI8-U";
	case OF_STRING_ENCODING_AUTODETECT:
		return @"autodetect";
	}

	return nil;
}

size_t
of_string_utf8_encode(of_unichar_t character, char *buffer)
{
	if (character < 0x80) {
		buffer[0] = character;
		return 1;
	} else if (character < 0x800) {
		buffer[0] = 0xC0 | (character >> 6);
		buffer[1] = 0x80 | (character & 0x3F);







<










|
<


>
>
>
>












|

|

|
>
|





|

|

|

|

|

|

|

|

|

|

|






|

















|
|


|




|

|


|


|


|


|



|



|


|


|


|

|

|

|









|


|

|

|

|

|

|

|

|

|

|

|

|

|

|

|







|







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
#import "OFOpenItemFailedException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFRetrieveItemAttributesFailedException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"


#import "unicode.h"

/*
 * It seems strtod is buggy on Win32.
 * However, the MinGW version __strtod seems to be ok.
 */
#ifdef __MINGW32__
# define strtod __strtod
#endif

#ifndef HAVE_STRTOF

# define strtof strtod
#endif

#ifndef INFINITY
# define INFINITY __builtin_inf()
#endif

static struct {
	Class isa;
} placeholder;

#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L)
static locale_t cLocale;
#endif

@interface OFString ()
- (size_t)of_getCString: (char *)cString
	      maxLength: (size_t)maxLength
	       encoding: (OFStringEncoding)encoding
		  lossy: (bool)lossy OF_DIRECT;
- (const char *)of_cStringWithEncoding: (OFStringEncoding)encoding
				 lossy: (bool)lossy OF_DIRECT;
- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth;
@end

@interface OFStringPlaceholder: OFString
@end

extern bool OFUnicodeToISO8859_2(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToISO8859_3(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToISO8859_15(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToWindows1251(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToWindows1252(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToCodepage437(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToCodepage850(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToCodepage858(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToMacRoman(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToKOI8R(const OFUnichar *, unsigned char *,
    size_t, bool);
extern bool OFUnicodeToKOI8U(const OFUnichar *, unsigned char *,
    size_t, bool);

/* References for static linking */
void
_references_to_categories_of_OFString(void)
{
	_OFString_CryptographicHashing_reference = 1;
	_OFString_JSONParsing_reference = 1;
#ifdef OF_HAVE_FILES
	_OFString_PathAdditions_reference = 1;
#endif
	_OFString_PropertyListParsing_reference = 1;
	_OFString_Serialization_reference = 1;
	_OFString_URLEncoding_reference = 1;
	_OFString_XMLEscaping_reference = 1;
	_OFString_XMLUnescaping_reference = 1;
}

void
_reference_to_OFConstantString(void)
{
	[OFConstantString class];
}

OFStringEncoding
OFStringEncodingParseName(OFString *string)
{
	void *pool = objc_autoreleasePoolPush();
	OFStringEncoding encoding;

	string = string.lowercaseString;

	if ([string isEqual: @"utf8"] || [string isEqual: @"utf-8"])
		encoding = OFStringEncodingUTF8;
	else if ([string isEqual: @"ascii"] || [string isEqual: @"us-ascii"])
		encoding = OFStringEncodingASCII;
	else if ([string isEqual: @"iso-8859-1"] ||
	    [string isEqual: @"iso_8859-1"])
		encoding = OFStringEncodingISO8859_1;
	else if ([string isEqual: @"iso-8859-2"] ||
	    [string isEqual: @"iso_8859-2"])
		encoding = OFStringEncodingISO8859_2;
	else if ([string isEqual: @"iso-8859-3"] ||
	    [string isEqual: @"iso_8859-3"])
		encoding = OFStringEncodingISO8859_3;
	else if ([string isEqual: @"iso-8859-15"] ||
	    [string isEqual: @"iso_8859-15"])
		encoding = OFStringEncodingISO8859_15;
	else if ([string isEqual: @"windows-1251"] ||
	    [string isEqual: @"cp1251"] || [string isEqual: @"cp-1251"] ||
	    [string isEqual: @"1251"])
		encoding = OFStringEncodingWindows1251;
	else if ([string isEqual: @"windows-1252"] ||
	    [string isEqual: @"cp1252"] || [string isEqual: @"cp-1252"] ||
	    [string isEqual: @"1252"])
		encoding = OFStringEncodingWindows1252;
	else if ([string isEqual: @"cp437"] || [string isEqual: @"cp-437"] ||
	    [string isEqual: @"ibm437"] || [string isEqual: @"437"])
		encoding = OFStringEncodingCodepage437;
	else if ([string isEqual: @"cp850"] || [string isEqual: @"cp-850"] ||
	    [string isEqual: @"ibm850"] || [string isEqual: @"850"])
		encoding = OFStringEncodingCodepage850;
	else if ([string isEqual: @"cp858"] || [string isEqual: @"cp-858"] ||
	    [string isEqual: @"ibm858"] || [string isEqual: @"858"])
		encoding = OFStringEncodingCodepage858;
	else if ([string isEqual: @"macintosh"] || [string isEqual: @"mac"])
		encoding = OFStringEncodingMacRoman;
	else if ([string isEqual: @"koi8-r"])
		encoding = OFStringEncodingKOI8R;
	else if ([string isEqual: @"koi8-u"])
		encoding = OFStringEncodingKOI8U;
	else
		@throw [OFInvalidArgumentException exception];

	objc_autoreleasePoolPop(pool);

	return encoding;
}

OFString *
OFStringEncodingName(OFStringEncoding encoding)
{
	switch (encoding) {
	case OFStringEncodingUTF8:
		return @"UTF-8";
	case OFStringEncodingASCII:
		return @"ASCII";
	case OFStringEncodingISO8859_1:
		return @"ISO 8859-1";
	case OFStringEncodingISO8859_2:
		return @"ISO 8859-2";
	case OFStringEncodingISO8859_3:
		return @"ISO 8859-3";
	case OFStringEncodingISO8859_15:
		return @"ISO 8859-15";
	case OFStringEncodingWindows1251:
		return @"Windows-1251";
	case OFStringEncodingWindows1252:
		return @"Windows-1252";
	case OFStringEncodingCodepage437:
		return @"Codepage 437";
	case OFStringEncodingCodepage850:
		return @"Codepage 850";
	case OFStringEncodingCodepage858:
		return @"Codepage 858";
	case OFStringEncodingMacRoman:
		return @"Mac Roman";
	case OFStringEncodingKOI8R:
		return @"KOI8-R";
	case OFStringEncodingKOI8U:
		return @"KOI8-U";
	case OFStringEncodingAutodetect:
		return @"autodetect";
	}

	return nil;
}

size_t
OFUTF8StringEncode(OFUnichar character, char *buffer)
{
	if (character < 0x80) {
		buffer[0] = character;
		return 1;
	} else if (character < 0x800) {
		buffer[0] = 0xC0 | (character >> 6);
		buffer[1] = 0x80 | (character & 0x3F);
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
		return 4;
	}

	return 0;
}

ssize_t
of_string_utf8_decode(const char *buffer_, size_t length, of_unichar_t *ret)
{
	const unsigned char *buffer = (const unsigned char *)buffer_;

	if (!(*buffer & 0x80)) {
		*ret = buffer[0];
		return 1;
	}







|







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
		return 4;
	}

	return 0;
}

ssize_t
OFUTF8StringDecode(const char *buffer_, size_t length, OFUnichar *ret)
{
	const unsigned char *buffer = (const unsigned char *)buffer_;

	if (!(*buffer & 0x80)) {
		*ret = buffer[0];
		return 1;
	}
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
		return 4;
	}

	return 0;
}

size_t
of_string_utf16_length(const of_char16_t *string)
{
	size_t length = 0;

	while (*string++ != 0)
		length++;

	return length;
}

size_t
of_string_utf32_length(const of_char32_t *string)
{
	size_t length = 0;

	while (*string++ != 0)
		length++;

	return length;
}











#ifdef OF_HAVE_UNICODE_TABLES
static OFString *
decomposedString(OFString *self, const char *const *const *table, size_t size)
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters = self.characters;
	size_t length = self.length;

	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = characters[i];
		const char *const *page;

		if (c >= size) {
			[ret appendCharacters: &c
				       length: 1];
			continue;
		}

		page = table[c >> 8];
		if (page != NULL && page[c & 0xFF] != NULL)
			[ret appendUTF8String: page[c & 0xFF]];
		else
			[ret appendCharacters: &c
				       length: 1];
	}

	objc_autoreleasePoolPop(pool);

	return ret;
}
#endif







|










|








>
>
>
>
>
>
>
>
>
>







|



|



|
<







|
<







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
		return 4;
	}

	return 0;
}

size_t
OFUTF16StringLength(const OFChar16 *string)
{
	size_t length = 0;

	while (*string++ != 0)
		length++;

	return length;
}

size_t
OFUTF32StringLength(const OFChar32 *string)
{
	size_t length = 0;

	while (*string++ != 0)
		length++;

	return length;
}

char *
OFStrDup(const char *string)
{
	size_t length = strlen(string);
	char *copy = (char *)OFAllocMemory(1, length + 1);
	memcpy(copy, string, length + 1);

	return copy;
}

#ifdef OF_HAVE_UNICODE_TABLES
static OFString *
decomposedString(OFString *self, const char *const *const *table, size_t size)
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters = self.characters;
	size_t length = self.length;

	for (size_t i = 0; i < length; i++) {
		OFUnichar c = characters[i];
		const char *const *page;

		if (c >= size) {
			[ret appendCharacters: &c length: 1];

			continue;
		}

		page = table[c >> 8];
		if (page != NULL && page[c & 0xFF] != NULL)
			[ret appendUTF8String: page[c & 0xFF]];
		else
			[ret appendCharacters: &c length: 1];

	}

	objc_autoreleasePoolPop(pool);

	return ret;
}
#endif
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
- (instancetype)initWithUTF8String: (const char *)UTF8String
{
	OFUTF8String *string;
	size_t length;
	void *storage;

	length = strlen(UTF8String);
	string = of_alloc_object([OFUTF8String class], length + 1, 1, &storage);

	return (id)[string of_initWithUTF8String: UTF8String
					  length: length
					 storage: storage];
}

- (instancetype)initWithUTF8String: (const char *)UTF8String
			    length: (size_t)UTF8StringLength
{
	OFUTF8String *string;
	void *storage;

	string = of_alloc_object([OFUTF8String class], UTF8StringLength + 1, 1,
	    &storage);

	return (id)[string of_initWithUTF8String: UTF8String
					  length: UTF8StringLength
					 storage: storage];
}








|












|







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
- (instancetype)initWithUTF8String: (const char *)UTF8String
{
	OFUTF8String *string;
	size_t length;
	void *storage;

	length = strlen(UTF8String);
	string = OFAllocObject([OFUTF8String class], length + 1, 1, &storage);

	return (id)[string of_initWithUTF8String: UTF8String
					  length: length
					 storage: storage];
}

- (instancetype)initWithUTF8String: (const char *)UTF8String
			    length: (size_t)UTF8StringLength
{
	OFUTF8String *string;
	void *storage;

	string = OFAllocObject([OFUTF8String class], UTF8StringLength + 1, 1,
	    &storage);

	return (id)[string of_initWithUTF8String: UTF8String
					  length: UTF8StringLength
					 storage: storage];
}

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
	return (id)[[OFUTF8String alloc]
	    initWithUTF8StringNoCopy: UTF8String
			      length: UTF8StringLength
			freeWhenDone: freeWhenDone];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
{
	if (encoding == OF_STRING_ENCODING_UTF_8) {
		OFUTF8String *string;
		size_t length;
		void *storage;

		length = strlen(cString);
		string = of_alloc_object([OFUTF8String class], length + 1, 1,
		    &storage);

		return (id)[string of_initWithUTF8String: cString
						  length: length
						 storage: storage];
	}

	return (id)[[OFUTF8String alloc] initWithCString: cString
						encoding: encoding];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
			 length: (size_t)cStringLength
{
	if (encoding == OF_STRING_ENCODING_UTF_8) {
		OFUTF8String *string;
		void *storage;

		string = of_alloc_object([OFUTF8String class],
		    cStringLength + 1, 1, &storage);

		return (id)[string of_initWithUTF8String: cString
						  length: cStringLength
						 storage: storage];
	}

	return (id)[[OFUTF8String alloc] initWithCString: cString
						encoding: encoding
						  length: cStringLength];
}

- (instancetype)initWithData: (OFData *)data
		    encoding: (of_string_encoding_t)encoding
{
	return (id)[[OFUTF8String alloc] initWithData: data
					     encoding: encoding];
}

- (instancetype)initWithString: (OFString *)string
{
	return (id)[[OFUTF8String alloc] initWithString: string];
}

- (instancetype)initWithCharacters: (const of_unichar_t *)string
			    length: (size_t)length
{
	return (id)[[OFUTF8String alloc] initWithCharacters: string
						     length: length];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
{
	return (id)[[OFUTF8String alloc] initWithUTF16String: string];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length
{
	return (id)[[OFUTF8String alloc] initWithUTF16String: string
						      length: length];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			  byteOrder: (of_byte_order_t)byteOrder
{
	return (id)[[OFUTF8String alloc] initWithUTF16String: string
						   byteOrder: byteOrder];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	return (id)[[OFUTF8String alloc] initWithUTF16String: string
						      length: length
						   byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
{
	return (id)[[OFUTF8String alloc] initWithUTF32String: string];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			     length: (size_t)length
{
	return (id)[[OFUTF8String alloc] initWithUTF32String: string
						      length: length];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			  byteOrder: (of_byte_order_t)byteOrder
{
	return (id)[[OFUTF8String alloc] initWithUTF32String: string
						   byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	return (id)[[OFUTF8String alloc] initWithUTF32String: string
						      length: length
						   byteOrder: byteOrder];
}

- (instancetype)initWithFormat: (OFConstantString *)format, ...







|

|





|












|


|



|
|












|










|






|




|






|
|





|

|






|




|






|
|





|

|







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
	return (id)[[OFUTF8String alloc]
	    initWithUTF8StringNoCopy: UTF8String
			      length: UTF8StringLength
			freeWhenDone: freeWhenDone];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
{
	if (encoding == OFStringEncodingUTF8) {
		OFUTF8String *string;
		size_t length;
		void *storage;

		length = strlen(cString);
		string = OFAllocObject([OFUTF8String class], length + 1, 1,
		    &storage);

		return (id)[string of_initWithUTF8String: cString
						  length: length
						 storage: storage];
	}

	return (id)[[OFUTF8String alloc] initWithCString: cString
						encoding: encoding];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
			 length: (size_t)cStringLength
{
	if (encoding == OFStringEncodingUTF8) {
		OFUTF8String *string;
		void *storage;

		string = OFAllocObject([OFUTF8String class], cStringLength + 1,
		    1, &storage);

		return (id)[string of_initWithUTF8String: cString
						  length: cStringLength
						 storage: storage];
	}

	return (id)[[OFUTF8String alloc] initWithCString: cString
						encoding: encoding
						  length: cStringLength];
}

- (instancetype)initWithData: (OFData *)data
		    encoding: (OFStringEncoding)encoding
{
	return (id)[[OFUTF8String alloc] initWithData: data
					     encoding: encoding];
}

- (instancetype)initWithString: (OFString *)string
{
	return (id)[[OFUTF8String alloc] initWithString: string];
}

- (instancetype)initWithCharacters: (const OFUnichar *)string
			    length: (size_t)length
{
	return (id)[[OFUTF8String alloc] initWithCharacters: string
						     length: length];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
{
	return (id)[[OFUTF8String alloc] initWithUTF16String: string];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length
{
	return (id)[[OFUTF8String alloc] initWithUTF16String: string
						      length: length];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			  byteOrder: (OFByteOrder)byteOrder
{
	return (id)[[OFUTF8String alloc] initWithUTF16String: string
						   byteOrder: byteOrder];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	return (id)[[OFUTF8String alloc] initWithUTF16String: string
						      length: length
						   byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
{
	return (id)[[OFUTF8String alloc] initWithUTF32String: string];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			     length: (size_t)length
{
	return (id)[[OFUTF8String alloc] initWithUTF32String: string
						      length: length];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			  byteOrder: (OFByteOrder)byteOrder
{
	return (id)[[OFUTF8String alloc] initWithUTF32String: string
						   byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	return (id)[[OFUTF8String alloc] initWithUTF32String: string
						      length: length
						   byteOrder: byteOrder];
}

- (instancetype)initWithFormat: (OFConstantString *)format, ...
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
#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
	return (id)[[OFUTF8String alloc] initWithContentsOfFile: path];
}

- (instancetype)initWithContentsOfFile: (OFString *)path
			      encoding: (of_string_encoding_t)encoding
{
	return (id)[[OFUTF8String alloc] initWithContentsOfFile: path
						       encoding: encoding];
}
#endif

#if defined(OF_HAVE_FILES) || defined(OF_HAVE_SOCKETS)
- (instancetype)initWithContentsOfURL: (OFURL *)URL
{
	return (id)[[OFUTF8String alloc] initWithContentsOfURL: URL];
}

- (instancetype)initWithContentsOfURL: (OFURL *)URL
			     encoding: (of_string_encoding_t)encoding
{
	return (id)[[OFUTF8String alloc] initWithContentsOfURL: URL
						      encoding: encoding];
}
#endif

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	return (id)[[OFUTF8String alloc] initWithSerialization: element];
}

- (instancetype)retain







|






<






|




<







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
#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
	return (id)[[OFUTF8String alloc] initWithContentsOfFile: path];
}

- (instancetype)initWithContentsOfFile: (OFString *)path
			      encoding: (OFStringEncoding)encoding
{
	return (id)[[OFUTF8String alloc] initWithContentsOfFile: path
						       encoding: encoding];
}
#endif


- (instancetype)initWithContentsOfURL: (OFURL *)URL
{
	return (id)[[OFUTF8String alloc] initWithContentsOfURL: URL];
}

- (instancetype)initWithContentsOfURL: (OFURL *)URL
			     encoding: (OFStringEncoding)encoding
{
	return (id)[[OFUTF8String alloc] initWithContentsOfURL: URL
						      encoding: encoding];
}


- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	return (id)[[OFUTF8String alloc] initWithSerialization: element];
}

- (instancetype)retain
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
761
762
763
764
	return [[[self alloc]
	    initWithUTF8StringNoCopy: UTF8String
			      length: UTF8StringLength
			freeWhenDone: freeWhenDone] autorelease];
}

+ (instancetype)stringWithCString: (const char *)cString
			 encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithCString: cString
				     encoding: encoding] autorelease];
}

+ (instancetype)stringWithCString: (const char *)cString
			 encoding: (of_string_encoding_t)encoding
			   length: (size_t)cStringLength
{
	return [[[self alloc] initWithCString: cString
				     encoding: encoding
				       length: cStringLength] autorelease];
}

+ (instancetype)stringWithData: (OFData *)data
		      encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithData: data
				  encoding: encoding] autorelease];
}

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

+ (instancetype)stringWithCharacters: (const of_unichar_t *)string
			      length: (size_t)length
{
	return [[[self alloc] initWithCharacters: string
					  length: length] autorelease];
}

+ (instancetype)stringWithUTF16String: (const of_char16_t *)string
{
	return [[[self alloc] initWithUTF16String: string] autorelease];
}

+ (instancetype)stringWithUTF16String: (const of_char16_t *)string
			       length: (size_t)length
{
	return [[[self alloc] initWithUTF16String: string
					   length: length] autorelease];
}

+ (instancetype)stringWithUTF16String: (const of_char16_t *)string
			    byteOrder: (of_byte_order_t)byteOrder
{
	return [[[self alloc] initWithUTF16String: string
					byteOrder: byteOrder] autorelease];
}

+ (instancetype)stringWithUTF16String: (const of_char16_t *)string
			       length: (size_t)length
			    byteOrder: (of_byte_order_t)byteOrder
{
	return [[[self alloc] initWithUTF16String: string
					   length: length
					byteOrder: byteOrder] autorelease];
}

+ (instancetype)stringWithUTF32String: (const of_char32_t *)string
{
	return [[[self alloc] initWithUTF32String: string] autorelease];
}

+ (instancetype)stringWithUTF32String: (const of_char32_t *)string
			       length: (size_t)length
{
	return [[[self alloc] initWithUTF32String: string
					   length: length] autorelease];
}

+ (instancetype)stringWithUTF32String: (const of_char32_t *)string
			    byteOrder: (of_byte_order_t)byteOrder
{
	return [[[self alloc] initWithUTF32String: string
					byteOrder: byteOrder] autorelease];
}

+ (instancetype)stringWithUTF32String: (const of_char32_t *)string
			       length: (size_t)length
			    byteOrder: (of_byte_order_t)byteOrder
{
	return [[[self alloc] initWithUTF32String: string
					   length: length
					byteOrder: byteOrder] autorelease];
}

+ (instancetype)stringWithFormat: (OFConstantString *)format, ...







|






|








|










|






|




|






|
|





|

|






|




|






|
|





|

|







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
761
762
763
764
765
766
767
768
769
770
771
772
	return [[[self alloc]
	    initWithUTF8StringNoCopy: UTF8String
			      length: UTF8StringLength
			freeWhenDone: freeWhenDone] autorelease];
}

+ (instancetype)stringWithCString: (const char *)cString
			 encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithCString: cString
				     encoding: encoding] autorelease];
}

+ (instancetype)stringWithCString: (const char *)cString
			 encoding: (OFStringEncoding)encoding
			   length: (size_t)cStringLength
{
	return [[[self alloc] initWithCString: cString
				     encoding: encoding
				       length: cStringLength] autorelease];
}

+ (instancetype)stringWithData: (OFData *)data
		      encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithData: data
				  encoding: encoding] autorelease];
}

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

+ (instancetype)stringWithCharacters: (const OFUnichar *)string
			      length: (size_t)length
{
	return [[[self alloc] initWithCharacters: string
					  length: length] autorelease];
}

+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
{
	return [[[self alloc] initWithUTF16String: string] autorelease];
}

+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
			       length: (size_t)length
{
	return [[[self alloc] initWithUTF16String: string
					   length: length] autorelease];
}

+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
			    byteOrder: (OFByteOrder)byteOrder
{
	return [[[self alloc] initWithUTF16String: string
					byteOrder: byteOrder] autorelease];
}

+ (instancetype)stringWithUTF16String: (const OFChar16 *)string
			       length: (size_t)length
			    byteOrder: (OFByteOrder)byteOrder
{
	return [[[self alloc] initWithUTF16String: string
					   length: length
					byteOrder: byteOrder] autorelease];
}

+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
{
	return [[[self alloc] initWithUTF32String: string] autorelease];
}

+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
			       length: (size_t)length
{
	return [[[self alloc] initWithUTF32String: string
					   length: length] autorelease];
}

+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
			    byteOrder: (OFByteOrder)byteOrder
{
	return [[[self alloc] initWithUTF32String: string
					byteOrder: byteOrder] autorelease];
}

+ (instancetype)stringWithUTF32String: (const OFChar32 *)string
			       length: (size_t)length
			    byteOrder: (OFByteOrder)byteOrder
{
	return [[[self alloc] initWithUTF32String: string
					   length: length
					byteOrder: byteOrder] autorelease];
}

+ (instancetype)stringWithFormat: (OFConstantString *)format, ...
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
#ifdef OF_HAVE_FILES
+ (instancetype)stringWithContentsOfFile: (OFString *)path
{
	return [[[self alloc] initWithContentsOfFile: path] autorelease];
}

+ (instancetype)stringWithContentsOfFile: (OFString *)path
				encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithContentsOfFile: path
					    encoding: encoding] autorelease];
}
#endif

#if defined(OF_HAVE_FILES) || defined(OF_HAVE_SOCKETS)
+ (instancetype)stringWithContentsOfURL: (OFURL *)URL
{
	return [[[self alloc] initWithContentsOfURL: URL] autorelease];
}

+ (instancetype)stringWithContentsOfURL: (OFURL *)URL
			       encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithContentsOfURL: URL
					   encoding: encoding] autorelease];
}
#endif

- (instancetype)init
{
	if ([self isMemberOfClass: [OFString class]]) {
		@try {
			[self doesNotRecognizeSelector: _cmd];
		} @catch (id e) {
			[self release];
			@throw e;
		}

		abort();
	}

	return [super init];
}

- (instancetype)initWithUTF8String: (const char *)UTF8String
{
	return [self initWithCString: UTF8String
			    encoding: OF_STRING_ENCODING_UTF_8
			      length: strlen(UTF8String)];
}

- (instancetype)initWithUTF8String: (const char *)UTF8String
			    length: (size_t)UTF8StringLength
{
	return [self initWithCString: UTF8String
			    encoding: OF_STRING_ENCODING_UTF_8
			      length: UTF8StringLength];
}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
			    freeWhenDone: (bool)freeWhenDone
{
	id ret;

	@try {
		ret = [self initWithUTF8String: UTF8String];
	} @finally {
		if (freeWhenDone)
			of_free(UTF8String);
	}

	return ret;
}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
				  length: (size_t)UTF8StringLength
			    freeWhenDone: (bool)freeWhenDone
{
	id ret;

	@try {
		ret = [self initWithUTF8String: UTF8String
					length: UTF8StringLength];
	} @finally {
		if (freeWhenDone)
			of_free(UTF8String);
	}

	return ret;
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
{
	return [self initWithCString: cString
			    encoding: encoding
			      length: strlen(cString)];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
			 length: (size_t)cStringLength
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithData: (OFData *)data
		    encoding: (of_string_encoding_t)encoding
{
	@try {
		if (data.itemSize != 1)
			@throw [OFInvalidArgumentException exception];
	} @catch (id e) {
		[self release];
		@throw e;







|






<






|




<




















|







|






|

<
<
<
|
|
<








|

<
<
<
<
|
|
<





|







|






|







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
#ifdef OF_HAVE_FILES
+ (instancetype)stringWithContentsOfFile: (OFString *)path
{
	return [[[self alloc] initWithContentsOfFile: path] autorelease];
}

+ (instancetype)stringWithContentsOfFile: (OFString *)path
				encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithContentsOfFile: path
					    encoding: encoding] autorelease];
}
#endif


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

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


- (instancetype)init
{
	if ([self isMemberOfClass: [OFString class]]) {
		@try {
			[self doesNotRecognizeSelector: _cmd];
		} @catch (id e) {
			[self release];
			@throw e;
		}

		abort();
	}

	return [super init];
}

- (instancetype)initWithUTF8String: (const char *)UTF8String
{
	return [self initWithCString: UTF8String
			    encoding: OFStringEncodingUTF8
			      length: strlen(UTF8String)];
}

- (instancetype)initWithUTF8String: (const char *)UTF8String
			    length: (size_t)UTF8StringLength
{
	return [self initWithCString: UTF8String
			    encoding: OFStringEncodingUTF8
			      length: UTF8StringLength];
}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
			    freeWhenDone: (bool)freeWhenDone
{
	id ret = [self initWithUTF8String: UTF8String];




	if (freeWhenDone)
		OFFreeMemory(UTF8String);


	return ret;
}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
				  length: (size_t)UTF8StringLength
			    freeWhenDone: (bool)freeWhenDone
{
	id ret = [self initWithUTF8String: UTF8String length: UTF8StringLength];





	if (freeWhenDone)
		OFFreeMemory(UTF8String);


	return ret;
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
{
	return [self initWithCString: cString
			    encoding: encoding
			      length: strlen(cString)];
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
			 length: (size_t)cStringLength
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithData: (OFData *)data
		    encoding: (OFStringEncoding)encoding
{
	@try {
		if (data.itemSize != 1)
			@throw [OFInvalidArgumentException exception];
	} @catch (id e) {
		[self release];
		@throw e;
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
}

- (instancetype)initWithString: (OFString *)string
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithCharacters: (const of_unichar_t *)string
			    length: (size_t)length
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
{
	return [self initWithUTF16String: string
				  length: of_string_utf16_length(string)
			       byteOrder: OF_BYTE_ORDER_NATIVE];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length
{
	return [self initWithUTF16String: string
				  length: length
			       byteOrder: OF_BYTE_ORDER_NATIVE];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			  byteOrder: (of_byte_order_t)byteOrder
{
	return [self initWithUTF16String: string
				  length: of_string_utf16_length(string)
			       byteOrder: byteOrder];
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
{
	return [self initWithUTF32String: string
				  length: of_string_utf32_length(string)
			       byteOrder: OF_BYTE_ORDER_NATIVE];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			     length: (size_t)length
{
	return [self initWithUTF32String: string
				  length: length
			       byteOrder: OF_BYTE_ORDER_NATIVE];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			  byteOrder: (of_byte_order_t)byteOrder
{
	return [self initWithUTF32String: string
				  length: of_string_utf32_length(string)
			       byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const of_char32_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithFormat: (OFConstantString *)format, ...
{
	id ret;
	va_list arguments;

	va_start(arguments, format);
	ret = [self initWithFormat: format
			 arguments: arguments];
	va_end(arguments);

	return ret;
}

- (instancetype)initWithFormat: (OFConstantString *)format
		     arguments: (va_list)arguments
{
	OF_INVALID_INIT_METHOD
}

#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
	return [self initWithContentsOfFile: path
				   encoding: OF_STRING_ENCODING_UTF_8];
}

- (instancetype)initWithContentsOfFile: (OFString *)path
			      encoding: (of_string_encoding_t)encoding
{
	char *tmp;
	unsigned long long fileSize;

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFFile *file = nil;







|





|


|
|


|




|


|
|


|



|

|




|


|
|


|




|


|
|


|



|

|










|
<















|



|







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
}

- (instancetype)initWithString: (OFString *)string
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithCharacters: (const OFUnichar *)string
			    length: (size_t)length
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
{
	return [self initWithUTF16String: string
				  length: OFUTF16StringLength(string)
			       byteOrder: OFByteOrderNative];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length
{
	return [self initWithUTF16String: string
				  length: length
			       byteOrder: OFByteOrderNative];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			  byteOrder: (OFByteOrder)byteOrder
{
	return [self initWithUTF16String: string
				  length: OFUTF16StringLength(string)
			       byteOrder: byteOrder];
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
{
	return [self initWithUTF32String: string
				  length: OFUTF32StringLength(string)
			       byteOrder: OFByteOrderNative];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			     length: (size_t)length
{
	return [self initWithUTF32String: string
				  length: length
			       byteOrder: OFByteOrderNative];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			  byteOrder: (OFByteOrder)byteOrder
{
	return [self initWithUTF32String: string
				  length: OFUTF32StringLength(string)
			       byteOrder: byteOrder];
}

- (instancetype)initWithUTF32String: (const OFChar32 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithFormat: (OFConstantString *)format, ...
{
	id ret;
	va_list arguments;

	va_start(arguments, format);
	ret = [self initWithFormat: format arguments: arguments];

	va_end(arguments);

	return ret;
}

- (instancetype)initWithFormat: (OFConstantString *)format
		     arguments: (va_list)arguments
{
	OF_INVALID_INIT_METHOD
}

#ifdef OF_HAVE_FILES
- (instancetype)initWithContentsOfFile: (OFString *)path
{
	return [self initWithContentsOfFile: path
				   encoding: OFStringEncodingUTF8];
}

- (instancetype)initWithContentsOfFile: (OFString *)path
			      encoding: (OFStringEncoding)encoding
{
	char *tmp;
	unsigned long long fileSize;

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFFile *file = nil;
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
		/*
		 * We need one extra byte for the terminating zero if we want
		 * to use -[initWithUTF8StringNoCopy:length:freeWhenDone:].
		 */
		if (SIZE_MAX - (size_t)fileSize < 1)
			@throw [OFOutOfRangeException exception];

		tmp = of_malloc(1, (size_t)fileSize + 1);
		@try {
			file = [[OFFile alloc] initWithPath: path
						       mode: @"r"];

			[file readIntoBuffer: tmp
				 exactLength: (size_t)fileSize];
		} @catch (id e) {
			of_free(tmp);
			@throw e;
		} @finally {
			[file release];
		}

		tmp[(size_t)fileSize] = '\0';
	} @catch (id e) {
		[self release];
		@throw e;
	}

	if (encoding == OF_STRING_ENCODING_UTF_8)

		self = [self initWithUTF8StringNoCopy: tmp
					       length: (size_t)fileSize
					 freeWhenDone: true];




	else {
		@try {
			self = [self initWithCString: tmp
					    encoding: encoding
					      length: (size_t)fileSize];
		} @finally {
			of_free(tmp);
		}
	}

	return self;
}
#endif

- (instancetype)initWithContentsOfURL: (OFURL *)URL
{
	return [self initWithContentsOfURL: URL
				  encoding: OF_STRING_ENCODING_AUTODETECT];
}

- (instancetype)initWithContentsOfURL: (OFURL *)URL
			     encoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data;

	@try {
		data = [OFData dataWithContentsOfURL: URL];
	} @catch (id e) {







|

|
<
<



|











|
>
|
|
|
>
>
>
>
|





|










|



|







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
		/*
		 * We need one extra byte for the terminating zero if we want
		 * to use -[initWithUTF8StringNoCopy:length:freeWhenDone:].
		 */
		if (SIZE_MAX - (size_t)fileSize < 1)
			@throw [OFOutOfRangeException exception];

		tmp = OFAllocMemory((size_t)fileSize + 1, 1);
		@try {
			file = [[OFFile alloc] initWithPath: path mode: @"r"];


			[file readIntoBuffer: tmp
				 exactLength: (size_t)fileSize];
		} @catch (id e) {
			OFFreeMemory(tmp);
			@throw e;
		} @finally {
			[file release];
		}

		tmp[(size_t)fileSize] = '\0';
	} @catch (id e) {
		[self release];
		@throw e;
	}

	if (encoding == OFStringEncodingUTF8) {
		@try {
			self = [self initWithUTF8StringNoCopy: tmp
						       length: (size_t)fileSize
						 freeWhenDone: true];
		} @catch (id e) {
			OFFreeMemory(tmp);
			@throw e;
		}
	} else {
		@try {
			self = [self initWithCString: tmp
					    encoding: encoding
					      length: (size_t)fileSize];
		} @finally {
			OFFreeMemory(tmp);
		}
	}

	return self;
}
#endif

- (instancetype)initWithContentsOfURL: (OFURL *)URL
{
	return [self initWithContentsOfURL: URL
				  encoding: OFStringEncodingAutodetect];
}

- (instancetype)initWithContentsOfURL: (OFURL *)URL
			     encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data;

	@try {
		data = [OFData dataWithContentsOfURL: URL];
	} @catch (id e) {
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stringValue;

	@try {
		if (![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		if ([self isKindOfClass: [OFMutableString class]]) {
			if (![element.name isEqual: @"OFMutableString"])
				@throw [OFInvalidArgumentException exception];
		} else {
			if (![element.name isEqual: @"OFString"])







|







1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stringValue;

	@try {
		if (![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		if ([self isKindOfClass: [OFMutableString class]]) {
			if (![element.name isEqual: @"OFMutableString"])
				@throw [OFInvalidArgumentException exception];
		} else {
			if (![element.name isEqual: @"OFString"])
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
	objc_autoreleasePoolPop(pool);

	return self;
}

- (size_t)of_getCString: (char *)cString
	      maxLength: (size_t)maxLength
	       encoding: (of_string_encoding_t)encoding
		  lossy: (bool)lossy
{
	const of_unichar_t *characters = self.characters;
	size_t i, length = self.length;

	switch (encoding) {
	case OF_STRING_ENCODING_UTF_8:;
		size_t j = 0;

		for (i = 0; i < length; i++) {
			char buffer[4];
			size_t len = of_string_utf8_encode(characters[i],
			    buffer);

			/*
			 * Check for one more than the current index, as we
			 * need one for the terminating zero.
			 */
			if (j + len >= maxLength)
				@throw [OFOutOfRangeException exception];







|


|



|




|
<







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
	objc_autoreleasePoolPop(pool);

	return self;
}

- (size_t)of_getCString: (char *)cString
	      maxLength: (size_t)maxLength
	       encoding: (OFStringEncoding)encoding
		  lossy: (bool)lossy
{
	const OFUnichar *characters = self.characters;
	size_t i, length = self.length;

	switch (encoding) {
	case OFStringEncodingUTF8:;
		size_t j = 0;

		for (i = 0; i < length; i++) {
			char buffer[4];
			size_t len = OFUTF8StringEncode(characters[i], buffer);


			/*
			 * Check for one more than the current index, as we
			 * need one for the terminating zero.
			 */
			if (j + len >= maxLength)
				@throw [OFOutOfRangeException exception];
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
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
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
				break;
			}
		}

		cString[j] = '\0';

		return j;
	case OF_STRING_ENCODING_ASCII:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		for (i = 0; i < length; i++) {
			if OF_UNLIKELY (characters[i] > 0x80) {
				if (lossy)
					cString[i] = '?';
				else
					@throw [OFInvalidEncodingException
					    exception];
			} else
				cString[i] = (unsigned char)characters[i];
		}

		cString[i] = '\0';

		return length;
	case OF_STRING_ENCODING_ISO_8859_1:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		for (i = 0; i < length; i++) {
			if OF_UNLIKELY (characters[i] > 0xFF) {
				if (lossy)
					cString[i] = '?';
				else
					@throw [OFInvalidEncodingException
					    exception];
			} else
				cString[i] = (unsigned char)characters[i];
		}

		cString[i] = '\0';

		return length;
#ifdef HAVE_ISO_8859_2
	case OF_STRING_ENCODING_ISO_8859_2:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_iso_8859_2(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_ISO_8859_3
	case OF_STRING_ENCODING_ISO_8859_3:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_iso_8859_3(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_ISO_8859_15
	case OF_STRING_ENCODING_ISO_8859_15:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_iso_8859_15(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_WINDOWS_1251
	case OF_STRING_ENCODING_WINDOWS_1251:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_windows_1251(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_WINDOWS_1252
	case OF_STRING_ENCODING_WINDOWS_1252:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_windows_1252(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_CODEPAGE_437
	case OF_STRING_ENCODING_CODEPAGE_437:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_codepage_437(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_CODEPAGE_850
	case OF_STRING_ENCODING_CODEPAGE_850:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_codepage_850(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_CODEPAGE_858
	case OF_STRING_ENCODING_CODEPAGE_858:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_codepage_858(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_MAC_ROMAN
	case OF_STRING_ENCODING_MAC_ROMAN:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_mac_roman(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_KOI8_R
	case OF_STRING_ENCODING_KOI8_R:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_koi8_r(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_KOI8_U
	case OF_STRING_ENCODING_KOI8_U:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!of_unicode_to_koi8_u(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
	default:
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];
	}
}

- (size_t)getCString: (char *)cString
	   maxLength: (size_t)maxLength
	    encoding: (of_string_encoding_t)encoding
{
	return [self of_getCString: cString
			 maxLength: maxLength
			  encoding: encoding
			     lossy: false];
}

- (size_t)getLossyCString: (char *)cString
		maxLength: (size_t)maxLength
		 encoding: (of_string_encoding_t)encoding
{
	return [self of_getCString: cString
			 maxLength: maxLength
			  encoding: encoding
			     lossy: true];
}

- (const char *)of_cStringWithEncoding: (of_string_encoding_t)encoding
				 lossy: (bool)lossy
{
	OFObject *object = [[[OFObject alloc] init] autorelease];
	size_t length = self.length;
	char *cString;



	switch (encoding) {
	case OF_STRING_ENCODING_UTF_8:;
		size_t cStringLength;

		cString = [object allocMemoryWithSize: (length * 4) + 1];

		cStringLength = [self of_getCString: cString

					  maxLength: (length * 4) + 1
					   encoding: OF_STRING_ENCODING_UTF_8
					      lossy: lossy];





		@try {
			cString = [object resizeMemory: cString
						  size: cStringLength + 1];
		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only tried to make it smaller */
		}

		break;
	case OF_STRING_ENCODING_ASCII:
	case OF_STRING_ENCODING_ISO_8859_1:
	case OF_STRING_ENCODING_ISO_8859_2:
	case OF_STRING_ENCODING_ISO_8859_3:
	case OF_STRING_ENCODING_ISO_8859_15:
	case OF_STRING_ENCODING_WINDOWS_1251:
	case OF_STRING_ENCODING_WINDOWS_1252:
	case OF_STRING_ENCODING_CODEPAGE_437:
	case OF_STRING_ENCODING_CODEPAGE_850:
	case OF_STRING_ENCODING_CODEPAGE_858:
	case OF_STRING_ENCODING_MAC_ROMAN:
	case OF_STRING_ENCODING_KOI8_R:
	case OF_STRING_ENCODING_KOI8_U:
		cString = [object allocMemoryWithSize: length + 1];


		[self of_getCString: cString
			  maxLength: length + 1
			   encoding: encoding
			      lossy: lossy];





		break;
	default:
		@throw [OFInvalidEncodingException exception];
	}










	return cString;
}

- (const char *)cStringWithEncoding: (of_string_encoding_t)encoding
{
	return [self of_cStringWithEncoding: encoding
				      lossy: false];
}

- (const char *)lossyCStringWithEncoding: (of_string_encoding_t)encoding
{
	return [self of_cStringWithEncoding: encoding
				      lossy: true];
}

- (const char *)UTF8String
{
	return [self cStringWithEncoding: OF_STRING_ENCODING_UTF_8];
}

- (size_t)length
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)cStringLengthWithEncoding: (of_string_encoding_t)encoding
{
	switch (encoding) {
	case OF_STRING_ENCODING_UTF_8:;
		const of_unichar_t *characters;
		size_t length, UTF8StringLength = 0;

		characters = self.characters;
		length = self.length;

		for (size_t i = 0; i < length; i++) {
			char buffer[4];
			size_t len = of_string_utf8_encode(characters[i],
			    buffer);

			if (len == 0)
				@throw [OFInvalidEncodingException exception];

			UTF8StringLength += len;
		}

		return UTF8StringLength;
	case OF_STRING_ENCODING_ASCII:
	case OF_STRING_ENCODING_ISO_8859_1:
	case OF_STRING_ENCODING_ISO_8859_2:
	case OF_STRING_ENCODING_ISO_8859_3:
	case OF_STRING_ENCODING_ISO_8859_15:
	case OF_STRING_ENCODING_WINDOWS_1251:
	case OF_STRING_ENCODING_WINDOWS_1252:
	case OF_STRING_ENCODING_CODEPAGE_437:
	case OF_STRING_ENCODING_CODEPAGE_850:
	case OF_STRING_ENCODING_CODEPAGE_858:
	case OF_STRING_ENCODING_MAC_ROMAN:
	case OF_STRING_ENCODING_KOI8_R:
	case OF_STRING_ENCODING_KOI8_U:
		return self.length;
	default:
		@throw [OFInvalidEncodingException exception];
	}
}

- (size_t)UTF8StringLength
{
	return [self cStringLengthWithEncoding: OF_STRING_ENCODING_UTF_8];
}

- (of_unichar_t)characterAtIndex: (size_t)idx
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)getCharacters: (of_unichar_t *)buffer
	      inRange: (of_range_t)range
{
	for (size_t i = 0; i < range.length; i++)
		buffer[i] = [self characterAtIndex: range.location + i];
}

- (bool)isEqual: (id)object
{
	void *pool;
	OFString *otherString;
	const of_unichar_t *characters, *otherCharacters;
	size_t length;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFString class]])
		return false;

	otherString = object;
	length = self.length;

	if (otherString.length != length)
		return false;

	pool = objc_autoreleasePoolPush();

	characters = self.characters;
	otherCharacters = otherString.characters;

	if (memcmp(characters, otherCharacters,
	    length * sizeof(of_unichar_t)) != 0) {
		objc_autoreleasePoolPop(pool);
		return false;
	}

	objc_autoreleasePoolPop(pool);

	return true;
}

- (id)copy
{
	return [self retain];
}

- (id)mutableCopy
{
	return [[OFMutableString alloc] initWithString: self];
}

- (of_comparison_result_t)compare: (id <OFComparing>)object
{
	void *pool;
	OFString *otherString;
	const of_unichar_t *characters, *otherCharacters;
	size_t minimumLength;

	if (object == self)
		return OF_ORDERED_SAME;

	if (![(id)object isKindOfClass: [OFString class]])
		@throw [OFInvalidArgumentException exception];

	otherString = (OFString *)object;
	minimumLength = (self.length > otherString.length
	    ? otherString.length : self.length);

	pool = objc_autoreleasePoolPush();

	characters = self.characters;
	otherCharacters = otherString.characters;

	for (size_t i = 0; i < minimumLength; i++) {
		if (characters[i] > otherCharacters[i]) {
			objc_autoreleasePoolPop(pool);
			return OF_ORDERED_DESCENDING;
		}

		if (characters[i] < otherCharacters[i]) {
			objc_autoreleasePoolPop(pool);
			return OF_ORDERED_ASCENDING;
		}
	}

	objc_autoreleasePoolPop(pool);

	if (self.length > otherString.length)
		return OF_ORDERED_DESCENDING;
	if (self.length < otherString.length)
		return OF_ORDERED_ASCENDING;

	return OF_ORDERED_SAME;
}

- (of_comparison_result_t)caseInsensitiveCompare: (OFString *)otherString
{
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters, *otherCharacters;
	size_t length, otherLength, minimumLength;

	if (otherString == self)
		return OF_ORDERED_SAME;

	characters = self.characters;
	otherCharacters = otherString.characters;
	length = self.length;
	otherLength = otherString.length;

	minimumLength = (length > otherLength ? otherLength : length);

	for (size_t i = 0; i < minimumLength; i++) {
		of_unichar_t c = characters[i];
		of_unichar_t oc = otherCharacters[i];

#ifdef OF_HAVE_UNICODE_TABLES
		if (c >> 8 < OF_UNICODE_CASEFOLDING_TABLE_SIZE) {
			of_unichar_t tc =
			    of_unicode_casefolding_table[c >> 8][c & 0xFF];

			if (tc)
				c = tc;
		}
		if (oc >> 8 < OF_UNICODE_CASEFOLDING_TABLE_SIZE) {
			of_unichar_t tc =
			    of_unicode_casefolding_table[oc >> 8][oc & 0xFF];

			if (tc)
				oc = tc;
		}
#else
		c = of_ascii_toupper(c);
		oc = of_ascii_toupper(oc);
#endif

		if (c > oc) {
			objc_autoreleasePoolPop(pool);
			return OF_ORDERED_DESCENDING;
		}
		if (c < oc) {
			objc_autoreleasePoolPop(pool);
			return OF_ORDERED_ASCENDING;
		}
	}

	objc_autoreleasePoolPop(pool);

	if (length > otherLength)
		return OF_ORDERED_DESCENDING;
	if (length < otherLength)
		return OF_ORDERED_ASCENDING;

	return OF_ORDERED_SAME;
}

- (unsigned long)hash
{
	const of_unichar_t *characters = self.characters;
	size_t length = self.length;
	uint32_t hash;

	OF_HASH_INIT(hash);

	for (size_t i = 0; i < length; i++) {
		const of_unichar_t c = characters[i];

		OF_HASH_ADD(hash, (c & 0xFF0000) >> 16);
		OF_HASH_ADD(hash, (c & 0x00FF00) >> 8);
		OF_HASH_ADD(hash, c & 0x0000FF);
	}

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	return [[self copy] autorelease];







|

















|


















|



|
|







|



|
|







|



|
|







|



|








|



|








|



|








|



|








|



|








|



|
|







|



|
|







|



|
|














|









|







|


<


>
>


|
|

<
|
|
>
|
|
|
>
>
>
|
>

<
|





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

>
|
|
|
|
>
>
>
|
>





>
>
>
>
>
>
>
>
>
|


|

|
<


|

|
<




|







|


|
|







|
<








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








|


|




|
|








|
|








|


|





|


|



















|


<
|


|
|

|


<
|
|




|




|




|





|
|
|
|

|


|


|


|
|


|

|




|
|


|
|
|




|
|
|





|
|




|



|






|

|

|




|

|

|


|

|
|
|


|







1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
1453
1454
1455
1456

1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481

1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
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
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
				break;
			}
		}

		cString[j] = '\0';

		return j;
	case OFStringEncodingASCII:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		for (i = 0; i < length; i++) {
			if OF_UNLIKELY (characters[i] > 0x80) {
				if (lossy)
					cString[i] = '?';
				else
					@throw [OFInvalidEncodingException
					    exception];
			} else
				cString[i] = (unsigned char)characters[i];
		}

		cString[i] = '\0';

		return length;
	case OFStringEncodingISO8859_1:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		for (i = 0; i < length; i++) {
			if OF_UNLIKELY (characters[i] > 0xFF) {
				if (lossy)
					cString[i] = '?';
				else
					@throw [OFInvalidEncodingException
					    exception];
			} else
				cString[i] = (unsigned char)characters[i];
		}

		cString[i] = '\0';

		return length;
#ifdef HAVE_ISO_8859_2
	case OFStringEncodingISO8859_2:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToISO8859_2(characters, (unsigned char *)cString,
		    length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_ISO_8859_3
	case OFStringEncodingISO8859_3:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToISO8859_3(characters, (unsigned char *)cString,
		    length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_ISO_8859_15
	case OFStringEncodingISO8859_15:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToISO8859_15(characters, (unsigned char *)cString,
		    length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_WINDOWS_1251
	case OFStringEncodingWindows1251:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToWindows1251(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_WINDOWS_1252
	case OFStringEncodingWindows1252:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToWindows1252(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_CODEPAGE_437
	case OFStringEncodingCodepage437:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToCodepage437(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_CODEPAGE_850
	case OFStringEncodingCodepage850:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToCodepage850(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_CODEPAGE_858
	case OFStringEncodingCodepage858:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToCodepage858(characters,
		    (unsigned char *)cString, length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_MAC_ROMAN
	case OFStringEncodingMacRoman:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToMacRoman(characters, (unsigned char *)cString,
		    length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_KOI8_R
	case OFStringEncodingKOI8R:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToKOI8R(characters, (unsigned char *)cString,
		    length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
#ifdef HAVE_KOI8_U
	case OFStringEncodingKOI8U:
		if (length + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		if (!OFUnicodeToKOI8U(characters, (unsigned char *)cString,
		    length, lossy))
			@throw [OFInvalidEncodingException exception];

		cString[length] = '\0';

		return length;
#endif
	default:
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];
	}
}

- (size_t)getCString: (char *)cString
	   maxLength: (size_t)maxLength
	    encoding: (OFStringEncoding)encoding
{
	return [self of_getCString: cString
			 maxLength: maxLength
			  encoding: encoding
			     lossy: false];
}

- (size_t)getLossyCString: (char *)cString
		maxLength: (size_t)maxLength
		 encoding: (OFStringEncoding)encoding
{
	return [self of_getCString: cString
			 maxLength: maxLength
			  encoding: encoding
			     lossy: true];
}

- (const char *)of_cStringWithEncoding: (OFStringEncoding)encoding
				 lossy: (bool)lossy
{

	size_t length = self.length;
	char *cString;
	size_t cStringLength;
	const char *ret;

	switch (encoding) {
	case OFStringEncodingUTF8:
		cString = OFAllocMemory((length * 4) + 1, 1);


		@try {
			cStringLength = [self
			    of_getCString: cString
				maxLength: (length * 4) + 1
				 encoding: OFStringEncodingUTF8
				    lossy: lossy];
		} @catch (id e) {
			OFFreeMemory(cString);
			@throw e;
		}

		@try {

			cString = OFResizeMemory(cString, cStringLength + 1, 1);
		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only tried to make it smaller */
		}

		break;
	case OFStringEncodingASCII:
	case OFStringEncodingISO8859_1:
	case OFStringEncodingISO8859_2:
	case OFStringEncodingISO8859_3:
	case OFStringEncodingISO8859_15:
	case OFStringEncodingWindows1251:
	case OFStringEncodingWindows1252:
	case OFStringEncodingCodepage437:
	case OFStringEncodingCodepage850:
	case OFStringEncodingCodepage858:
	case OFStringEncodingMacRoman:
	case OFStringEncodingKOI8R:
	case OFStringEncodingKOI8U:
		cString = OFAllocMemory(length + 1, 1);

		@try {
			cStringLength = [self of_getCString: cString
						  maxLength: length + 1
						   encoding: encoding
						      lossy: lossy];
		} @catch (id e) {
			OFFreeMemory(cString);
			@throw e;
		}

		break;
	default:
		@throw [OFInvalidEncodingException exception];
	}

	@try {
		ret = [[OFData dataWithItemsNoCopy: cString
					     count: cStringLength + 1
				      freeWhenDone: true] items];
	} @catch (id e) {
		OFFreeMemory(cString);
		@throw e;
	}

	return ret;
}

- (const char *)cStringWithEncoding: (OFStringEncoding)encoding
{
	return [self of_cStringWithEncoding: encoding lossy: false];

}

- (const char *)lossyCStringWithEncoding: (OFStringEncoding)encoding
{
	return [self of_cStringWithEncoding: encoding lossy: true];

}

- (const char *)UTF8String
{
	return [self cStringWithEncoding: OFStringEncodingUTF8];
}

- (size_t)length
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)cStringLengthWithEncoding: (OFStringEncoding)encoding
{
	switch (encoding) {
	case OFStringEncodingUTF8:;
		const OFUnichar *characters;
		size_t length, UTF8StringLength = 0;

		characters = self.characters;
		length = self.length;

		for (size_t i = 0; i < length; i++) {
			char buffer[4];
			size_t len = OFUTF8StringEncode(characters[i], buffer);


			if (len == 0)
				@throw [OFInvalidEncodingException exception];

			UTF8StringLength += len;
		}

		return UTF8StringLength;
	case OFStringEncodingASCII:
	case OFStringEncodingISO8859_1:
	case OFStringEncodingISO8859_2:
	case OFStringEncodingISO8859_3:
	case OFStringEncodingISO8859_15:
	case OFStringEncodingWindows1251:
	case OFStringEncodingWindows1252:
	case OFStringEncodingCodepage437:
	case OFStringEncodingCodepage850:
	case OFStringEncodingCodepage858:
	case OFStringEncodingMacRoman:
	case OFStringEncodingKOI8R:
	case OFStringEncodingKOI8U:
		return self.length;
	default:
		@throw [OFInvalidEncodingException exception];
	}
}

- (size_t)UTF8StringLength
{
	return [self cStringLengthWithEncoding: OFStringEncodingUTF8];
}

- (OFUnichar)characterAtIndex: (size_t)idx
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)getCharacters: (OFUnichar *)buffer
	      inRange: (OFRange)range
{
	for (size_t i = 0; i < range.length; i++)
		buffer[i] = [self characterAtIndex: range.location + i];
}

- (bool)isEqual: (id)object
{
	void *pool;
	OFString *string;
	const OFUnichar *characters, *otherCharacters;
	size_t length;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFString class]])
		return false;

	string = object;
	length = self.length;

	if (string.length != length)
		return false;

	pool = objc_autoreleasePoolPush();

	characters = self.characters;
	otherCharacters = string.characters;

	if (memcmp(characters, otherCharacters,
	    length * sizeof(OFUnichar)) != 0) {
		objc_autoreleasePoolPop(pool);
		return false;
	}

	objc_autoreleasePoolPop(pool);

	return true;
}

- (id)copy
{
	return [self retain];
}

- (id)mutableCopy
{
	return [[OFMutableString alloc] initWithString: self];
}

- (OFComparisonResult)compare: (OFString *)string
{
	void *pool;

	const OFUnichar *characters, *otherCharacters;
	size_t minimumLength;

	if (string == self)
		return OFOrderedSame;

	if (![string isKindOfClass: [OFString class]])
		@throw [OFInvalidArgumentException exception];


	minimumLength = (self.length > string.length
	    ? string.length : self.length);

	pool = objc_autoreleasePoolPush();

	characters = self.characters;
	otherCharacters = string.characters;

	for (size_t i = 0; i < minimumLength; i++) {
		if (characters[i] > otherCharacters[i]) {
			objc_autoreleasePoolPop(pool);
			return OFOrderedDescending;
		}

		if (characters[i] < otherCharacters[i]) {
			objc_autoreleasePoolPop(pool);
			return OFOrderedAscending;
		}
	}

	objc_autoreleasePoolPop(pool);

	if (self.length > string.length)
		return OFOrderedDescending;
	if (self.length < string.length)
		return OFOrderedAscending;

	return OFOrderedSame;
}

- (OFComparisonResult)caseInsensitiveCompare: (OFString *)string
{
	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters, *otherCharacters;
	size_t length, otherLength, minimumLength;

	if (string == self)
		return OFOrderedSame;

	characters = self.characters;
	otherCharacters = string.characters;
	length = self.length;
	otherLength = string.length;

	minimumLength = (length > otherLength ? otherLength : length);

	for (size_t i = 0; i < minimumLength; i++) {
		OFUnichar c = characters[i];
		OFUnichar oc = otherCharacters[i];

#ifdef OF_HAVE_UNICODE_TABLES
		if (c >> 8 < OFUnicodeCaseFoldingTableSize) {
			OFUnichar tc =
			    OFUnicodeCaseFoldingTable[c >> 8][c & 0xFF];

			if (tc)
				c = tc;
		}
		if (oc >> 8 < OFUnicodeCaseFoldingTableSize) {
			OFUnichar tc =
			    OFUnicodeCaseFoldingTable[oc >> 8][oc & 0xFF];

			if (tc)
				oc = tc;
		}
#else
		c = OFASCIIToUpper(c);
		oc = OFASCIIToUpper(oc);
#endif

		if (c > oc) {
			objc_autoreleasePoolPop(pool);
			return OFOrderedDescending;
		}
		if (c < oc) {
			objc_autoreleasePoolPop(pool);
			return OFOrderedAscending;
		}
	}

	objc_autoreleasePoolPop(pool);

	if (length > otherLength)
		return OFOrderedDescending;
	if (length < otherLength)
		return OFOrderedAscending;

	return OFOrderedSame;
}

- (unsigned long)hash
{
	const OFUnichar *characters = self.characters;
	size_t length = self.length;
	unsigned long hash;

	OFHashInit(&hash);

	for (size_t i = 0; i < length; i++) {
		const OFUnichar c = characters[i];

		OFHashAdd(&hash, (c & 0xFF0000) >> 16);
		OFHashAdd(&hash, (c & 0x00FF00) >> 8);
		OFHashAdd(&hash, c & 0x0000FF);
	}

	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)description
{
	return [[self copy] autorelease];
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716

1717
1718
1719
1720
1721
1722

1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
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
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005

	if ([self isKindOfClass: [OFMutableString class]])
		className = @"OFMutableString";
	else
		className = @"OFString";

	element = [OFXMLElement elementWithName: className
				      namespace: OF_SERIALIZATION_NS
				    stringValue: self];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0
						depth: 0];
}

- (OFString *)JSONRepresentationWithOptions: (int)options

{
	return [self of_JSONRepresentationWithOptions: options
						depth: 0];
}

- (OFString *)of_JSONRepresentationWithOptions: (int)options

					 depth: (size_t)depth
{
	OFMutableString *JSON = [[self mutableCopy] autorelease];

	/* FIXME: This is slow! Write it in pure C! */
	[JSON replaceOccurrencesOfString: @"\\"
			      withString: @"\\\\"];
	[JSON replaceOccurrencesOfString: @"\""
			      withString: @"\\\""];
	[JSON replaceOccurrencesOfString: @"\b"
			      withString: @"\\b"];
	[JSON replaceOccurrencesOfString: @"\f"
			      withString: @"\\f"];
	[JSON replaceOccurrencesOfString: @"\r"
			      withString: @"\\r"];
	[JSON replaceOccurrencesOfString: @"\t"
			      withString: @"\\t"];

	if (options & OF_JSON_REPRESENTATION_JSON5) {
		[JSON replaceOccurrencesOfString: @"\n"
				      withString: @"\\\n"];

		if (options & OF_JSON_REPRESENTATION_IDENTIFIER) {
			const char *cString = self.UTF8String;

			if ((!of_ascii_isalpha(cString[0]) &&
			    cString[0] != '_' && cString[0] != '$') ||
			    strpbrk(cString, " \n\r\t\b\f\\\"'") != NULL) {
				[JSON prependString: @"\""];
				[JSON appendString: @"\""];
			}
		} else {
			[JSON prependString: @"\""];
			[JSON appendString: @"\""];
		}
	} else {
		[JSON replaceOccurrencesOfString: @"\n"
				      withString: @"\\n"];

		[JSON prependString: @"\""];
		[JSON appendString: @"\""];
	}

	[JSON makeImmutable];

	return JSON;
}

- (OFData *)messagePackRepresentation
{
	OFMutableData *data;
	size_t length;

	length = self.UTF8StringLength;

	if (length <= 31) {
		uint8_t tmp = 0xA0 | ((uint8_t)length & 0x1F);

		data = [OFMutableData dataWithItemSize: 1
					      capacity: length + 1];

		[data addItem: &tmp];
	} else if (length <= UINT8_MAX) {
		uint8_t type = 0xD9;
		uint8_t tmp = (uint8_t)length;

		data = [OFMutableData dataWithItemSize: 1
					      capacity: length + 2];

		[data addItem: &type];
		[data addItem: &tmp];
	} else if (length <= UINT16_MAX) {
		uint8_t type = 0xDA;
		uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)length);

		data = [OFMutableData dataWithItemSize: 1
					      capacity: length + 3];

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else if (length <= UINT32_MAX) {
		uint8_t type = 0xDB;
		uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)length);

		data = [OFMutableData dataWithItemSize: 1
					      capacity: length + 5];

		[data addItem: &type];
		[data addItems: &tmp
			 count: sizeof(tmp)];
	} else
		@throw [OFOutOfRangeException exception];

	[data addItems: self.UTF8String
		 count: length];

	return data;
}

- (of_range_t)rangeOfString: (OFString *)string
{
	return [self rangeOfString: string
			   options: 0
			     range: of_range(0, self.length)];
}

- (of_range_t)rangeOfString: (OFString *)string
		    options: (int)options
{
	return [self rangeOfString: string
			   options: options
			     range: of_range(0, self.length)];
}

- (of_range_t)rangeOfString: (OFString *)string
		    options: (int)options
		      range: (of_range_t)range
{
	void *pool;
	const of_unichar_t *searchCharacters;
	of_unichar_t *characters;
	size_t searchLength;

	if ((searchLength = string.length) == 0)
		return of_range(0, 0);

	if (searchLength > range.length)
		return of_range(OF_NOT_FOUND, 0);

	if (range.length > SIZE_MAX / sizeof(of_unichar_t))
		@throw [OFOutOfRangeException exception];

	pool = objc_autoreleasePoolPush();

	searchCharacters = string.characters;

	characters = of_malloc(range.length, sizeof(of_unichar_t));
	@try {
		[self getCharacters: characters
			    inRange: range];

		if (options & OF_STRING_SEARCH_BACKWARDS) {
			for (size_t i = range.length - searchLength;; i--) {
				if (memcmp(characters + i, searchCharacters,
				    searchLength * sizeof(of_unichar_t)) == 0) {
					objc_autoreleasePoolPop(pool);
					return of_range(range.location + i,
					    searchLength);
				}

				/* No match and we're at the last character */
				if (i == 0)
					break;
			}
		} else {
			for (size_t i = 0;
			    i <= range.length - searchLength; i++) {
				if (memcmp(characters + i, searchCharacters,
				    searchLength * sizeof(of_unichar_t)) == 0) {
					objc_autoreleasePoolPop(pool);
					return of_range(range.location + i,
					    searchLength);
				}
			}
		}
	} @finally {
		of_free(characters);
	}

	objc_autoreleasePoolPop(pool);

	return of_range(OF_NOT_FOUND, 0);
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
{
	return [self indexOfCharacterFromSet: characterSet
				     options: 0
				       range: of_range(0, self.length)];
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (int)options
{
	return [self indexOfCharacterFromSet: characterSet
				     options: options
				       range: of_range(0, self.length)];
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (int)options
			    range: (of_range_t)range
{
	bool (*characterIsMember)(id, SEL, of_unichar_t) =
	    (bool (*)(id, SEL, of_unichar_t))[characterSet
	    methodForSelector: @selector(characterIsMember:)];
	of_unichar_t *characters;

	if (range.length == 0)
		return OF_NOT_FOUND;

	if (range.length > SIZE_MAX / sizeof(of_unichar_t))
		@throw [OFOutOfRangeException exception];

	characters = of_malloc(range.length, sizeof(of_unichar_t));
	@try {
		[self getCharacters: characters
			    inRange: range];

		if (options & OF_STRING_SEARCH_BACKWARDS) {
			for (size_t i = range.length - 1;; i--) {
				if (characterIsMember(characterSet,
				    @selector(characterIsMember:),
				    characters[i]))
					return range.location + i;

				/* No match and we're at the last character */
				if (i == 0)
					break;
			}
		} else {
			for (size_t i = 0; i < range.length; i++)
				if (characterIsMember(characterSet,
				    @selector(characterIsMember:),
				    characters[i]))
					return range.location + i;
		}
	} @finally {
		of_free(characters);
	}

	return OF_NOT_FOUND;
}

- (bool)containsString: (OFString *)string
{
	void *pool;
	const of_unichar_t *characters, *searchCharacters;
	size_t length, searchLength;

	if ((searchLength = string.length) == 0)
		return true;

	if (searchLength > (length = self.length))
		return false;

	pool = objc_autoreleasePoolPush();

	characters = self.characters;
	searchCharacters = string.characters;

	for (size_t i = 0; i <= length - searchLength; i++) {
		if (memcmp(characters + i, searchCharacters,
		    searchLength * sizeof(of_unichar_t)) == 0) {
			objc_autoreleasePoolPop(pool);
			return true;
		}
	}

	objc_autoreleasePoolPop(pool);

	return false;
}

- (OFString *)substringFromIndex: (size_t)idx
{
	return [self substringWithRange: of_range(idx, self.length - idx)];
}

- (OFString *)substringToIndex: (size_t)idx
{
	return [self substringWithRange: of_range(0, idx)];
}

- (OFString *)substringWithRange: (of_range_t)range
{
	void *pool;
	OFString *ret;

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







|











|
<


|
>

|
<


|
>
|




|
<
|
<
|
<
|
<
|
<
|
<

|
|
<

|


|










|
<




















|
<
<





|
<
<




|

|
<
<

|
<


|

|
<
<

|
<



|
<




|



|


|
|



|


|
|
|


|
|



|


|

|






|

|
<

|


|

|











|

|





|




|






|



|



|



|
|

|
|

|


|

|


|

|
<

|


















|


|





|















|












|




|


|







1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723

1724
1725
1726
1727
1728
1729

1730
1731
1732
1733
1734
1735
1736
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
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784


1785
1786
1787
1788
1789
1790


1791
1792
1793
1794
1795
1796
1797


1798
1799

1800
1801
1802
1803
1804


1805
1806

1807
1808
1809
1810

1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854

1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921

1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995

	if ([self isKindOfClass: [OFMutableString class]])
		className = @"OFMutableString";
	else
		className = @"OFString";

	element = [OFXMLElement elementWithName: className
				      namespace: OFSerializationNS
				    stringValue: self];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

- (OFString *)JSONRepresentation
{
	return [self of_JSONRepresentationWithOptions: 0 depth: 0];

}

- (OFString *)JSONRepresentationWithOptions:
    (OFJSONRepresentationOptions)options
{
	return [self of_JSONRepresentationWithOptions: options depth: 0];

}

- (OFString *)
    of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options
			       depth: (size_t)depth
{
	OFMutableString *JSON = [[self mutableCopy] autorelease];

	/* FIXME: This is slow! Write it in pure C! */
	[JSON replaceOccurrencesOfString: @"\\" withString: @"\\\\"];

	[JSON replaceOccurrencesOfString: @"\"" withString: @"\\\""];

	[JSON replaceOccurrencesOfString: @"\b" withString: @"\\b"];

	[JSON replaceOccurrencesOfString: @"\f" withString: @"\\f"];

	[JSON replaceOccurrencesOfString: @"\r" withString: @"\\r"];

	[JSON replaceOccurrencesOfString: @"\t" withString: @"\\t"];


	if (options & OFJSONRepresentationOptionJSON5) {
		[JSON replaceOccurrencesOfString: @"\n" withString: @"\\\n"];


		if (options & OFJSONRepresentationOptionIsIdentifier) {
			const char *cString = self.UTF8String;

			if ((!OFASCIIIsAlpha(cString[0]) &&
			    cString[0] != '_' && cString[0] != '$') ||
			    strpbrk(cString, " \n\r\t\b\f\\\"'") != NULL) {
				[JSON prependString: @"\""];
				[JSON appendString: @"\""];
			}
		} else {
			[JSON prependString: @"\""];
			[JSON appendString: @"\""];
		}
	} else {
		[JSON replaceOccurrencesOfString: @"\n" withString: @"\\n"];


		[JSON prependString: @"\""];
		[JSON appendString: @"\""];
	}

	[JSON makeImmutable];

	return JSON;
}

- (OFData *)messagePackRepresentation
{
	OFMutableData *data;
	size_t length;

	length = self.UTF8StringLength;

	if (length <= 31) {
		uint8_t tmp = 0xA0 | ((uint8_t)length & 0x1F);

		data = [OFMutableData dataWithCapacity: length + 1];


		[data addItem: &tmp];
	} else if (length <= UINT8_MAX) {
		uint8_t type = 0xD9;
		uint8_t tmp = (uint8_t)length;

		data = [OFMutableData dataWithCapacity: length + 2];


		[data addItem: &type];
		[data addItem: &tmp];
	} else if (length <= UINT16_MAX) {
		uint8_t type = 0xDA;
		uint16_t tmp = OFToBigEndian16((uint16_t)length);

		data = [OFMutableData dataWithCapacity: length + 3];


		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else if (length <= UINT32_MAX) {
		uint8_t type = 0xDB;
		uint32_t tmp = OFToBigEndian32((uint32_t)length);

		data = [OFMutableData dataWithCapacity: length + 5];


		[data addItem: &type];
		[data addItems: &tmp count: sizeof(tmp)];

	} else
		@throw [OFOutOfRangeException exception];

	[data addItems: self.UTF8String count: length];


	return data;
}

- (OFRange)rangeOfString: (OFString *)string
{
	return [self rangeOfString: string
			   options: 0
			     range: OFRangeMake(0, self.length)];
}

- (OFRange)rangeOfString: (OFString *)string
		 options: (OFStringSearchOptions)options
{
	return [self rangeOfString: string
			   options: options
			     range: OFRangeMake(0, self.length)];
}

- (OFRange)rangeOfString: (OFString *)string
		 options: (OFStringSearchOptions)options
		   range: (OFRange)range
{
	void *pool;
	const OFUnichar *searchCharacters;
	OFUnichar *characters;
	size_t searchLength;

	if ((searchLength = string.length) == 0)
		return OFRangeMake(0, 0);

	if (searchLength > range.length)
		return OFRangeMake(OFNotFound, 0);

	if (range.length > SIZE_MAX / sizeof(OFUnichar))
		@throw [OFOutOfRangeException exception];

	pool = objc_autoreleasePoolPush();

	searchCharacters = string.characters;

	characters = OFAllocMemory(range.length, sizeof(OFUnichar));
	@try {
		[self getCharacters: characters inRange: range];


		if (options & OFStringSearchBackwards) {
			for (size_t i = range.length - searchLength;; i--) {
				if (memcmp(characters + i, searchCharacters,
				    searchLength * sizeof(OFUnichar)) == 0) {
					objc_autoreleasePoolPop(pool);
					return OFRangeMake(range.location + i,
					    searchLength);
				}

				/* No match and we're at the last character */
				if (i == 0)
					break;
			}
		} else {
			for (size_t i = 0;
			    i <= range.length - searchLength; i++) {
				if (memcmp(characters + i, searchCharacters,
				    searchLength * sizeof(OFUnichar)) == 0) {
					objc_autoreleasePoolPop(pool);
					return OFRangeMake(range.location + i,
					    searchLength);
				}
			}
		}
	} @finally {
		OFFreeMemory(characters);
	}

	objc_autoreleasePoolPop(pool);

	return OFRangeMake(OFNotFound, 0);
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
{
	return [self indexOfCharacterFromSet: characterSet
				     options: 0
				       range: OFRangeMake(0, self.length)];
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (OFStringSearchOptions)options
{
	return [self indexOfCharacterFromSet: characterSet
				     options: options
				       range: OFRangeMake(0, self.length)];
}

- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
			  options: (OFStringSearchOptions)options
			    range: (OFRange)range
{
	bool (*characterIsMember)(id, SEL, OFUnichar) =
	    (bool (*)(id, SEL, OFUnichar))[characterSet
	    methodForSelector: @selector(characterIsMember:)];
	OFUnichar *characters;

	if (range.length == 0)
		return OFNotFound;

	if (range.length > SIZE_MAX / sizeof(OFUnichar))
		@throw [OFOutOfRangeException exception];

	characters = OFAllocMemory(range.length, sizeof(OFUnichar));
	@try {
		[self getCharacters: characters inRange: range];


		if (options & OFStringSearchBackwards) {
			for (size_t i = range.length - 1;; i--) {
				if (characterIsMember(characterSet,
				    @selector(characterIsMember:),
				    characters[i]))
					return range.location + i;

				/* No match and we're at the last character */
				if (i == 0)
					break;
			}
		} else {
			for (size_t i = 0; i < range.length; i++)
				if (characterIsMember(characterSet,
				    @selector(characterIsMember:),
				    characters[i]))
					return range.location + i;
		}
	} @finally {
		OFFreeMemory(characters);
	}

	return OFNotFound;
}

- (bool)containsString: (OFString *)string
{
	void *pool;
	const OFUnichar *characters, *searchCharacters;
	size_t length, searchLength;

	if ((searchLength = string.length) == 0)
		return true;

	if (searchLength > (length = self.length))
		return false;

	pool = objc_autoreleasePoolPush();

	characters = self.characters;
	searchCharacters = string.characters;

	for (size_t i = 0; i <= length - searchLength; i++) {
		if (memcmp(characters + i, searchCharacters,
		    searchLength * sizeof(OFUnichar)) == 0) {
			objc_autoreleasePoolPop(pool);
			return true;
		}
	}

	objc_autoreleasePoolPop(pool);

	return false;
}

- (OFString *)substringFromIndex: (size_t)idx
{
	return [self substringWithRange: OFRangeMake(idx, self.length - idx)];
}

- (OFString *)substringToIndex: (size_t)idx
{
	return [self substringWithRange: OFRangeMake(0, idx)];
}

- (OFString *)substringWithRange: (OFRange)range
{
	void *pool;
	OFString *ret;

	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > self.length)
		@throw [OFOutOfRangeException exception];
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240







2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261

2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337


2338




2339
2340
2341






2342
2343




2344



2345


2346


2347









2348


2349












2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367


2368
2369



2370
2371

2372





2373
2374



2375
2376






2377


2378








2379
2380


2381
2382
2383
2384
2385
2386







2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403




2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452




2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495

2496



2497

2498
2499
2500




2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518

2519
2520
2521
2522
2523
2524
2525
2526
2527
2528

2529

2530
2531
2532
2533
2534

2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557










2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585

2586
2587
2588

2589
2590
2591
2592
2593
2594

2595







2596

2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740

- (OFString *)stringByAppendingFormat: (OFConstantString *)format, ...
{
	OFString *ret;
	va_list arguments;

	va_start(arguments, format);
	ret = [self stringByAppendingFormat: format
				  arguments: arguments];
	va_end(arguments);

	return ret;
}

- (OFString *)stringByAppendingFormat: (OFConstantString *)format
			    arguments: (va_list)arguments
{
	OFMutableString *new;

	new = [OFMutableString stringWithString: self];
	[new appendFormat: format
		arguments: arguments];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByPrependingString: (OFString *)string
{
	OFMutableString *new = [[string mutableCopy] autorelease];

	[new appendString: self];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new replaceOccurrencesOfString: string
			     withString: replacement];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
					   options: (int)options
					     range: (of_range_t)range
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new replaceOccurrencesOfString: string
			     withString: replacement
				options: options
				  range: range];

	[new makeImmutable];

	return new;
}

- (OFString *)uppercaseString
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new uppercase];

	[new makeImmutable];

	return new;
}

- (OFString *)lowercaseString
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new lowercase];

	[new makeImmutable];

	return new;
}

- (OFString *)capitalizedString
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new capitalize];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByDeletingLeadingWhitespaces
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new deleteLeadingWhitespaces];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByDeletingTrailingWhitespaces
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new deleteTrailingWhitespaces];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByDeletingEnclosingWhitespaces
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new deleteEnclosingWhitespaces];

	[new makeImmutable];

	return new;
}

- (bool)hasPrefix: (OFString *)prefix
{
	of_unichar_t *tmp;
	size_t prefixLength;
	bool hasPrefix;

	if ((prefixLength = prefix.length) > self.length)
		return false;

	tmp = [self allocMemoryWithSize: sizeof(of_unichar_t)
				  count: prefixLength];
	@try {
		void *pool = objc_autoreleasePoolPush();

		[self getCharacters: tmp
			    inRange: of_range(0, prefixLength)];

		hasPrefix = (memcmp(tmp, prefix.characters,
		    prefixLength * sizeof(of_unichar_t)) == 0);

		objc_autoreleasePoolPop(pool);
	} @finally {
		[self freeMemory: tmp];
	}

	return hasPrefix;
}

- (bool)hasSuffix: (OFString *)suffix
{
	of_unichar_t *tmp;
	const of_unichar_t *suffixCharacters;
	size_t length, suffixLength;
	bool hasSuffix;

	if ((suffixLength = suffix.length) > self.length)
		return false;

	length = self.length;

	tmp = [self allocMemoryWithSize: sizeof(of_unichar_t)
				  count: suffixLength];
	@try {
		void *pool = objc_autoreleasePoolPush();

		[self getCharacters: tmp
			    inRange: of_range(length - suffixLength,
					 suffixLength)];

		suffixCharacters = suffix.characters;
		hasSuffix = (memcmp(tmp, suffixCharacters,
		    suffixLength * sizeof(of_unichar_t)) == 0);

		objc_autoreleasePoolPop(pool);
	} @finally {
		[self freeMemory: tmp];
	}

	return hasSuffix;
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
{
	return [self componentsSeparatedByString: delimiter
					 options: 0];
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
				 options: (int)options
{
	void *pool;
	OFMutableArray *array = [OFMutableArray array];
	const of_unichar_t *characters, *delimiterCharacters;
	bool skipEmpty = (options & OF_STRING_SKIP_EMPTY);
	size_t length = self.length;
	size_t delimiterLength = delimiter.length;
	size_t last;
	OFString *component;








	pool = objc_autoreleasePoolPush();

	characters = self.characters;
	delimiterCharacters = delimiter.characters;

	if (delimiterLength > length) {
		[array addObject: [[self copy] autorelease]];
		[array makeImmutable];

		objc_autoreleasePoolPop(pool);

		return array;
	}

	last = 0;
	for (size_t i = 0; i <= length - delimiterLength; i++) {
		if (memcmp(characters + i, delimiterCharacters,
		    delimiterLength * sizeof(of_unichar_t)) != 0)
			continue;

		component = [self substringWithRange: of_range(last, i - last)];

		if (!skipEmpty || component.length > 0)
			[array addObject: component];

		i += delimiterLength - 1;
		last = i + 1;
	}
	component = [self substringWithRange: of_range(last, length - last)];
	if (!skipEmpty || component.length > 0)
		[array addObject: component];

	[array makeImmutable];

	objc_autoreleasePoolPop(pool);

	return array;
}

- (OFArray *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
{
	return [self componentsSeparatedByCharactersInSet: characterSet
						  options: 0];
}

- (OFArray *)
   componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
				options: (int)options
{
	OFMutableArray *array = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();
	bool skipEmpty = (options & OF_STRING_SKIP_EMPTY);
	const of_unichar_t *characters = self.characters;
	size_t length = self.length;
	bool (*characterIsMember)(id, SEL, of_unichar_t) =
	    (bool (*)(id, SEL, of_unichar_t))[characterSet
	    methodForSelector: @selector(characterIsMember:)];
	size_t last;

	last = 0;
	for (size_t i = 0; i < length; i++) {
		if (characterIsMember(characterSet,
		    @selector(characterIsMember:), characters[i])) {
			if (!skipEmpty || i != last) {
				OFString *component = [self substringWithRange:
				    of_range(last, i - last)];
				[array addObject: component];
			}

			last = i + 1;
		}
	}
	if (!skipEmpty || length != last) {
		OFString *component = [self substringWithRange:
		    of_range(last, length - last)];
		[array addObject: component];
	}

	[array makeImmutable];

	objc_autoreleasePoolPop(pool);

	return array;
}

- (long long)longLongValue
{
	return [self longLongValueWithBase: 10];
}

- (long long)longLongValueWithBase: (int)base
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = self.UTF8String;
	char *endPointer = NULL;
	long long value;



	errno = 0;




	value = strtoll(UTF8String, &endPointer, base);

	if ((value == LLONG_MIN || value == LLONG_MAX) && errno == ERANGE)






		@throw [OFOutOfRangeException exception];





	/* Check if there are any invalid chars left */



	if (endPointer != NULL)


		for (; *endPointer != '\0'; endPointer++)


			/* Use isspace since strtoll uses the same. */









			if (!isspace((unsigned char)*endPointer))


				@throw [OFInvalidFormatException exception];













	objc_autoreleasePoolPop(pool);

	return value;
}

- (unsigned long long)unsignedLongLongValue
{
	return [self unsignedLongLongValueWithBase: 10];
}

- (unsigned long long)unsignedLongLongValueWithBase: (int)base
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = self.UTF8String;
	char *endPointer = NULL;
	unsigned long long value;



	/* Use isspace since strtoull uses the same. */
	while (isspace((unsigned char)*UTF8String))



		UTF8String++;


	if (*UTF8String == '-')





		@throw [OFInvalidFormatException exception];




	errno = 0;
	value = strtoull(UTF8String, &endPointer, base);









	if (value == ULLONG_MAX && errno == ERANGE)








		@throw [OFOutOfRangeException exception];



	/* Check if there are any invalid chars left */
	if (endPointer != NULL)
		for (; *endPointer != '\0'; endPointer++)
			/* Use isspace since strtoull uses the same. */
			if (!isspace((unsigned char)*endPointer))
				@throw [OFInvalidFormatException exception];








	objc_autoreleasePoolPop(pool);

	return value;
}

- (float)floatValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stripped = self.stringByDeletingEnclosingWhitespaces;

	if ([stripped caseInsensitiveCompare: @"INF"] == OF_ORDERED_SAME ||
	    [stripped caseInsensitiveCompare: @"INFINITY"] == OF_ORDERED_SAME)
		return INFINITY;
	if ([stripped caseInsensitiveCompare: @"-INF"] == OF_ORDERED_SAME ||
	    [stripped caseInsensitiveCompare: @"-INFINITY"] == OF_ORDERED_SAME)
		return -INFINITY;





#ifdef HAVE_STRTOF_L
	const char *UTF8String = self.UTF8String;
#else
	/*
	 * If we have no strtof_l, we have no other choice but to replace "."
	 * with the locale's decimal point.
	 */
	OFString *decimalPoint = [OFLocale decimalPoint];
	const char *UTF8String = [self
	    stringByReplacingOccurrencesOfString: @"."
				      withString: decimalPoint].UTF8String;
#endif
	char *endPointer = NULL;
	float value;

	errno = 0;
#ifdef HAVE_STRTOF_L
	value = strtof_l(UTF8String, &endPointer, cLocale);
#else
	value = strtof(UTF8String, &endPointer);
#endif

	if (value == HUGE_VALF && errno == ERANGE)
		@throw [OFOutOfRangeException exception];

	/* Check if there are any invalid chars left */
	if (endPointer != NULL)
		for (; *endPointer != '\0'; endPointer++)
			/* Use isspace since strtof uses the same. */
			if (!isspace((unsigned char)*endPointer))
				@throw [OFInvalidFormatException exception];

	objc_autoreleasePoolPop(pool);

	return value;
}

- (double)doubleValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stripped = self.stringByDeletingEnclosingWhitespaces;

	if ([stripped caseInsensitiveCompare: @"INF"] == OF_ORDERED_SAME ||
	    [stripped caseInsensitiveCompare: @"INFINITY"] == OF_ORDERED_SAME)
		return INFINITY;
	if ([stripped caseInsensitiveCompare: @"-INF"] == OF_ORDERED_SAME ||
	    [stripped caseInsensitiveCompare: @"-INFINITY"] == OF_ORDERED_SAME)
		return -INFINITY;





#ifdef HAVE_STRTOD_L
	const char *UTF8String = self.UTF8String;
#else
	/*
	 * If we have no strtod_l, we have no other choice but to replace "."
	 * with the locale's decimal point.
	 */
	OFString *decimalPoint = [OFLocale decimalPoint];
	const char *UTF8String = [self
	    stringByReplacingOccurrencesOfString: @"."
				      withString: decimalPoint].UTF8String;
#endif
	char *endPointer = NULL;
	double value;

	errno = 0;
#ifdef HAVE_STRTOD_L
	value = strtod_l(UTF8String, &endPointer, cLocale);
#else
	value = strtod(UTF8String, &endPointer);
#endif

	if (value == HUGE_VAL && errno == ERANGE)
		@throw [OFOutOfRangeException exception];

	/* Check if there are any invalid chars left */
	if (endPointer != NULL)
		for (; *endPointer != '\0'; endPointer++)
			/* Use isspace since strtod uses the same. */
			if (!isspace((unsigned char)*endPointer))
				@throw [OFInvalidFormatException exception];

	objc_autoreleasePoolPop(pool);

	return value;
}

- (const of_unichar_t *)characters
{
	OFObject *object = [[[OFObject alloc] init] autorelease];
	size_t length = self.length;
	of_unichar_t *ret;





	ret = [object allocMemoryWithSize: sizeof(of_unichar_t)

				    count: length];
	[self getCharacters: ret
		    inRange: of_range(0, length)];





	return ret;
}

- (const of_char16_t *)UTF16String
{
	return [self UTF16StringWithByteOrder: OF_BYTE_ORDER_NATIVE];
}

- (const of_char16_t *)UTF16StringWithByteOrder: (of_byte_order_t)byteOrder
{
	OFObject *object = [[[OFObject alloc] init] autorelease];
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters = self.characters;
	size_t length = self.length;
	of_char16_t *ret;
	size_t j;
	bool swap = (byteOrder != OF_BYTE_ORDER_NATIVE);


	/* Allocate memory for the worst case */
	ret = [object allocMemoryWithSize: sizeof(of_char16_t)
				    count: (length + 1) * 2];

	j = 0;
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = characters[i];

		if (c > 0x10FFFF)

			@throw [OFInvalidEncodingException exception];


		if (swap) {
			if (c > 0xFFFF) {
				c -= 0x10000;
				ret[j++] = OF_BSWAP16(0xD800 | (c >> 10));

				ret[j++] = OF_BSWAP16(0xDC00 | (c & 0x3FF));
			} else
				ret[j++] = OF_BSWAP16(c);
		} else {
			if (c > 0xFFFF) {
				c -= 0x10000;
				ret[j++] = 0xD800 | (c >> 10);
				ret[j++] = 0xDC00 | (c & 0x3FF);
			} else
				ret[j++] = c;
		}
	}
	ret[j] = 0;

	@try {
		ret = [object resizeMemory: ret
				      size: sizeof(of_char16_t)
				     count: j + 1];
	} @catch (OFOutOfMemoryException *e) {
		/* We don't care, as we only tried to make it smaller */
	}

	objc_autoreleasePoolPop(pool);











	return ret;
}

- (size_t)UTF16StringLength
{
	const of_unichar_t *characters = self.characters;
	size_t length, UTF16StringLength;

	length = UTF16StringLength = self.length;

	for (size_t i = 0; i < length; i++)
		if (characters[i] > 0xFFFF)
			UTF16StringLength++;

	return UTF16StringLength;
}

- (const of_char32_t *)UTF32String
{
	return [self UTF32StringWithByteOrder: OF_BYTE_ORDER_NATIVE];
}

- (const of_char32_t *)UTF32StringWithByteOrder: (of_byte_order_t)byteOrder
{
	OFObject *object = [[[OFObject alloc] init] autorelease];
	size_t length = self.length;
	of_char32_t *ret;


	ret = [object allocMemoryWithSize: sizeof(of_char32_t)
				    count: length + 1];

	[self getCharacters: ret
		    inRange: of_range(0, length)];
	ret[length] = 0;

	if (byteOrder != OF_BYTE_ORDER_NATIVE)
		for (size_t i = 0; i < length; i++)

			ret[i] = OF_BSWAP32(ret[i]);









	return ret;
}

- (OFData *)dataWithEncoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data =
	    [OFData dataWithItems: [self cStringWithEncoding: encoding]
			    count: [self cStringLengthWithEncoding: encoding]];

	[data retain];

	objc_autoreleasePoolPop(pool);

	return [data autorelease];
}

#ifdef OF_HAVE_UNICODE_TABLES
- (OFString *)decomposedStringWithCanonicalMapping
{
	return decomposedString(self, of_unicode_decomposition_table,
	    OF_UNICODE_DECOMPOSITION_TABLE_SIZE);
}

- (OFString *)decomposedStringWithCompatibilityMapping
{
	return decomposedString(self, of_unicode_decomposition_compat_table,
	    OF_UNICODE_DECOMPOSITION_COMPAT_TABLE_SIZE);
}
#endif

#ifdef OF_WINDOWS
- (OFString *)stringByExpandingWindowsEnvironmentStrings
{
	if ([OFSystemInfo isWindowsNT]) {
		wchar_t buffer[512];
		size_t length;

		if ((length = ExpandEnvironmentStringsW(self.UTF16String,
		    buffer, sizeof(buffer))) == 0)
			return self;

		return [OFString stringWithUTF16String: buffer
						length: length - 1];
	} else {
		of_string_encoding_t encoding = [OFLocale encoding];
		char buffer[512];
		size_t length;

		if ((length = ExpandEnvironmentStringsA(
		    [self cStringWithEncoding: encoding], buffer,
		    sizeof(buffer))) == 0)
			return self;

		return [OFString stringWithCString: buffer
					  encoding: encoding
					    length: length - 1];
	}
}
#endif

#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path
{
	[self writeToFile: path
		 encoding: OF_STRING_ENCODING_UTF_8];
}

- (void)writeToFile: (OFString *)path
	   encoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file;

	file = [OFFile fileWithPath: path
			       mode: @"w"];
	[file writeString: self
		 encoding: encoding];

	objc_autoreleasePoolPop(pool);
}
#endif

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

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

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

	stream = [URLHandler openItemAtURL: URL
				      mode: @"w"];
	[stream writeString: self
		   encoding: encoding];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (of_string_line_enumeration_block_t)block
{
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters = self.characters;
	size_t i, last = 0, length = self.length;
	bool stop = false, lastCarriageReturn = false;

	for (i = 0; i < length && !stop; i++) {
		if (lastCarriageReturn && characters[i] == '\n') {
			lastCarriageReturn = false;
			last++;

			continue;
		}

		if (characters[i] == '\n' || characters[i] == '\r') {
			void *pool2 = objc_autoreleasePoolPush();

			block([self substringWithRange:
			    of_range(last, i - last)], &stop);
			last = i + 1;

			objc_autoreleasePoolPop(pool2);
		}

		lastCarriageReturn = (characters[i] == '\r');
	}

	if (!stop)
		block([self substringWithRange: of_range(last, i - last)],
		    &stop);

	objc_autoreleasePoolPop(pool);
}
#endif
@end







|
<








<
<
|
|
<
<

<






<

<

<







<
|
<
<

<






|


<




<

<






<

<

<






<

<

<






<

<

<






<

<

<






<

<

<






<

<

<





|






<
|



|
<


|



|







|
|








<
|




|




|



|







|
<



|


|
|
|





>
>
>
>
>
>
>

















|


|
>






|



















|



|
|

|
|









|








|



















|
|

>
>
|
>
>
>
>
|
|
|
>
>
>
>
>
>
|

>
>
>
>
|
>
>
>
|
>
>
|
>
>
|
>
>
>
>
>
>
>
>
>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>















<
|

>
>
|
|
>
>
>

|
>
|
>
>
>
>
>
|

>
>
>
|
|
>
>
>
>
>
>

>
>
|
>
>
>
>
>
>
>
>
|

>
>
|
|
<
<
|
|
>
>
>
>
>
>
>











|
|

|
|

>
>
>
>













|




|

|






|
|

|












|
|

|
|

>
>
>
>













|




|

|






|
|

|







|

<

|
>

>
>
>
|
>
|
|
|
>
>
>
>




|

|


|

<

|

|

|
>


<
|



|

|
>

>




|
>
|

|



|
|

|


|


|
<
<





>
>
>
>
>
>
>
>
>
>






|











|

|


|

<

|
>

<
|
>
|
<
|

|
|
>
|
>
>
>
>
>
>
>
|
>



|
















|
|




|
|

















|


















|
<


|
<


<
<
|
<
|
<
<






|
<


|
<








|
<
|
<





|


|















|









|






2017
2018
2019
2020
2021
2022
2023
2024

2025
2026
2027
2028
2029
2030
2031
2032


2033
2034


2035

2036
2037
2038
2039
2040
2041

2042

2043

2044
2045
2046
2047
2048
2049
2050

2051


2052

2053
2054
2055
2056
2057
2058
2059
2060
2061

2062
2063
2064
2065

2066

2067
2068
2069
2070
2071
2072

2073

2074

2075
2076
2077
2078
2079
2080

2081

2082

2083
2084
2085
2086
2087
2088

2089

2090

2091
2092
2093
2094
2095
2096

2097

2098

2099
2100
2101
2102
2103
2104

2105

2106

2107
2108
2109
2110
2111
2112

2113

2114

2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126

2127
2128
2129
2130
2131

2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155

2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178

2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370

2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419


2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542

2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569

2570
2571
2572
2573
2574
2575
2576
2577
2578

2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610


2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650

2651
2652
2653
2654

2655
2656
2657

2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737

2738
2739
2740

2741
2742


2743

2744


2745
2746
2747
2748
2749
2750
2751

2752
2753
2754

2755
2756
2757
2758
2759
2760
2761
2762
2763

2764

2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805

- (OFString *)stringByAppendingFormat: (OFConstantString *)format, ...
{
	OFString *ret;
	va_list arguments;

	va_start(arguments, format);
	ret = [self stringByAppendingFormat: format arguments: arguments];

	va_end(arguments);

	return ret;
}

- (OFString *)stringByAppendingFormat: (OFConstantString *)format
			    arguments: (va_list)arguments
{


	OFMutableString *new = [OFMutableString stringWithString: self];
	[new appendFormat: format arguments: arguments];


	[new makeImmutable];

	return new;
}

- (OFString *)stringByPrependingString: (OFString *)string
{
	OFMutableString *new = [[string mutableCopy] autorelease];

	[new appendString: self];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new replaceOccurrencesOfString: string withString: replacement];


	[new makeImmutable];

	return new;
}

- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
					withString: (OFString *)replacement
					   options: (int)options
					     range: (OFRange)range
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new replaceOccurrencesOfString: string
			     withString: replacement
				options: options
				  range: range];

	[new makeImmutable];

	return new;
}

- (OFString *)uppercaseString
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new uppercase];

	[new makeImmutable];

	return new;
}

- (OFString *)lowercaseString
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new lowercase];

	[new makeImmutable];

	return new;
}

- (OFString *)capitalizedString
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new capitalize];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByDeletingLeadingWhitespaces
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new deleteLeadingWhitespaces];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByDeletingTrailingWhitespaces
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new deleteTrailingWhitespaces];

	[new makeImmutable];

	return new;
}

- (OFString *)stringByDeletingEnclosingWhitespaces
{
	OFMutableString *new = [[self mutableCopy] autorelease];

	[new deleteEnclosingWhitespaces];

	[new makeImmutable];

	return new;
}

- (bool)hasPrefix: (OFString *)prefix
{
	OFUnichar *tmp;
	size_t prefixLength;
	bool hasPrefix;

	if ((prefixLength = prefix.length) > self.length)
		return false;


	tmp = OFAllocMemory(prefixLength, sizeof(OFUnichar));
	@try {
		void *pool = objc_autoreleasePoolPush();

		[self getCharacters: tmp inRange: OFRangeMake(0, prefixLength)];


		hasPrefix = (memcmp(tmp, prefix.characters,
		    prefixLength * sizeof(OFUnichar)) == 0);

		objc_autoreleasePoolPop(pool);
	} @finally {
		OFFreeMemory(tmp);
	}

	return hasPrefix;
}

- (bool)hasSuffix: (OFString *)suffix
{
	OFUnichar *tmp;
	const OFUnichar *suffixCharacters;
	size_t length, suffixLength;
	bool hasSuffix;

	if ((suffixLength = suffix.length) > self.length)
		return false;

	length = self.length;


	tmp = OFAllocMemory(suffixLength, sizeof(OFUnichar));
	@try {
		void *pool = objc_autoreleasePoolPush();

		[self getCharacters: tmp
			    inRange: OFRangeMake(length - suffixLength,
					 suffixLength)];

		suffixCharacters = suffix.characters;
		hasSuffix = (memcmp(tmp, suffixCharacters,
		    suffixLength * sizeof(OFUnichar)) == 0);

		objc_autoreleasePoolPop(pool);
	} @finally {
		OFFreeMemory(tmp);
	}

	return hasSuffix;
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
{
	return [self componentsSeparatedByString: delimiter options: 0];

}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
				 options: (OFStringSeparationOptions)options
{
	void *pool;
	OFMutableArray *array;
	const OFUnichar *characters, *delimiterCharacters;
	bool skipEmpty = (options & OFStringSkipEmptyComponents);
	size_t length = self.length;
	size_t delimiterLength = delimiter.length;
	size_t last;
	OFString *component;

	if (delimiter == nil)
		@throw [OFInvalidArgumentException exception];

	if (delimiter.length == 0)
		return [OFArray arrayWithObject: self];

	array = [OFMutableArray array];
	pool = objc_autoreleasePoolPush();

	characters = self.characters;
	delimiterCharacters = delimiter.characters;

	if (delimiterLength > length) {
		[array addObject: [[self copy] autorelease]];
		[array makeImmutable];

		objc_autoreleasePoolPop(pool);

		return array;
	}

	last = 0;
	for (size_t i = 0; i <= length - delimiterLength; i++) {
		if (memcmp(characters + i, delimiterCharacters,
		    delimiterLength * sizeof(OFUnichar)) != 0)
			continue;

		component = [self substringWithRange:
		    OFRangeMake(last, i - last)];
		if (!skipEmpty || component.length > 0)
			[array addObject: component];

		i += delimiterLength - 1;
		last = i + 1;
	}
	component = [self substringWithRange: OFRangeMake(last, length - last)];
	if (!skipEmpty || component.length > 0)
		[array addObject: component];

	[array makeImmutable];

	objc_autoreleasePoolPop(pool);

	return array;
}

- (OFArray *)
    componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
{
	return [self componentsSeparatedByCharactersInSet: characterSet
						  options: 0];
}

- (OFArray *)
   componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
				options: (OFStringSeparationOptions)options
{
	OFMutableArray *array = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();
	bool skipEmpty = (options & OFStringSkipEmptyComponents);
	const OFUnichar *characters = self.characters;
	size_t length = self.length;
	bool (*characterIsMember)(id, SEL, OFUnichar) =
	    (bool (*)(id, SEL, OFUnichar))[characterSet
	    methodForSelector: @selector(characterIsMember:)];
	size_t last;

	last = 0;
	for (size_t i = 0; i < length; i++) {
		if (characterIsMember(characterSet,
		    @selector(characterIsMember:), characters[i])) {
			if (!skipEmpty || i != last) {
				OFString *component = [self substringWithRange:
				    OFRangeMake(last, i - last)];
				[array addObject: component];
			}

			last = i + 1;
		}
	}
	if (!skipEmpty || length != last) {
		OFString *component = [self substringWithRange:
		    OFRangeMake(last, length - last)];
		[array addObject: component];
	}

	[array makeImmutable];

	objc_autoreleasePoolPop(pool);

	return array;
}

- (long long)longLongValue
{
	return [self longLongValueWithBase: 10];
}

- (long long)longLongValueWithBase: (int)base
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = self.UTF8String;
	bool negative = false;
	long long value = 0;

	while (OFASCIIIsSpace(*UTF8String))
		UTF8String++;

	switch (*UTF8String) {
	case '-':
		negative = true;
	case '+':
		UTF8String++;
	}

	if (UTF8String[0] == '0') {
		if (UTF8String[1] == 'x') {
			if (base == 0)
				base = 16;

			if (base != 16 || UTF8String[2] == '\0')
				@throw [OFInvalidFormatException exception];

			UTF8String += 2;
		} else {
			if (base == 0)
				base = 8;

			UTF8String++;
		}
	}

	if (base == 0)
		base = 10;

	while (*UTF8String != '\0') {
		unsigned char c = OFASCIIToUpper(*UTF8String++);

		if (c >= '0' && c <= '9')
			c -= '0';
		else if (c >= 'A' && c <= 'Z')
			c -= ('A' - 10);
		else if (OFASCIIIsSpace(c)) {
			while (*UTF8String != '\0')
				if (!OFASCIIIsSpace(*UTF8String++))
					@throw [OFInvalidFormatException
					    exception];

			break;
		} else
			@throw [OFInvalidFormatException exception];

		if (c >= base)
			@throw [OFInvalidFormatException exception];

		if (LLONG_MAX / base < value || LLONG_MAX - (value * base) < c)
			@throw [OFOutOfRangeException exception];

		value = (value * base) + c;
	}

	if (negative)
		value *= -1;

	objc_autoreleasePoolPop(pool);

	return value;
}

- (unsigned long long)unsignedLongLongValue
{
	return [self unsignedLongLongValueWithBase: 10];
}

- (unsigned long long)unsignedLongLongValueWithBase: (int)base
{
	void *pool = objc_autoreleasePoolPush();
	const char *UTF8String = self.UTF8String;

	unsigned long long value = 0;

	while (OFASCIIIsSpace(*UTF8String))
		UTF8String++;

	switch (*UTF8String) {
	case '-':
		@throw [OFInvalidFormatException exception];
	case '+':
		UTF8String++;
	}

	if (UTF8String[0] == '0') {
		if (UTF8String[1] == 'x') {
			if (base == 0)
				base = 16;

			if (base != 16 || UTF8String[2] == '\0')
				@throw [OFInvalidFormatException exception];

			UTF8String += 2;
		} else {
			if (base == 0)
				base = 8;

			UTF8String++;
		}
	}

	if (base == 0)
		base = 10;

	while (*UTF8String != '\0') {
		unsigned char c = OFASCIIToUpper(*UTF8String++);

		if (c >= '0' && c <= '9')
			c -= '0';
		else if (c >= 'A' && c <= 'Z')
			c -= ('A' - 10);
		else if (OFASCIIIsSpace(c)) {
			while (*UTF8String != '\0')
				if (!OFASCIIIsSpace(*UTF8String++))
					@throw [OFInvalidFormatException
					    exception];

			break;
		} else
			@throw [OFInvalidFormatException exception];



		if (c >= base)
			@throw [OFInvalidFormatException exception];

		if (ULLONG_MAX / base < value ||
		    ULLONG_MAX - (value * base) < c)
			@throw [OFOutOfRangeException exception];

		value = (value * base) + c;
	}

	objc_autoreleasePoolPop(pool);

	return value;
}

- (float)floatValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stripped = self.stringByDeletingEnclosingWhitespaces;

	if ([stripped caseInsensitiveCompare: @"INF"] == OFOrderedSame ||
	    [stripped caseInsensitiveCompare: @"INFINITY"] == OFOrderedSame)
		return INFINITY;
	if ([stripped caseInsensitiveCompare: @"-INF"] == OFOrderedSame ||
	    [stripped caseInsensitiveCompare: @"-INFINITY"] == OFOrderedSame)
		return -INFINITY;
	if ([stripped caseInsensitiveCompare: @"NAN"] == OFOrderedSame)
		return NAN;
	if ([stripped caseInsensitiveCompare: @"-NAN"] == OFOrderedSame)
		return -NAN;

#ifdef HAVE_STRTOF_L
	const char *UTF8String = self.UTF8String;
#else
	/*
	 * If we have no strtof_l, we have no other choice but to replace "."
	 * with the locale's decimal point.
	 */
	OFString *decimalPoint = [OFLocale decimalPoint];
	const char *UTF8String = [self
	    stringByReplacingOccurrencesOfString: @"."
				      withString: decimalPoint].UTF8String;
#endif
	char *endPtr = NULL;
	float value;

	errno = 0;
#ifdef HAVE_STRTOF_L
	value = strtof_l(UTF8String, &endPtr, cLocale);
#else
	value = strtof(UTF8String, &endPtr);
#endif

	if (value == HUGE_VALF && errno == ERANGE)
		@throw [OFOutOfRangeException exception];

	/* Check if there are any invalid chars left */
	if (endPtr != NULL)
		for (; *endPtr != '\0'; endPtr++)
			/* Use isspace since strtof uses the same. */
			if (!isspace((unsigned char)*endPtr))
				@throw [OFInvalidFormatException exception];

	objc_autoreleasePoolPop(pool);

	return value;
}

- (double)doubleValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stripped = self.stringByDeletingEnclosingWhitespaces;

	if ([stripped caseInsensitiveCompare: @"INF"] == OFOrderedSame ||
	    [stripped caseInsensitiveCompare: @"INFINITY"] == OFOrderedSame)
		return INFINITY;
	if ([stripped caseInsensitiveCompare: @"-INF"] == OFOrderedSame ||
	    [stripped caseInsensitiveCompare: @"-INFINITY"] == OFOrderedSame)
		return -INFINITY;
	if ([stripped caseInsensitiveCompare: @"NAN"] == OFOrderedSame)
		return NAN;
	if ([stripped caseInsensitiveCompare: @"-NAN"] == OFOrderedSame)
		return -NAN;

#ifdef HAVE_STRTOD_L
	const char *UTF8String = self.UTF8String;
#else
	/*
	 * If we have no strtod_l, we have no other choice but to replace "."
	 * with the locale's decimal point.
	 */
	OFString *decimalPoint = [OFLocale decimalPoint];
	const char *UTF8String = [self
	    stringByReplacingOccurrencesOfString: @"."
				      withString: decimalPoint].UTF8String;
#endif
	char *endPtr = NULL;
	double value;

	errno = 0;
#ifdef HAVE_STRTOD_L
	value = strtod_l(UTF8String, &endPtr, cLocale);
#else
	value = strtod(UTF8String, &endPtr);
#endif

	if (value == HUGE_VAL && errno == ERANGE)
		@throw [OFOutOfRangeException exception];

	/* Check if there are any invalid chars left */
	if (endPtr != NULL)
		for (; *endPtr != '\0'; endPtr++)
			/* Use isspace since strtod uses the same. */
			if (!isspace((unsigned char)*endPtr))
				@throw [OFInvalidFormatException exception];

	objc_autoreleasePoolPop(pool);

	return value;
}

- (const OFUnichar *)characters
{

	size_t length = self.length;
	OFUnichar *buffer;
	const OFUnichar *ret;

	buffer = OFAllocMemory(length, sizeof(OFUnichar));
	@try {
		[self getCharacters: buffer inRange: OFRangeMake(0, length)];

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

	return ret;
}

- (const OFChar16 *)UTF16String
{
	return [self UTF16StringWithByteOrder: OFByteOrderNative];
}

- (const OFChar16 *)UTF16StringWithByteOrder: (OFByteOrder)byteOrder
{

	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters = self.characters;
	size_t length = self.length;
	OFChar16 *buffer;
	size_t j;
	bool swap = (byteOrder != OFByteOrderNative);
	const OFChar16 *ret;

	/* Allocate memory for the worst case */

	buffer = OFAllocMemory((length + 1) * 2, sizeof(OFChar16));

	j = 0;
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = characters[i];

		if (c > 0x10FFFF) {
			OFFreeMemory(buffer);
			@throw [OFInvalidEncodingException exception];
		}

		if (swap) {
			if (c > 0xFFFF) {
				c -= 0x10000;
				buffer[j++] = OFByteSwap16(0xD800 | (c >> 10));
				buffer[j++] =
				    OFByteSwap16(0xDC00 | (c & 0x3FF));
			} else
				buffer[j++] = OFByteSwap16(c);
		} else {
			if (c > 0xFFFF) {
				c -= 0x10000;
				buffer[j++] = 0xD800 | (c >> 10);
				buffer[j++] = 0xDC00 | (c & 0x3FF);
			} else
				buffer[j++] = c;
		}
	}
	buffer[j] = 0;

	@try {
		buffer = OFResizeMemory(buffer, j + 1, sizeof(OFChar16));


	} @catch (OFOutOfMemoryException *e) {
		/* We don't care, as we only tried to make it smaller */
	}

	objc_autoreleasePoolPop(pool);

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

	return ret;
}

- (size_t)UTF16StringLength
{
	const OFUnichar *characters = self.characters;
	size_t length, UTF16StringLength;

	length = UTF16StringLength = self.length;

	for (size_t i = 0; i < length; i++)
		if (characters[i] > 0xFFFF)
			UTF16StringLength++;

	return UTF16StringLength;
}

- (const OFChar32 *)UTF32String
{
	return [self UTF32StringWithByteOrder: OFByteOrderNative];
}

- (const OFChar32 *)UTF32StringWithByteOrder: (OFByteOrder)byteOrder
{

	size_t length = self.length;
	OFChar32 *buffer;
	const OFChar32 *ret;


	buffer = OFAllocMemory(length + 1, sizeof(OFChar32));
	@try {
		[self getCharacters: buffer inRange: OFRangeMake(0, length)];

		buffer[length] = 0;

		if (byteOrder != OFByteOrderNative)
			for (size_t i = 0; i < length; i++)
				buffer[i] = OFByteSwap32(buffer[i]);

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

	return ret;
}

- (OFData *)dataWithEncoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data =
	    [OFData dataWithItems: [self cStringWithEncoding: encoding]
			    count: [self cStringLengthWithEncoding: encoding]];

	[data retain];

	objc_autoreleasePoolPop(pool);

	return [data autorelease];
}

#ifdef OF_HAVE_UNICODE_TABLES
- (OFString *)decomposedStringWithCanonicalMapping
{
	return decomposedString(self, OFUnicodeDecompositionTable,
	    OFUnicodeDecompositionTableSize);
}

- (OFString *)decomposedStringWithCompatibilityMapping
{
	return decomposedString(self, OFUnicodeDecompositionCompatTable,
	    OFUnicodeDecompositionCompatTableSize);
}
#endif

#ifdef OF_WINDOWS
- (OFString *)stringByExpandingWindowsEnvironmentStrings
{
	if ([OFSystemInfo isWindowsNT]) {
		wchar_t buffer[512];
		size_t length;

		if ((length = ExpandEnvironmentStringsW(self.UTF16String,
		    buffer, sizeof(buffer))) == 0)
			return self;

		return [OFString stringWithUTF16String: buffer
						length: length - 1];
	} else {
		OFStringEncoding encoding = [OFLocale encoding];
		char buffer[512];
		size_t length;

		if ((length = ExpandEnvironmentStringsA(
		    [self cStringWithEncoding: encoding], buffer,
		    sizeof(buffer))) == 0)
			return self;

		return [OFString stringWithCString: buffer
					  encoding: encoding
					    length: length - 1];
	}
}
#endif

#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path
{
	[self writeToFile: path encoding: OFStringEncodingUTF8];

}

- (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding

{
	void *pool = objc_autoreleasePoolPush();


	OFFile *file = [OFFile fileWithPath: path mode: @"w"];

	[file writeString: self encoding: encoding];


	objc_autoreleasePoolPop(pool);
}
#endif

- (void)writeToURL: (OFURL *)URL
{
	[self writeToURL: URL encoding: OFStringEncodingUTF8];

}

- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding

{
	void *pool = objc_autoreleasePoolPush();
	OFURLHandler *URLHandler;
	OFStream *stream;

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

	stream = [URLHandler openItemAtURL: URL mode: @"w"];

	[stream writeString: self encoding: encoding];


	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (OFStringLineEnumerationBlock)block
{
	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters = self.characters;
	size_t i, last = 0, length = self.length;
	bool stop = false, lastCarriageReturn = false;

	for (i = 0; i < length && !stop; i++) {
		if (lastCarriageReturn && characters[i] == '\n') {
			lastCarriageReturn = false;
			last++;

			continue;
		}

		if (characters[i] == '\n' || characters[i] == '\r') {
			void *pool2 = objc_autoreleasePoolPush();

			block([self substringWithRange:
			    OFRangeMake(last, i - last)], &stop);
			last = i + 1;

			objc_autoreleasePoolPop(pool2);
		}

		lastCarriageReturn = (characters[i] == '\r');
	}

	if (!stop)
		block([self substringWithRange: OFRangeMake(last, i - last)],
		    &stop);

	objc_autoreleasePoolPop(pool);
}
#endif
@end

Modified src/OFSubarray.h from [cb560d9395] to [06376fcb2e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFArray.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFSubarray: OFArray
{
	OFArray *_array;
	of_range_t _range;
}

+ (instancetype)arrayWithArray: (OFArray *)array
			 range: (of_range_t)range;
- (instancetype)initWithArray: (OFArray *)array
			range: (of_range_t)range;
@end

OF_ASSUME_NONNULL_END

<
<
|




















|


|
<
|
<



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


 * 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 "OFArray.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFSubarray: OFArray
{
	OFArray *_array;
	OFRange _range;
}

+ (instancetype)arrayWithArray: (OFArray *)array range: (OFRange)range;

- (instancetype)initWithArray: (OFArray *)array range: (OFRange)range;

@end

OF_ASSUME_NONNULL_END

Modified src/OFSubarray.m from [32cdeea2aa] to [0aad1eac45].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFSubarray.h"

#import "OFOutOfRangeException.h"

@implementation OFSubarray
+ (instancetype)arrayWithArray: (OFArray *)array
			 range: (of_range_t)range
{
	return [[[self alloc] initWithArray: array
				      range: range] autorelease];
}

- (instancetype)initWithArray: (OFArray *)array
			range: (of_range_t)range
{
	self = [super init];

	@try {
		/* Should usually be retain, as it's useless with a copy */
		_array = [array copy];
		_range = range;

<
<
|




















|
<

|
<


|
<







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


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

#import "OFSubarray.h"

#import "OFOutOfRangeException.h"

@implementation OFSubarray
+ (instancetype)arrayWithArray: (OFArray *)array range: (OFRange)range

{
	return [[[self alloc] initWithArray: array range: range] autorelease];

}

- (instancetype)initWithArray: (OFArray *)array range: (OFRange)range

{
	self = [super init];

	@try {
		/* Should usually be retain, as it's useless with a copy */
		_array = [array copy];
		_range = range;
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
{
	if (idx >= _range.length)
		@throw [OFOutOfRangeException exception];

	return [_array objectAtIndex: idx + _range.location];
}

- (void)getObjects: (id *)buffer
	   inRange: (of_range_t)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _range.length)
		@throw [OFOutOfRangeException exception];

	range.location += _range.location;

	[_array getObjects: buffer
		   inRange: range];
}

- (size_t)indexOfObject: (id)object
{
	size_t idx = [_array indexOfObject: object];

	if (idx < _range.location)
		return OF_NOT_FOUND;

	idx -= _range.location;

	if (idx >= _range.length)
		return OF_NOT_FOUND;

	return idx;
}

- (size_t)indexOfObjectIdenticalTo: (id)object
{
	size_t idx = [_array indexOfObjectIdenticalTo: object];

	if (idx < _range.location)
		return OF_NOT_FOUND;

	idx -= _range.location;

	if (idx >= _range.length)
		return OF_NOT_FOUND;

	return idx;
}

- (OFArray *)objectsInRange: (of_range_t)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _range.length)
		@throw [OFOutOfRangeException exception];

	range.location += _range.location;

	return [_array objectsInRange: range];
}
@end







|
<







|
<







|




|









|




|




|










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
{
	if (idx >= _range.length)
		@throw [OFOutOfRangeException exception];

	return [_array objectAtIndex: idx + _range.location];
}

- (void)getObjects: (id *)buffer inRange: (OFRange)range

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

	range.location += _range.location;

	[_array getObjects: buffer inRange: range];

}

- (size_t)indexOfObject: (id)object
{
	size_t idx = [_array indexOfObject: object];

	if (idx < _range.location)
		return OFNotFound;

	idx -= _range.location;

	if (idx >= _range.length)
		return OFNotFound;

	return idx;
}

- (size_t)indexOfObjectIdenticalTo: (id)object
{
	size_t idx = [_array indexOfObjectIdenticalTo: object];

	if (idx < _range.location)
		return OFNotFound;

	idx -= _range.location;

	if (idx >= _range.length)
		return OFNotFound;

	return idx;
}

- (OFArray *)objectsInRange: (OFRange)range
{
	if (range.length > SIZE_MAX - range.location ||
	    range.location + range.length > _range.length)
		@throw [OFOutOfRangeException exception];

	range.location += _range.location;

	return [_array objectsInRange: range];
}
@end

Renamed and modified src/OFProcess.h [9ba5254960] to src/OFSubprocess.h [edb6555d8e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);

/**
 * @class OFProcess OFProcess.h ObjFW/OFProcess.h
 *
 * @brief A class for stream-like communication with a newly created process.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFProcess: OFStream
#ifndef OF_WINDOWS
    <OFReadyForReadingObserving, OFReadyForWritingObserving>
#endif
{
#ifndef OF_WINDOWS
	pid_t _pid;
	int _readPipe[2], _writePipe[2];
#else
	HANDLE _process, _readPipe[2], _writePipe[2];
#endif
	int _status;
	bool _atEndOfStream;
}

/**
 * @brief Creates a new OFProcess with the specified program and invokes the
 *	  program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @return A new, autoreleased OFProcess.
 */
+ (instancetype)processWithProgram: (OFString *)program;

/**
 * @brief Creates a new OFProcess with the specified program and arguments and
 *	  invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param arguments The arguments to pass to the program, or `nil`
 * @return A new, autoreleased OFProcess.
 */
+ (instancetype)
    processWithProgram: (OFString *)program
	     arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Creates a new OFProcess with the specified program, program name and
 *	  arguments and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param programName The program name for the program to invoke (argv[0]).
 *		      Usually, this is equal to program.
 * @param arguments The arguments to pass to the program, or `nil`
 * @return A new, autoreleased OFProcess.
 */
+ (instancetype)
    processWithProgram: (OFString *)program
	   programName: (OFString *)programName
	     arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Creates a new OFProcess with the specified program, program name,
 *	  arguments and environment and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param programName The program name for the program to invoke (argv[0]).
 *		      Usually, this is equal to program.
 * @param arguments The arguments to pass to the program, or `nil`
 * @param environment The environment to pass to the program, or `nil`. If it
 *		      is not `nil`, the passed dictionary will be used to
 *		      override the environment. If you want to add to the
 *		      existing environment, you need to get the existing
 *		      environment first, copy it, modify it and then pass it.
 * @return A new, autoreleased OFProcess.
 */
+ (instancetype)
    processWithProgram: (OFString *)program
	   programName: (OFString *)programName
	     arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments
	   environment: (nullable OFDictionary
			    OF_GENERIC(OFString *, OFString *) *)environment;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFProcess with the specified program
 *	  and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @return An initialized OFProcess.
 */
- (instancetype)initWithProgram: (OFString *)program;

/**
 * @brief Initializes an already allocated OFProcess with the specified program
 *	  and arguments and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param arguments The arguments to pass to the program, or `nil`
 * @return An initialized OFProcess.
 */
- (instancetype)
    initWithProgram: (OFString *)program
	  arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Initializes an already allocated OFProcess with the specified program,
 *	  program name and arguments and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param programName The program name for the program to invoke (argv[0]).
 *		      Usually, this is equal to program.
 * @param arguments The arguments to pass to the program, or `nil`
 * @return An initialized OFProcess.
 */
- (instancetype)
    initWithProgram: (OFString *)program
	programName: (OFString *)programName
	  arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Initializes an already allocated OFProcess with the specified program,
 *	  program name, arguments and environment and invokes the program.

 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param programName The program name for the program to invoke (argv[0]).
 *		      Usually, this is equal to program.
 * @param arguments The arguments to pass to the program, or `nil`
 * @param environment The environment to pass to the program, or `nil`. If it
 *		      is not `nil`, the passed dictionary will be used to
 *		      override the environment. If you want to add to the
 *		      existing environment, you need to get the existing
 *		      environment first, copy it, modify it and then pass it.
 * @return An initialized OFProcess.
 */
- (instancetype)
    initWithProgram: (OFString *)program
	programName: (OFString *)programName
	  arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments
	environment: (nullable OFDictionary
			 OF_GENERIC(OFString *, OFString *) *)environment
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Closes the write direction of the process.
 *
 * This method needs to be called for some programs before data can be read,
 * since some programs don't start processing before the write direction is
 * closed.
 */
- (void)closeForWriting;

/**
 * @brief Waits for the process to terminate and returns the exit status.
 *
 * If the process has already exited, this returns the exit status immediately.

 */
- (int)waitForTermination;
@end

OF_ASSUME_NONNULL_END







|

|


|








|






|




|

|


|
|




|


|
|


|
|






|


|
|
|


|












|


|
|
|
|
|




|
|



|




|
|




|






|
|






|







|
|
>











|










|








|

|
>





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

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);

/**
 * @class OFSubprocess OFSubprocess.h ObjFW/OFSubprocess.h
 *
 * @brief A class for stream-like communication with a newly created subprocess.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFSubprocess: OFStream
#ifndef OF_WINDOWS
    <OFReadyForReadingObserving, OFReadyForWritingObserving>
#endif
{
#ifndef OF_WINDOWS
	pid_t _pid;
	int _readPipe[2], _writePipe[2];
#else
	HANDLE _handle, _readPipe[2], _writePipe[2];
#endif
	int _status;
	bool _atEndOfStream;
}

/**
 * @brief Creates a new OFSubprocess with the specified program and invokes the
 *	  program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @return A new, autoreleased OFSubprocess.
 */
+ (instancetype)subprocessWithProgram: (OFString *)program;

/**
 * @brief Creates a new OFSubprocess with the specified program and arguments
 *	  and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param arguments The arguments to pass to the program, or `nil`
 * @return A new, autoreleased OFSubprocess.
 */
+ (instancetype)
    subprocessWithProgram: (OFString *)program
		arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Creates a new OFSubprocess with the specified program, program name
 *	  and arguments and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param programName The program name for the program to invoke (argv[0]).
 *		      Usually, this is equal to program.
 * @param arguments The arguments to pass to the program, or `nil`
 * @return A new, autoreleased OFSubprocess.
 */
+ (instancetype)
    subprocessWithProgram: (OFString *)program
	      programName: (OFString *)programName
		arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Creates a new OFSubprocess with the specified program, program name,
 *	  arguments and environment and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param programName The program name for the program to invoke (argv[0]).
 *		      Usually, this is equal to program.
 * @param arguments The arguments to pass to the program, or `nil`
 * @param environment The environment to pass to the program, or `nil`. If it
 *		      is not `nil`, the passed dictionary will be used to
 *		      override the environment. If you want to add to the
 *		      existing environment, you need to get the existing
 *		      environment first, copy it, modify it and then pass it.
 * @return A new, autoreleased OFSubprocess.
 */
+ (instancetype)
    subprocessWithProgram: (OFString *)program
	      programName: (OFString *)programName
		arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments
	      environment: (nullable OFDictionary
			       OF_GENERIC(OFString *, OFString *) *)environment;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFSubprocess with the specified
 *	  program and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @return An initialized OFSubprocess.
 */
- (instancetype)initWithProgram: (OFString *)program;

/**
 * @brief Initializes an already allocated OFSubprocess with the specified
 *	  program and arguments and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param arguments The arguments to pass to the program, or `nil`
 * @return An initialized OFSubprocess.
 */
- (instancetype)
    initWithProgram: (OFString *)program
	  arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Initializes an already allocated OFSubprocess with the specified
 *	  program, program name and arguments and invokes the program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param programName The program name for the program to invoke (argv[0]).
 *		      Usually, this is equal to program.
 * @param arguments The arguments to pass to the program, or `nil`
 * @return An initialized OFSubprocess.
 */
- (instancetype)
    initWithProgram: (OFString *)program
	programName: (OFString *)programName
	  arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Initializes an already allocated OFSubprocess with the specified
 *	  program, program name, arguments and environment and invokes the
 *	  program.
 *
 * @param program The program to execute. If it does not start with a slash, the
 *		  search path specified in PATH is used.
 * @param programName The program name for the program to invoke (argv[0]).
 *		      Usually, this is equal to program.
 * @param arguments The arguments to pass to the program, or `nil`
 * @param environment The environment to pass to the program, or `nil`. If it
 *		      is not `nil`, the passed dictionary will be used to
 *		      override the environment. If you want to add to the
 *		      existing environment, you need to get the existing
 *		      environment first, copy it, modify it and then pass it.
 * @return An initialized OFSubprocess.
 */
- (instancetype)
    initWithProgram: (OFString *)program
	programName: (OFString *)programName
	  arguments: (nullable OFArray OF_GENERIC(OFString *) *)arguments
	environment: (nullable OFDictionary
			 OF_GENERIC(OFString *, OFString *) *)environment
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Closes the write direction of the subprocess.
 *
 * This method needs to be called for some programs before data can be read,
 * since some programs don't start processing before the write direction is
 * closed.
 */
- (void)closeForWriting;

/**
 * @brief Waits for the subprocess to terminate and returns the exit status.
 *
 * If the subprocess has already exited, this returns the exit status
 * immediately.
 */
- (int)waitForTermination;
@end

OF_ASSUME_NONNULL_END

Renamed and modified src/OFProcess.m [07d8cb1dd6] to src/OFSubprocess.m [c37900d1dc].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

#ifdef OF_WINDOWS
# include "platform/windows/OFProcess.m"
#else
# include "platform/posix/OFProcess.m"
#endif

<
<
|


















|

|

1


2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*


 * 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 "platform.h"

#ifdef OF_WINDOWS
# include "platform/Windows/OFSubprocess.m"
#else
# include "platform/POSIX/OFSubprocess.m"
#endif

Modified src/OFSystemInfo.h from [61055b02a1] to [c96bc638d9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 */
OF_SUBCLASSING_RESTRICTED
@interface OFSystemInfo: OFObject
#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) size_t pageSize;
@property (class, readonly, nonatomic) size_t numberOfCPUs;
@property (class, readonly, nonatomic) OFString *ObjFWVersion;
@property (class, readonly, nonatomic) unsigned int ObjFWVersionMajor;
@property (class, readonly, nonatomic) unsigned int ObjFWVersionMinor;
@property (class, readonly, nullable, nonatomic) OFString *operatingSystemName;
@property (class, readonly, nullable, nonatomic)
    OFString *operatingSystemVersion;
# ifdef OF_HAVE_FILES
@property (class, readonly, nullable, nonatomic) OFString *userDataPath;
@property (class, readonly, nullable, nonatomic) OFString *userConfigPath;
# endif







|
|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 */
OF_SUBCLASSING_RESTRICTED
@interface OFSystemInfo: OFObject
#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) size_t pageSize;
@property (class, readonly, nonatomic) size_t numberOfCPUs;
@property (class, readonly, nonatomic) OFString *ObjFWVersion;
@property (class, readonly, nonatomic) unsigned short ObjFWVersionMajor;
@property (class, readonly, nonatomic) unsigned short ObjFWVersionMinor;
@property (class, readonly, nullable, nonatomic) OFString *operatingSystemName;
@property (class, readonly, nullable, nonatomic)
    OFString *operatingSystemVersion;
# ifdef OF_HAVE_FILES
@property (class, readonly, nullable, nonatomic) OFString *userDataPath;
@property (class, readonly, nullable, nonatomic) OFString *userConfigPath;
# endif
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
+ (OFString *)ObjFWVersion;

/**
 * @brief The major version of ObjFW.
 *
 * @return The major version of ObjFW
 */
+ (unsigned int)ObjFWVersionMajor;

/**
 * @brief The minor version of ObjFW.
 *
 * @return The minor version of ObjFW
 */
+ (unsigned int)ObjFWVersionMinor;

/**
 * @brief Returns the name of the operating system the application is running
 *	  on.
 *
 * @return The name of the operating system the application is running on
 */







|






|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
+ (OFString *)ObjFWVersion;

/**
 * @brief The major version of ObjFW.
 *
 * @return The major version of ObjFW
 */
+ (unsigned short)ObjFWVersionMajor;

/**
 * @brief The minor version of ObjFW.
 *
 * @return The minor version of ObjFW
 */
+ (unsigned short)ObjFWVersionMinor;

/**
 * @brief Returns the name of the operating system the application is running
 *	  on.
 *
 * @return The name of the operating system the application is running on
 */
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
 */
+ (nullable OFString *)operatingSystemVersion;

#ifdef OF_HAVE_FILES
/**
 * @brief Returns the path where user data for the application can be stored.
 *
 * On Unix systems, this adheres to the XDG Base Directory specification.@n
 * On Mac OS X and iOS, it uses the `NSApplicationSupportDirectory` directory.@n
 * On Windows, it uses the `APPDATA` environment variable.@n
 * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.

 *
 * @return The path where user data for the application can be stored
 */
+ (nullable OFString *)userDataPath;

/**
 * @brief Returns the path where user configuration for the application can be
 *	  stored.
 *
 * On Unix systems, this adheres to the XDG Base Directory specification.@n
 * On Mac OS X and iOS, it uses the `Preferences` directory inside of
 * `NSLibraryDirectory` directory.@n
 * On Windows, it uses the `APPDATA` environment variable.@n
 * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.

 *
 * @return The path where user configuration for the application can be stored
 */
+ (nullable OFString *)userConfigPath;


















#endif

/**
 * @brief Returns the vendor of the CPU.
 *
 * If the vendor could not be determined, `nil` is returned instead.
 *







|
|

|
>









|
|



>




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







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
 */
+ (nullable OFString *)operatingSystemVersion;

#ifdef OF_HAVE_FILES
/**
 * @brief Returns the path where user data for the application can be stored.
 *
 * On UNIX systems, this adheres to the XDG Base Directory specification.@n
 * On macOS and iOS, it uses the `NSApplicationSupportDirectory` directory.@n
 * On Windows, it uses the `APPDATA` environment variable.@n
 * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.@n
 * On AmigaOS and MorphOS, it returns `PROGDIR:`.
 *
 * @return The path where user data for the application can be stored
 */
+ (nullable OFString *)userDataPath;

/**
 * @brief Returns the path where user configuration for the application can be
 *	  stored.
 *
 * On UNIX systems, this adheres to the XDG Base Directory specification.@n
 * On macOS and iOS, it uses the `Preferences` directory inside of
 * `NSLibraryDirectory` directory.@n
 * On Windows, it uses the `APPDATA` environment variable.@n
 * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.
 * On AmigaOS and MorphOS, it returns `PROGDIR:`.
 *
 * @return The path where user configuration for the application can be stored
 */
+ (nullable OFString *)userConfigPath;

/**
 * @brief Returns a path where temporary files for can be stored.
 * 
 * If possible, returns a temporary directory for the user, otherwise returns a
 * global temporary directory.
 *
 * On UNIX systems, this adheres to the XDG Base Directory specification and
 * returns `/tmp` if `XDG_RUNTIME_DIR` is not set.@n
 * On macOS and iOS, this uses `_CS_DARWIN_USER_TEMP_DIR`, falling back to
 * `/tmp` if this fails.@n
 * On Windows, it uses `GetTempPath`.@n
 * On Haiku, it uses the `B_SYSTEM_TEMP_DIRECTORY` directory.
 * On AmigaOS and MorphOS, it returns `T:`.
 *
 * @return A path where temporary files can be stored
 */
+ (nullable OFString *)temporaryDirectoryPath;
#endif

/**
 * @brief Returns the vendor of the CPU.
 *
 * If the vendor could not be determined, `nil` is returned instead.
 *

Modified src/OFSystemInfo.m from [97e07274dd] to [2ea5196f61].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#include "unistd_wrapper.h"

#include "platform.h"

#ifdef HAVE_SYS_UTSNAME_H
# include <sys/utsname.h>
#endif
#if defined(OF_MACOS) || defined(OF_NETBSD)
# include <sys/sysctl.h>
#endif






#if defined(OF_AMIGAOS4)
# include <exec/exectags.h>
# include <proto/exec.h>
#elif defined(OF_MORPHOS)
# include <exec/system.h>
# include <proto/exec.h>
#endif

#import "OFSystemInfo.h"
#import "OFApplication.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFLocale.h"

#import "OFString.h"

#import "OFNotImplementedException.h"

#import "once.h"

#if defined(OF_MACOS) || defined(OF_IOS)
# ifdef HAVE_SYSDIR_H
#  include <sysdir.h>
# endif
#endif
#ifdef OF_WINDOWS
# include <windows.h>







|


>
>
>
>
>



<


<







>




<
<







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
#include "unistd_wrapper.h"

#include "platform.h"

#ifdef HAVE_SYS_UTSNAME_H
# include <sys/utsname.h>
#endif
#if defined(OF_MACOS) || defined(OF_IOS) || defined(OF_NETBSD)
# include <sys/sysctl.h>
#endif

#ifdef OF_AMIGAOS
# include <exec/execbase.h>
# include <proto/exec.h>
#endif

#if defined(OF_AMIGAOS4)
# include <exec/exectags.h>

#elif defined(OF_MORPHOS)
# include <exec/system.h>

#endif

#import "OFSystemInfo.h"
#import "OFApplication.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFLocale.h"
#import "OFOnce.h"
#import "OFString.h"

#import "OFNotImplementedException.h"



#if defined(OF_MACOS) || defined(OF_IOS)
# ifdef HAVE_SYSDIR_H
#  include <sysdir.h>
# endif
#endif
#ifdef OF_WINDOWS
# include <windows.h>
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
extern NSSearchPathEnumerationState NSStartSearchPathEnumeration(
    NSSearchPathDirectory, NSSearchPathDomainMask);
extern NSSearchPathEnumerationState NSGetNextSearchPathEnumeration(
    NSSearchPathEnumerationState, char *);
#endif

#if defined(OF_X86_64) || defined(OF_X86)
struct x86_regs {
	uint32_t eax, ebx, ecx, edx;
};
#endif

static size_t pageSize = 4096;
static size_t numberOfCPUs = 1;
static OFString *operatingSystemName = nil;







|







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
extern NSSearchPathEnumerationState NSStartSearchPathEnumeration(
    NSSearchPathDirectory, NSSearchPathDomainMask);
extern NSSearchPathEnumerationState NSGetNextSearchPathEnumeration(
    NSSearchPathEnumerationState, char *);
#endif

#if defined(OF_X86_64) || defined(OF_X86)
struct X86Regs {
	uint32_t eax, ebx, ecx, edx;
};
#endif

static size_t pageSize = 4096;
static size_t numberOfCPUs = 1;
static OFString *operatingSystemName = nil;
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
	}
# endif
#elif defined(OF_WINDOWS)
# ifdef OF_HAVE_FILES
	void *pool = objc_autoreleasePoolPush();

	@try {
		of_string_encoding_t encoding = [OFLocale encoding];
		char systemDir[PATH_MAX];
		UINT systemDirLen;
		OFString *systemDirString;
		const char *path;
		void *buffer;
		DWORD bufferLen;








|







162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
	}
# endif
#elif defined(OF_WINDOWS)
# ifdef OF_HAVE_FILES
	void *pool = objc_autoreleasePoolPush();

	@try {
		OFStringEncoding encoding = [OFLocale encoding];
		char systemDir[PATH_MAX];
		UINT systemDirLen;
		OFString *systemDirString;
		const char *path;
		void *buffer;
		DWORD bufferLen;

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
		}
	} @finally {
		objc_autoreleasePoolPop(pool);
	}
# endif
#elif defined(OF_ANDROID)
	/* TODO */
#elif defined(OF_MORPHOS)
	/* TODO */
#elif defined(OF_AMIGAOS4)
	/* TODO */
#elif defined(OF_AMIGAOS_M68K)
	/* TODO */
#elif defined(OF_WII) || defined(NINTENDO_3DS) || defined(OF_NINTENDO_DS) || \
    defined(OF_PSP) || defined(OF_MSDOS)
	/* Intentionally nothing */
#elif defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
	struct utsname utsname;

	if (uname(&utsname) != 0)
		return;

	operatingSystemVersion = [[OFString alloc]
	    initWithCString: utsname.release
		   encoding: [OFLocale encoding]];
#endif
}

#if defined(OF_X86_64) || defined(OF_X86)
static OF_INLINE struct x86_regs OF_CONST_FUNC
x86_cpuid(uint32_t eax, uint32_t ecx)
{
	struct x86_regs regs;

# if defined(OF_X86_64_ASM)
	__asm__ (
	    "cpuid"
	    : "=a"(regs.eax), "=b"(regs.ebx), "=c"(regs.ecx), "=d"(regs.edx)
	    : "a"(eax), "c"(ecx)
	);
# elif defined(OF_X86_ASM)
	/*
	 * This workaround is required by older GCC versions when using -fPIC,
	 * as ebx is a special register in PIC code. Yes, GCC is indeed not
	 * able to just push a register onto the stack before the __asm__ block
	 * and to pop it afterwards.
	 */
	__asm__ (







|
|
|
|
<
<
















|
|

|

|





|







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
		}
	} @finally {
		objc_autoreleasePoolPop(pool);
	}
# endif
#elif defined(OF_ANDROID)
	/* TODO */
#elif defined(OF_AMIGAOS)
	operatingSystemVersion = [[OFString alloc]
	    initWithFormat: @"Kickstart %u.%u",
			    SysBase->LibNode.lib_Version, SysBase->SoftVer];


#elif defined(OF_WII) || defined(NINTENDO_3DS) || defined(OF_NINTENDO_DS) || \
    defined(OF_PSP) || defined(OF_MSDOS)
	/* Intentionally nothing */
#elif defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
	struct utsname utsname;

	if (uname(&utsname) != 0)
		return;

	operatingSystemVersion = [[OFString alloc]
	    initWithCString: utsname.release
		   encoding: [OFLocale encoding]];
#endif
}

#if defined(OF_X86_64) || defined(OF_X86)
static OF_INLINE struct X86Regs OF_CONST_FUNC
x86CPUID(uint32_t eax, uint32_t ecx)
{
	struct X86Regs regs;

# if defined(OF_X86_64) && defined(__GNUC__)
	__asm__ (
	    "cpuid"
	    : "=a"(regs.eax), "=b"(regs.ebx), "=c"(regs.ecx), "=d"(regs.edx)
	    : "a"(eax), "c"(ecx)
	);
# elif defined(OF_X86) && defined(__GNUC__)
	/*
	 * This workaround is required by older GCC versions when using -fPIC,
	 * as ebx is a special register in PIC code. Yes, GCC is indeed not
	 * able to just push a register onto the stack before the __asm__ block
	 * and to pop it afterwards.
	 */
	__asm__ (
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
}

+ (OFString *)ObjFWVersion
{
	return @PACKAGE_VERSION;
}

+ (unsigned int)ObjFWVersionMajor
{
	return OBJFW_VERSION_MAJOR;
}

+ (unsigned int)ObjFWVersionMinor
{
	return OBJFW_VERSION_MINOR;
}

+ (OFString *)operatingSystemName
{
	static of_once_t onceControl = OF_ONCE_INIT;
	of_once(&onceControl, initOperatingSystemName);

	return operatingSystemName;
}

+ (OFString *)operatingSystemVersion
{
	static of_once_t onceControl = OF_ONCE_INIT;
	of_once(&onceControl, initOperatingSystemVersion);

	return operatingSystemVersion;
}

#ifdef OF_HAVE_FILES
+ (OFString *)userDataPath
{
# if defined(OF_MACOS) || defined(OF_IOS)
	char pathC[PATH_MAX];
	OFMutableString *path;

#  ifdef HAVE_SYSDIR_START_SEARCH_PATH_ENUMERATION
	/* (1) to disable dead code warning when it is not a weak symbol */
	if ((1) && &sysdir_start_search_path_enumeration != NULL) {
		sysdir_search_path_enumeration_state state;

		state = sysdir_start_search_path_enumeration(
		    SYSDIR_DIRECTORY_APPLICATION_SUPPORT,
		    SYSDIR_DOMAIN_MASK_USER);
		if (sysdir_get_next_search_path_enumeration(state, pathC) == 0)
			@throw [OFNotImplementedException







|




|






|
|






|
|












|
<







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
}

+ (OFString *)ObjFWVersion
{
	return @PACKAGE_VERSION;
}

+ (unsigned short)ObjFWVersionMajor
{
	return OBJFW_VERSION_MAJOR;
}

+ (unsigned short)ObjFWVersionMinor
{
	return OBJFW_VERSION_MINOR;
}

+ (OFString *)operatingSystemName
{
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, initOperatingSystemName);

	return operatingSystemName;
}

+ (OFString *)operatingSystemVersion
{
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, initOperatingSystemVersion);

	return operatingSystemVersion;
}

#ifdef OF_HAVE_FILES
+ (OFString *)userDataPath
{
# if defined(OF_MACOS) || defined(OF_IOS)
	char pathC[PATH_MAX];
	OFMutableString *path;

#  ifdef HAVE_SYSDIR_START_SEARCH_PATH_ENUMERATION
	if (@available(macOS 10.12, iOS 10, *)) {

		sysdir_search_path_enumeration_state state;

		state = sysdir_start_search_path_enumeration(
		    SYSDIR_DIRECTORY_APPLICATION_SUPPORT,
		    SYSDIR_DOMAIN_MASK_USER);
		if (sysdir_get_next_search_path_enumeration(state, pathC) == 0)
			@throw [OFNotImplementedException
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
		OFString *home;

		if ((home = [env objectForKey: @"HOME"]) == nil)
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];

		[path deleteCharactersInRange: of_range(0, 1)];
		[path prependString: home];
	}

	[path makeImmutable];

	return path;
# elif defined(OF_WINDOWS)







|







385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
		OFString *home;

		if ((home = [env objectForKey: @"HOME"]) == nil)
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];

		[path deleteCharactersInRange: OFRangeMake(0, 1)];
		[path prependString: home];
	}

	[path makeImmutable];

	return path;
# elif defined(OF_WINDOWS)
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
+ (OFString *)userConfigPath
{
# if defined(OF_MACOS) || defined(OF_IOS)
	char pathC[PATH_MAX];
	OFMutableString *path;

#  ifdef HAVE_SYSDIR_START_SEARCH_PATH_ENUMERATION
	/* (1) to disable dead code warning when it is not a weak symbol */
	if ((1) && &sysdir_start_search_path_enumeration != NULL) {
		sysdir_search_path_enumeration_state state;

		state = sysdir_start_search_path_enumeration(
		    SYSDIR_DIRECTORY_LIBRARY, SYSDIR_DOMAIN_MASK_USER);
		if (sysdir_get_next_search_path_enumeration(state, pathC) == 0)
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd







|
<







443
444
445
446
447
448
449
450

451
452
453
454
455
456
457
+ (OFString *)userConfigPath
{
# if defined(OF_MACOS) || defined(OF_IOS)
	char pathC[PATH_MAX];
	OFMutableString *path;

#  ifdef HAVE_SYSDIR_START_SEARCH_PATH_ENUMERATION
	if (@available(macOS 10.12, iOS 10, *)) {

		sysdir_search_path_enumeration_state state;

		state = sysdir_start_search_path_enumeration(
		    SYSDIR_DIRECTORY_LIBRARY, SYSDIR_DOMAIN_MASK_USER);
		if (sysdir_get_next_search_path_enumeration(state, pathC) == 0)
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
		OFString *home;

		if ((home = [env objectForKey: @"HOME"]) == nil)
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];

		[path deleteCharactersInRange: of_range(0, 1)];
		[path prependString: home];
	}

	[path appendString: @"/Preferences"];

	[path makeImmutable];

	return path;
# elif defined(OF_WINDOWS)
	OFDictionary *env = [OFApplication environment];
	OFString *appData;








|




<







476
477
478
479
480
481
482
483
484
485
486
487

488
489
490
491
492
493
494
		OFString *home;

		if ((home = [env objectForKey: @"HOME"]) == nil)
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];

		[path deleteCharactersInRange: OFRangeMake(0, 1)];
		[path prependString: home];
	}

	[path appendString: @"/Preferences"];

	[path makeImmutable];

	return path;
# elif defined(OF_WINDOWS)
	OFDictionary *env = [OFApplication environment];
	OFString *appData;

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
	if ((var = [env objectForKey: @"HOME"]) == nil)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	return [var stringByAppendingPathComponent: @".config"];
# endif
}























































#endif

+ (OFString *)CPUVendor
{
#if defined(OF_X86_64_ASM) || defined(OF_X86_ASM)
	struct x86_regs regs = x86_cpuid(0, 0);
	uint32_t buffer[3];

	if (regs.eax == 0)
		return nil;

	buffer[0] = regs.ebx;
	buffer[1] = regs.edx;
	buffer[2] = regs.ecx;

	return [OFString stringWithCString: (char *)buffer
				  encoding: OF_STRING_ENCODING_ASCII
				    length: 12];


#else
	return nil;
#endif
}

+ (OFString *)CPUModel
{
#if defined(OF_X86_64_ASM) || defined(OF_X86_ASM)

	uint32_t buffer[12];
	size_t i;




	i = 0;
	for (uint32_t eax = 0x80000002; eax <= 0x80000004; eax++) {
		struct x86_regs regs = x86_cpuid(eax, 0);

		buffer[i++] = regs.eax;
		buffer[i++] = regs.ebx;
		buffer[i++] = regs.ecx;
		buffer[i++] = regs.edx;
	}

	return [OFString stringWithCString: (char *)buffer
				  encoding: OF_STRING_ENCODING_ASCII];











#elif defined(OF_AMIGAOS4)
	CONST_STRPTR model, version;

	GetCPUInfoTags(GCIT_ModelString, &model,
	    GCIT_VersionString, &version, TAG_END);

	if (version != NULL)
		return [OFString stringWithFormat: @"%s V%s", model, version];
	else
		return [OFString stringWithCString: model
					  encoding: OF_STRING_ENCODING_ASCII];













#else
	return nil;
#endif
}

#if defined(OF_X86_64) || defined(OF_X86)
+ (bool)supportsMMX
{
	return (x86_cpuid(1, 0).edx & (1u << 23));
}

+ (bool)supportsSSE
{
	return (x86_cpuid(1, 0).edx & (1u << 25));
}

+ (bool)supportsSSE2
{
	return (x86_cpuid(1, 0).edx & (1u << 26));
}

+ (bool)supportsSSE3
{
	return (x86_cpuid(1, 0).ecx & (1u << 0));
}

+ (bool)supportsSSSE3
{
	return (x86_cpuid(1, 0).ecx & (1u << 9));
}

+ (bool)supportsSSE41
{
	return (x86_cpuid(1, 0).ecx & (1u << 19));
}

+ (bool)supportsSSE42
{
	return (x86_cpuid(1, 0).ecx & (1u << 20));
}

+ (bool)supportsAVX
{
	return (x86_cpuid(1, 0).ecx & (1u << 28));
}

+ (bool)supportsAVX2
{
	return x86_cpuid(0, 0).eax >= 7 && (x86_cpuid(7, 0).ebx & (1u << 5));
}

+ (bool)supportsAESNI
{
	return (x86_cpuid(1, 0).ecx & (1u << 25));
}

+ (bool)supportsSHAExtensions
{
	return (x86_cpuid(7, 0).ebx & (1u << 29));
}
#endif

#if defined(OF_POWERPC) || defined(OF_POWERPC64)
+ (bool)supportsAltiVec
{
# if defined(OF_MACOS)







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




|
|










|

>
>







|
>



>
>
>


<
|







|
>
>
>
>
>
>
>
>
>
>
>










|
>
>
>
>
>
>
>
>
>
>
>
>
>








|




|




|




|




|




|




|




|




|




|




|







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
	if ((var = [env objectForKey: @"HOME"]) == nil)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	return [var stringByAppendingPathComponent: @".config"];
# endif
}

+ (OFString *)temporaryDirectoryPath
{
# if defined(OF_MACOS) || defined(OF_IOS)
	char buffer[PATH_MAX];
	size_t length;

	if ((length = confstr(_CS_DARWIN_USER_TEMP_DIR, buffer, PATH_MAX)) == 0)
		return @"/tmp";

	return [OFString stringWithCString: buffer
				  encoding: [OFLocale encoding]
				    length: length - 1];
# elif defined(OF_WINDOWS)
	if ([self isWindowsNT]) {
		wchar_t buffer[PATH_MAX];

		if (!GetTempPathW(PATH_MAX, buffer))
			return nil;

		return [OFString stringWithUTF16String: buffer];
	} else {
		char buffer[PATH_MAX];

		if (!GetTempPathA(PATH_MAX, buffer))
			return nil;

		return [OFString stringWithCString: buffer
					  encoding: [OFLocale encoding]];
	}
# elif defined(OF_HAIKU)
	char pathC[PATH_MAX];

	if (find_directory(B_SYSTEM_TEMP_DIRECTORY, 0, false,
	    pathC, PATH_MAX) != B_OK)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	return [OFString stringWithUTF8String: pathC];
# elif defined(OF_AMIGAOS)
	return @"T:";
# elif defined(OF_MSDOS)
	return [[OFApplication environment] objectForKey: @"TEMP"];
# elif defined(OF_MINT)
	return @"u:\\tmp";
# else
	OFString *path =
	    [[OFApplication environment] objectForKey: @"XDG_RUNTIME_DIR"];

	if (path != nil)
		return path;

	return @"/tmp";
# endif
}
#endif

+ (OFString *)CPUVendor
{
#if (defined(OF_X86_64) || defined(OF_X86)) && defined(__GNUC__)
	struct X86Regs regs = x86CPUID(0, 0);
	uint32_t buffer[3];

	if (regs.eax == 0)
		return nil;

	buffer[0] = regs.ebx;
	buffer[1] = regs.edx;
	buffer[2] = regs.ecx;

	return [OFString stringWithCString: (char *)buffer
				  encoding: OFStringEncodingASCII
				    length: 12];
#elif defined(OF_M68K)
	return @"Motorola";
#else
	return nil;
#endif
}

+ (OFString *)CPUModel
{
#if (defined(OF_X86_64) || defined(OF_X86)) && defined(__GNUC__)
	struct X86Regs regs = x86CPUID(0x80000000, 0);
	uint32_t buffer[12];
	size_t i;

	if (regs.eax < 0x80000004)
		return nil;

	i = 0;
	for (uint32_t eax = 0x80000002; eax <= 0x80000004; eax++) {

		regs = x86CPUID(eax, 0);
		buffer[i++] = regs.eax;
		buffer[i++] = regs.ebx;
		buffer[i++] = regs.ecx;
		buffer[i++] = regs.edx;
	}

	return [OFString stringWithCString: (char *)buffer
				  encoding: OFStringEncodingASCII];
#elif defined(OF_MACOS) || defined(OF_IOS)
	char buffer[128];
	size_t length = sizeof(buffer);

	if (sysctlbyname("machdep.cpu.brand_string", &buffer, &length,
	    NULL, 0) != 0)
		return nil;

	return [OFString stringWithCString: buffer
				  encoding: [OFLocale encoding]
				    length: length];
#elif defined(OF_AMIGAOS4)
	CONST_STRPTR model, version;

	GetCPUInfoTags(GCIT_ModelString, &model,
	    GCIT_VersionString, &version, TAG_END);

	if (version != NULL)
		return [OFString stringWithFormat: @"%s V%s", model, version];
	else
		return [OFString stringWithCString: model
					  encoding: OFStringEncodingASCII];
#elif defined(OF_AMIGAOS_M68K)
	if (SysBase->AttnFlags & AFF_68060)
		return @"68060";
	if (SysBase->AttnFlags & AFF_68040)
		return @"68040";
	if (SysBase->AttnFlags & AFF_68030)
		return @"68030";
	if (SysBase->AttnFlags & AFF_68020)
		return @"68020";
	if (SysBase->AttnFlags & AFF_68010)
		return @"68010";
	else
		return @"68000";
#else
	return nil;
#endif
}

#if defined(OF_X86_64) || defined(OF_X86)
+ (bool)supportsMMX
{
	return (x86CPUID(1, 0).edx & (1u << 23));
}

+ (bool)supportsSSE
{
	return (x86CPUID(1, 0).edx & (1u << 25));
}

+ (bool)supportsSSE2
{
	return (x86CPUID(1, 0).edx & (1u << 26));
}

+ (bool)supportsSSE3
{
	return (x86CPUID(1, 0).ecx & (1u << 0));
}

+ (bool)supportsSSSE3
{
	return (x86CPUID(1, 0).ecx & (1u << 9));
}

+ (bool)supportsSSE41
{
	return (x86CPUID(1, 0).ecx & (1u << 19));
}

+ (bool)supportsSSE42
{
	return (x86CPUID(1, 0).ecx & (1u << 20));
}

+ (bool)supportsAVX
{
	return (x86CPUID(1, 0).ecx & (1u << 28));
}

+ (bool)supportsAVX2
{
	return x86CPUID(0, 0).eax >= 7 && (x86CPUID(7, 0).ebx & (1u << 5));
}

+ (bool)supportsAESNI
{
	return (x86CPUID(1, 0).ecx & (1u << 25));
}

+ (bool)supportsSHAExtensions
{
	return (x86CPUID(7, 0).ebx & (1u << 29));
}
#endif

#if defined(OF_POWERPC) || defined(OF_POWERPC64)
+ (bool)supportsAltiVec
{
# if defined(OF_MACOS)

Modified src/OFTCPSocket.h from [63c13750a5] to [fc53b099cc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket connected.
 *
 * @param exception An exception which occurred while connecting the socket or
 *		    `nil` on success
 */
typedef void (^of_tcp_socket_async_connect_block_t)(id _Nullable exception);
#endif

/**
 * @protocol OFTCPSocketDelegate OFTCPSocket.h ObjFW/OFTCPSocket.h
 *
 * A delegate for OFTCPSocket.
 */







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block which is called when the socket connected.
 *
 * @param exception An exception which occurred while connecting the socket or
 *		    `nil` on success
 */
typedef void (^OFTCPSocketAsyncConnectBlock)(id _Nullable exception);
#endif

/**
 * @protocol OFTCPSocketDelegate OFTCPSocket.h ObjFW/OFTCPSocket.h
 *
 * A delegate for OFTCPSocket.
 */
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
 * @brief Returns the port to use as a SOCKS5 proxy when creating a new socket
 *
 * @return The port to use as a SOCKS5 proxy when creating a new socket
 */
+ (uint16_t)SOCKS5Port;

/**
 * @brief Connect the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 */
- (void)connectToHost: (OFString *)host
		 port: (uint16_t)port;

/**
 * @brief Asynchronously connect the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port;

/**
 * @brief Asynchronously connect the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously connect the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
		     block: (of_tcp_socket_async_connect_block_t)block;

/**
 * @brief Asynchronously connect the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_tcp_socket_async_connect_block_t)block;
#endif

/**
 * @brief Bind the socket to the specified host and port.
 *
 * @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for
 *	       IPv6 to bind to all.
 * @param port The port to bind to. If the port is 0, an unused port will be
 *	       chosen, which can be obtained using the return value.
 * @return The port the socket was bound to
 */
- (uint16_t)bindToHost: (OFString *)host
		  port: (uint16_t)port;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern Class _Nullable of_tls_socket_class;
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|




|
<


|




|
<


|







|



|







|


|








|
|



|







|
<


<
<
<
<
<
<
<
<

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
 * @brief Returns the port to use as a SOCKS5 proxy when creating a new socket
 *
 * @return The port to use as a SOCKS5 proxy when creating a new socket
 */
+ (uint16_t)SOCKS5Port;

/**
 * @brief Connects the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 */
- (void)connectToHost: (OFString *)host port: (uint16_t)port;


/**
 * @brief Asynchronously connects the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 */
- (void)asyncConnectToHost: (OFString *)host port: (uint16_t)port;


/**
 * @brief Asynchronously connects the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Asynchronously connects the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
		     block: (OFTCPSocketAsyncConnectBlock)block;

/**
 * @brief Asynchronously connects the OFTCPSocket to the specified destination.
 *
 * @param host The host to connect to
 * @param port The port on the host to connect to
 * @param runLoopMode The run loop mode in which to perform the async connect
 * @param block The block to execute once the connection has been established
 */
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
		     block: (OFTCPSocketAsyncConnectBlock)block;
#endif

/**
 * @brief Binds the socket to the specified host and port.
 *
 * @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for
 *	       IPv6 to bind to all.
 * @param port The port to bind to. If the port is 0, an unused port will be
 *	       chosen, which can be obtained using the return value.
 * @return The port the socket was bound to
 */
- (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port;

@end









OF_ASSUME_NONNULL_END

Modified src/OFTCPSocket.m from [dc32b6d4c9] to [06bb1cad5c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */






#define __NO_EXT_QNX

#include "config.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFTCPSocket.h"
#import "OFDNSResolver.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFIPSocketAsyncConnector.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"


#import "OFString.h"
#import "OFTCPSocketSOCKS5Connector.h"
#import "OFThread.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFGetOptionFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFSetOptionFailedException.h"

#import "socket.h"
#import "socket_helpers.h"

static const of_run_loop_mode_t connectRunLoopMode =
    @"of_tcp_socket_connect_mode";

Class of_tls_socket_class = Nil;

static OFString *defaultSOCKS5Host = nil;
static uint16_t defaultSOCKS5Port = 1080;

@interface OFTCPSocket () <OFIPSocketAsyncConnecting>
@end


<
<
|













>
>
>
>
>

|
<

















>
>











<
<
|
|
<
<
<







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


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

#ifndef _XOPEN_SOURCE_EXTENDED
# define _XOPEN_SOURCE_EXTENDED
#endif
#define __NO_EXT_QNX
#define _HPUX_ALT_XOPEN_SOCKET_API


#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFTCPSocket.h"
#import "OFDNSResolver.h"
#import "OFData.h"
#import "OFDate.h"
#import "OFIPSocketAsyncConnector.h"
#import "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#import "OFTCPSocketSOCKS5Connector.h"
#import "OFThread.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFGetOptionFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFSetOptionFailedException.h"



static const OFRunLoopMode connectRunLoopMode =
    @"OFTCPSocketConnectRunLoopMode";




static OFString *defaultSOCKS5Host = nil;
static uint16_t defaultSOCKS5Port = 1080;

@interface OFTCPSocket () <OFIPSocketAsyncConnecting>
@end

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
- (void)dealloc
{
	[_SOCKS5Host release];

	[super dealloc];
}

- (bool)of_createSocketForAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo
{
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if ((_socket = socket(address->sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, 0)) == INVALID_SOCKET) {
		*errNo = of_socket_errno();
		return false;
	}

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	return true;
}

- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
			    errNo: (int *)errNo
{
	if (_socket == INVALID_SOCKET)
		@throw [OFNotOpenException exceptionWithObject: self];

	/* Cast needed for AmigaOS, where the argument is declared non-const */
	if (connect(_socket, (struct sockaddr *)&address->sockaddr.sockaddr,
	    address->length) != 0) {
		*errNo = of_socket_errno();
		return false;
	}

	return true;
}

- (void)of_closeSocket
{
	closesocket(_socket);
	_socket = INVALID_SOCKET;
}

- (void)connectToHost: (OFString *)host
		 port: (uint16_t)port
{
	void *pool = objc_autoreleasePoolPush();
	id <OFTCPSocketDelegate> delegate = _delegate;
	OFTCPSocketConnectDelegate *connectDelegate =
	    [[[OFTCPSocketConnectDelegate alloc] init] autorelease];
	OFRunLoop *runLoop = [OFRunLoop currentRunLoop];

	self.delegate = connectDelegate;
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: connectRunLoopMode];

	while (!connectDelegate->_done)
		[runLoop runMode: connectRunLoopMode
		      beforeDate: nil];

	/* Cleanup */
	[runLoop runMode: connectRunLoopMode
	      beforeDate: [OFDate date]];


	if (connectDelegate->_exception != nil)
		@throw connectDelegate->_exception;

	self.delegate = delegate;

	objc_autoreleasePoolPop(pool);
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
{
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: of_run_loop_mode_default];
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
{
	void *pool = objc_autoreleasePoolPush();
	id <OFTCPSocketDelegate> delegate;

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if (_SOCKS5Host != nil) {
		delegate = [[[OFTCPSocketSOCKS5Connector alloc]
		    initWithSocket: self
			      host: host
			      port: port







|






|



|
|











|


|





|









|


|
<







|





|
<


|
|
>




<
<



|
<



|




|




|







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
- (void)dealloc
{
	[_SOCKS5Host release];

	[super dealloc];
}

- (bool)of_createSocketForAddress: (const OFSocketAddress *)address
			    errNo: (int *)errNo
{
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if ((_socket = socket(address->sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle) {
		*errNo = OFSocketErrNo();
		return false;
	}

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	return true;
}

- (bool)of_connectSocketToAddress: (const OFSocketAddress *)address
			    errNo: (int *)errNo
{
	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	/* Cast needed for AmigaOS, where the argument is declared non-const */
	if (connect(_socket, (struct sockaddr *)&address->sockaddr.sockaddr,
	    address->length) != 0) {
		*errNo = OFSocketErrNo();
		return false;
	}

	return true;
}

- (void)of_closeSocket
{
	closesocket(_socket);
	_socket = OFInvalidSocketHandle;
}

- (void)connectToHost: (OFString *)host port: (uint16_t)port

{
	void *pool = objc_autoreleasePoolPush();
	id <OFTCPSocketDelegate> delegate = _delegate;
	OFTCPSocketConnectDelegate *connectDelegate =
	    [[[OFTCPSocketConnectDelegate alloc] init] autorelease];
	OFRunLoop *runLoop = [OFRunLoop currentRunLoop];

	_delegate = connectDelegate;
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: connectRunLoopMode];

	while (!connectDelegate->_done)
		[runLoop runMode: connectRunLoopMode beforeDate: nil];


	/* Cleanup */
	[runLoop runMode: connectRunLoopMode beforeDate: [OFDate date]];

	_delegate = delegate;

	if (connectDelegate->_exception != nil)
		@throw connectDelegate->_exception;



	objc_autoreleasePoolPop(pool);
}

- (void)asyncConnectToHost: (OFString *)host port: (uint16_t)port

{
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
{
	void *pool = objc_autoreleasePoolPush();
	id <OFTCPSocketDelegate> delegate;

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if (_SOCKS5Host != nil) {
		delegate = [[[OFTCPSocketSOCKS5Connector alloc]
		    initWithSocket: self
			      host: host
			      port: port
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

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
		     block: (of_tcp_socket_async_connect_block_t)block
{
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: of_run_loop_mode_default
			   block: block];
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (of_run_loop_mode_t)runLoopMode
		     block: (of_tcp_socket_async_connect_block_t)block
{
	void *pool = objc_autoreleasePoolPush();
	id <OFTCPSocketDelegate> delegate = nil;

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if (_SOCKS5Host != nil) {
		delegate = [[[OFTCPSocketSOCKS5Connector alloc]
		    initWithSocket: self
			      host: host
			      port: port







|



|





|
|




|







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

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
		     block: (OFTCPSocketAsyncConnectBlock)block
{
	[self asyncConnectToHost: host
			    port: port
		     runLoopMode: OFDefaultRunLoopMode
			   block: block];
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
		     block: (OFTCPSocketAsyncConnectBlock)block
{
	void *pool = objc_autoreleasePoolPush();
	id <OFTCPSocketDelegate> delegate = nil;

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if (_SOCKS5Host != nil) {
		delegate = [[[OFTCPSocketSOCKS5Connector alloc]
		    initWithSocket: self
			      host: host
			      port: port
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
			   block: (delegate == nil ? block : NULL)] autorelease]
	    startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}
#endif

- (uint16_t)bindToHost: (OFString *)host
		  port: (uint16_t)port
{
	const int one = 1;
	void *pool = objc_autoreleasePoolPush();
	OFData *socketAddresses;
	of_socket_address_t address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

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

	socketAddresses = [[OFThread DNSResolver]
	    resolveAddressesForHost: host
		      addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY];

	address = *(of_socket_address_t *)[socketAddresses itemAtIndex: 0];
	of_socket_address_set_port(&address, port);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, 0)) == INVALID_SOCKET)
		@throw [OFBindFailedException
		    exceptionWithHost: host
				 port: port
			       socket: self
				errNo: of_socket_errno()];

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR,
	    (char *)&one, (socklen_t)sizeof(one));

#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
	if (port != 0) {
#endif
		if (bind(_socket, &address.sockaddr.sockaddr,
		    address.length) != 0) {
			int errNo = of_socket_errno();

			closesocket(_socket);
			_socket = INVALID_SOCKET;

			@throw [OFBindFailedException exceptionWithHost: host
								   port: port
								 socket: self
								  errNo: errNo];
		}
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
	} else {
		for (;;) {
			uint16_t rnd = 0;
			int ret;

			while (rnd < 1024)
				rnd = (uint16_t)rand();

			of_socket_address_set_port(&address, rnd);

			if ((ret = bind(_socket, &address.sockaddr.sockaddr,
			    address.length)) == 0) {
				port = rnd;
				break;
			}

			if (of_socket_errno() != EADDRINUSE) {
				int errNo = of_socket_errno();

				closesocket(_socket);
				_socket = INVALID_SOCKET;

				@throw [OFBindFailedException
				    exceptionWithHost: host
						 port: port
					       socket: self
						errNo: errNo];
			}
		}
	}
#endif

	objc_autoreleasePoolPop(pool);

	if (port > 0)
		return port;

#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
	memset(&address, 0, sizeof(address));

	address.length = (socklen_t)sizeof(address.sockaddr);
	if (of_getsockname(_socket, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithHost: host
							   port: port
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_family == AF_INET)
		return OF_BSWAP16_IF_LE(address.sockaddr.in.sin_port);
# ifdef OF_HAVE_IPV6
	else if (address.sockaddr.sockaddr.sa_family == AF_INET6)
		return OF_BSWAP16_IF_LE(address.sockaddr.in6.sin6_port);
# endif
	else {
		closesocket(_socket);
		_socket = INVALID_SOCKET;

		@throw [OFBindFailedException exceptionWithHost: host
							   port: port
							 socket: self
							  errNo: EAFNOSUPPORT];
	}
#else
	closesocket(_socket);
	_socket = INVALID_SOCKET;
	@throw [OFBindFailedException exceptionWithHost: host
						   port: port
						 socket: self
						  errNo: EADDRNOTAVAIL];
#endif
}

#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
- (void)setSendsKeepAlives: (bool)sendsKeepAlives
{
	int v = sendsKeepAlives;

	if (setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE,
	    (char *)&v, (socklen_t)sizeof(v)) != 0)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];
}

- (bool)sendsKeepAlives
{
	int v;
	socklen_t len = sizeof(v);

	if (getsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE,
	    (char *)&v, &len) != 0 || len != sizeof(v))
		@throw [OFGetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	return v;
}
#endif

#ifndef OF_WII
- (void)setCanDelaySendingSegments: (bool)canDelaySendingSegments
{
	int v = !canDelaySendingSegments;

	if (setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
	    (char *)&v, (socklen_t)sizeof(v)) != 0)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];
}

- (bool)canDelaySendingSegments
{
	int v;
	socklen_t len = sizeof(v);

	if (getsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
	    (char *)&v, &len) != 0 || len != sizeof(v))
		@throw [OFGetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	return !v;
}
#endif

- (void)close
{







|
<




|




|








|

|
|


|




|











|




|


|






|








|







|
|


|
















|



|

|


|








|


|



|








|
















|











|














|











|







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
			   block: (delegate == nil ? block : NULL)] autorelease]
	    startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}
#endif

- (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port

{
	const int one = 1;
	void *pool = objc_autoreleasePoolPush();
	OFData *socketAddresses;
	OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

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

	socketAddresses = [[OFThread DNSResolver]
	    resolveAddressesForHost: host
		      addressFamily: OFSocketAddressFamilyAny];

	address = *(OFSocketAddress *)[socketAddresses itemAtIndex: 0];
	OFSocketAddressSetPort(&address, port);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
		@throw [OFBindFailedException
		    exceptionWithHost: host
				 port: port
			       socket: self
				errNo: OFSocketErrNo()];

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR,
	    (char *)&one, (socklen_t)sizeof(one));

#if defined(OF_HPUX) || defined(OF_WII) || defined(OF_NINTENDO_3DS)
	if (port != 0) {
#endif
		if (bind(_socket, &address.sockaddr.sockaddr,
		    address.length) != 0) {
			int errNo = OFSocketErrNo();

			closesocket(_socket);
			_socket = OFInvalidSocketHandle;

			@throw [OFBindFailedException exceptionWithHost: host
								   port: port
								 socket: self
								  errNo: errNo];
		}
#if defined(OF_HPUX) || defined(OF_WII) || defined(OF_NINTENDO_3DS)
	} else {
		for (;;) {
			uint16_t rnd = 0;
			int ret;

			while (rnd < 1024)
				rnd = (uint16_t)rand();

			OFSocketAddressSetPort(&address, rnd);

			if ((ret = bind(_socket, &address.sockaddr.sockaddr,
			    address.length)) == 0) {
				port = rnd;
				break;
			}

			if (OFSocketErrNo() != EADDRINUSE) {
				int errNo = OFSocketErrNo();

				closesocket(_socket);
				_socket = OFInvalidSocketHandle;

				@throw [OFBindFailedException
				    exceptionWithHost: host
						 port: port
					       socket: self
						errNo: errNo];
			}
		}
	}
#endif

	objc_autoreleasePoolPop(pool);

	if (port > 0)
		return port;

#if !defined(OF_HPUX) && !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
	memset(&address, 0, sizeof(address));

	address.length = (socklen_t)sizeof(address.sockaddr);
	if (OFGetSockName(_socket, &address.sockaddr.sockaddr,
	    &address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithHost: host
							   port: port
							 socket: self
							  errNo: errNo];
	}

	if (address.sockaddr.sockaddr.sa_family == AF_INET)
		return OFFromBigEndian16(address.sockaddr.in.sin_port);
# ifdef OF_HAVE_IPV6
	else if (address.sockaddr.sockaddr.sa_family == AF_INET6)
		return OFFromBigEndian16(address.sockaddr.in6.sin6_port);
# endif
	else {
		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithHost: host
							   port: port
							 socket: self
							  errNo: EAFNOSUPPORT];
	}
#else
	closesocket(_socket);
	_socket = OFInvalidSocketHandle;
	@throw [OFBindFailedException exceptionWithHost: host
						   port: port
						 socket: self
						  errNo: EADDRNOTAVAIL];
#endif
}

#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
- (void)setSendsKeepAlives: (bool)sendsKeepAlives
{
	int v = sendsKeepAlives;

	if (setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE,
	    (char *)&v, (socklen_t)sizeof(v)) != 0)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];
}

- (bool)sendsKeepAlives
{
	int v;
	socklen_t len = sizeof(v);

	if (getsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE,
	    (char *)&v, &len) != 0 || len != sizeof(v))
		@throw [OFGetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];

	return v;
}
#endif

#ifndef OF_WII
- (void)setCanDelaySendingSegments: (bool)canDelaySendingSegments
{
	int v = !canDelaySendingSegments;

	if (setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
	    (char *)&v, (socklen_t)sizeof(v)) != 0)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];
}

- (bool)canDelaySendingSegments
{
	int v;
	socklen_t len = sizeof(v);

	if (getsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
	    (char *)&v, &len) != 0 || len != sizeof(v))
		@throw [OFGetOptionFailedException
		    exceptionWithObject: self
				  errNo: OFSocketErrNo()];

	return !v;
}
#endif

- (void)close
{

Modified src/OFTCPSocketSOCKS5Connector.h from [1461d18ce8] to [a83f3eec6d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@interface OFTCPSocketSOCKS5Connector: OFObject <OFTCPSocketDelegate>
{
	OFTCPSocket *_socket;
	OFString *_host;
	uint16_t _port;
	id <OFTCPSocketDelegate> _Nullable _delegate;
#ifdef OF_HAVE_BLOCKS
	of_tcp_socket_async_connect_block_t _Nullable _block;
#endif
	id _Nullable _exception;
	enum {
		OF_SOCKS5_STATE_SEND_AUTHENTICATION = 1,
		OF_SOCKS5_STATE_READ_VERSION,
		OF_SOCKS5_STATE_SEND_REQUEST,
		OF_SOCKS5_STATE_READ_RESPONSE,
		OF_SOCKS5_STATE_READ_ADDRESS,
		OF_SOCKS5_STATE_READ_ADDRESS_LENGTH,
	} _SOCKS5State;
	/* Longest read is domain name (max 255 bytes) + port */
	unsigned char _buffer[257];
	OFMutableData *_Nullable _request;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (nullable id <OFTCPSocketDelegate>)delegate
#ifdef OF_HAVE_BLOCKS
			 block: (nullable of_tcp_socket_async_connect_block_t)
				    block
#endif
;
- (void)didConnect;
@end

OF_ASSUME_NONNULL_END







|


<
<
<
<
<
<
<
|










|
<






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
@interface OFTCPSocketSOCKS5Connector: OFObject <OFTCPSocketDelegate>
{
	OFTCPSocket *_socket;
	OFString *_host;
	uint16_t _port;
	id <OFTCPSocketDelegate> _Nullable _delegate;
#ifdef OF_HAVE_BLOCKS
	OFTCPSocketAsyncConnectBlock _Nullable _block;
#endif
	id _Nullable _exception;







	uint_least8_t _SOCKS5State;
	/* Longest read is domain name (max 255 bytes) + port */
	unsigned char _buffer[257];
	OFMutableData *_Nullable _request;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (nullable id <OFTCPSocketDelegate>)delegate
#ifdef OF_HAVE_BLOCKS
			 block: (nullable OFTCPSocketAsyncConnectBlock)block

#endif
;
- (void)didConnect;
@end

OF_ASSUME_NONNULL_END

Modified src/OFTCPSocketSOCKS5Connector.m from [a4bbcb1257] to [5abece8458].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
22
23
24
25
26
27
28









29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#import "OFTCPSocketSOCKS5Connector.h"
#import "OFData.h"
#import "OFRunLoop.h"
#import "OFString.h"

#import "OFConnectionFailedException.h"










@implementation OFTCPSocketSOCKS5Connector
- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (id <OFTCPSocketDelegate>)delegate
#ifdef OF_HAVE_BLOCKS
			 block: (of_tcp_socket_async_connect_block_t)block
#endif
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_host = [host copy];







>
>
>
>
>
>
>
>
>







|







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

#import "OFTCPSocketSOCKS5Connector.h"
#import "OFData.h"
#import "OFRunLoop.h"
#import "OFString.h"

#import "OFConnectionFailedException.h"

enum {
	stateSendAuthentication = 1,
	stateReadVersion,
	stateSendRequest,
	stateReadResponse,
	stateReadAddress,
	stateReadAddressLength,
};

@implementation OFTCPSocketSOCKS5Connector
- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (id <OFTCPSocketDelegate>)delegate
#ifdef OF_HAVE_BLOCKS
			 block: (OFTCPSocketAsyncConnectBlock)block
#endif
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_host = [host copy];
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(_exception);
	else {
#endif
		if ([_delegate respondsToSelector:
		    @selector(socket:didConnectToHost:port:exception:)])
			[_delegate    socket: _socket
			    didConnectToHost: _host
					port: _port
				   exception: _exception];
#ifdef OF_HAVE_BLOCKS
	}
#endif
}







|







87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(_exception);
	else {
#endif
		if ([_delegate respondsToSelector:
		    @selector(socket:didConnectToHost:port:exception:)])
			[_delegate socket: _socket
			    didConnectToHost: _host
					port: _port
				   exception: _exception];
#ifdef OF_HAVE_BLOCKS
	}
#endif
}
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

	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return;
	}

	data = [OFData dataWithItems: "\x05\x01\x00"
			       count: 3];

	_SOCKS5State = OF_SOCKS5_STATE_SEND_AUTHENTICATION;
	[_socket asyncWriteData: data
		    runLoopMode: [OFRunLoop currentRunLoop].currentMode];
}

-      (bool)stream: (OFStream *)sock
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (id)exception
{
	of_run_loop_mode_t runLoopMode;
	unsigned char *SOCKSVersion;
	uint8_t hostLength;
	unsigned char port[2];
	unsigned char *response, *addressLength;

	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return false;
	}

	runLoopMode = [OFRunLoop currentRunLoop].currentMode;

	switch (_SOCKS5State) {
	case OF_SOCKS5_STATE_READ_VERSION:
		SOCKSVersion = buffer;

		if (SOCKSVersion[0] != 5 || SOCKSVersion[1] != 0) {
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		[_request release];
		_request = [[OFMutableData alloc] init];

		[_request addItems: "\x05\x01\x00\x03"
			     count: 4];

		hostLength = (uint8_t)_host.UTF8StringLength;
		[_request addItem: &hostLength];
		[_request addItems: _host.UTF8String
			     count: hostLength];

		port[0] = _port >> 8;
		port[1] = _port & 0xFF;
		[_request addItems: port
			     count: 2];

		_SOCKS5State = OF_SOCKS5_STATE_SEND_REQUEST;
		[_socket asyncWriteData: _request
			    runLoopMode: runLoopMode];
		return false;
	case OF_SOCKS5_STATE_READ_RESPONSE:
		response = buffer;

		if (response[0] != 5 || response[2] != 0) {
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self







|
<

|









|














|















|
<



|
<



|
<

|
|
<

|







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

	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return;
	}

	data = [OFData dataWithItems: "\x05\x01\x00" count: 3];


	_SOCKS5State = stateSendAuthentication;
	[_socket asyncWriteData: data
		    runLoopMode: [OFRunLoop currentRunLoop].currentMode];
}

-      (bool)stream: (OFStream *)sock
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (id)exception
{
	OFRunLoopMode runLoopMode;
	unsigned char *SOCKSVersion;
	uint8_t hostLength;
	unsigned char port[2];
	unsigned char *response, *addressLength;

	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return false;
	}

	runLoopMode = [OFRunLoop currentRunLoop].currentMode;

	switch (_SOCKS5State) {
	case stateReadVersion:
		SOCKSVersion = buffer;

		if (SOCKSVersion[0] != 5 || SOCKSVersion[1] != 0) {
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		[_request release];
		_request = [[OFMutableData alloc] init];

		[_request addItems: "\x05\x01\x00\x03" count: 4];


		hostLength = (uint8_t)_host.UTF8StringLength;
		[_request addItem: &hostLength];
		[_request addItems: _host.UTF8String count: hostLength];


		port[0] = _port >> 8;
		port[1] = _port & 0xFF;
		[_request addItems: port count: 2];


		_SOCKS5State = stateSendRequest;
		[_socket asyncWriteData: _request runLoopMode: runLoopMode];

		return false;
	case stateReadResponse:
		response = buffer;

		if (response[0] != 5 || response[2] != 0) {
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
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
			[self didConnect];
			return false;
		}

		/* Skip the rest of the response */
		switch (response[3]) {
		case 1: /* IPv4 */
			_SOCKS5State = OF_SOCKS5_STATE_READ_ADDRESS;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 4 + 2
					 runLoopMode: runLoopMode];
			return false;
		case 3: /* Domain name */
			_SOCKS5State = OF_SOCKS5_STATE_READ_ADDRESS_LENGTH;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 1
					 runLoopMode: runLoopMode];
			return false;
		case 4: /* IPv6 */
			_SOCKS5State = OF_SOCKS5_STATE_READ_ADDRESS;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 16 + 2
					 runLoopMode: runLoopMode];
			return false;
		default:
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		return false;
	case OF_SOCKS5_STATE_READ_ADDRESS:
		[self didConnect];
		return false;
	case OF_SOCKS5_STATE_READ_ADDRESS_LENGTH:
		addressLength = buffer;

		_SOCKS5State = OF_SOCKS5_STATE_READ_ADDRESS;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: addressLength[0] + 2
				 runLoopMode: runLoopMode];
		return false;
	default:
		assert(0);
		return false;
	}
}

- (OFData *)stream: (OFStream *)sock
      didWriteData: (OFData *)data
      bytesWritten: (size_t)bytesWritten
	 exception: (id)exception
{
	of_run_loop_mode_t runLoopMode;

	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return nil;
	}

	runLoopMode = [OFRunLoop currentRunLoop].currentMode;

	switch (_SOCKS5State) {
	case OF_SOCKS5_STATE_SEND_AUTHENTICATION:
		_SOCKS5State = OF_SOCKS5_STATE_READ_VERSION;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 2
				 runLoopMode: runLoopMode];
		return nil;
	case OF_SOCKS5_STATE_SEND_REQUEST:
		[_request release];
		_request = nil;

		_SOCKS5State = OF_SOCKS5_STATE_READ_RESPONSE;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 4
				 runLoopMode: runLoopMode];
		return nil;
	default:
		assert(0);
		return nil;
	}
}
@end







|





|





|















|


|


|















|










|
|




|



|










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
			[self didConnect];
			return false;
		}

		/* Skip the rest of the response */
		switch (response[3]) {
		case 1: /* IPv4 */
			_SOCKS5State = stateReadAddress;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 4 + 2
					 runLoopMode: runLoopMode];
			return false;
		case 3: /* Domain name */
			_SOCKS5State = stateReadAddressLength;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 1
					 runLoopMode: runLoopMode];
			return false;
		case 4: /* IPv6 */
			_SOCKS5State = stateReadAddress;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 16 + 2
					 runLoopMode: runLoopMode];
			return false;
		default:
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		return false;
	case stateReadAddress:
		[self didConnect];
		return false;
	case stateReadAddressLength:
		addressLength = buffer;

		_SOCKS5State = stateReadAddress;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: addressLength[0] + 2
				 runLoopMode: runLoopMode];
		return false;
	default:
		assert(0);
		return false;
	}
}

- (OFData *)stream: (OFStream *)sock
      didWriteData: (OFData *)data
      bytesWritten: (size_t)bytesWritten
	 exception: (id)exception
{
	OFRunLoopMode runLoopMode;

	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return nil;
	}

	runLoopMode = [OFRunLoop currentRunLoop].currentMode;

	switch (_SOCKS5State) {
	case stateSendAuthentication:
		_SOCKS5State = stateReadVersion;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 2
				 runLoopMode: runLoopMode];
		return nil;
	case stateSendRequest:
		[_request release];
		_request = nil;

		_SOCKS5State = stateReadResponse;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 4
				 runLoopMode: runLoopMode];
		return nil;
	default:
		assert(0);
		return nil;
	}
}
@end

Renamed and modified src/tlskey.h [32bd25a13e] to src/OFTLSKey.h [daee12b5cc].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "objfw-defs.h"



#include "platform.h"

#if !defined(OF_HAVE_THREADS) || \
    (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS))
# error No thread-local storage available!
#endif

#import "macros.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_key_t of_tlskey_t;
#elif defined(OF_WINDOWS)
# include <windows.h>
typedef DWORD of_tlskey_t;



#elif defined(OF_AMIGAOS)
typedef struct of_tlskey {
	struct objc_hashtable *table;
	struct of_tlskey *next, *previous;
} *of_tlskey_t;
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern bool of_tlskey_new(of_tlskey_t *key);
extern bool of_tlskey_free(of_tlskey_t key);
#ifdef __cplusplus
}
#endif

/* TLS keys are inlined for performance. */

#if defined(OF_HAVE_PTHREADS)
static OF_INLINE void *
of_tlskey_get(of_tlskey_t key)
{
	return pthread_getspecific(key);
}

static OF_INLINE bool
of_tlskey_set(of_tlskey_t key, void *ptr)
{
	return (pthread_setspecific(key, ptr) == 0);
}
#elif defined(OF_WINDOWS)
static OF_INLINE void *
of_tlskey_get(of_tlskey_t key)
{
	return TlsGetValue(key);
}

static OF_INLINE bool
of_tlskey_set(of_tlskey_t key, void *ptr)
{
	return TlsSetValue(key, ptr);












}
#elif defined(OF_AMIGAOS)
/* Those are too big too inline. */
# ifdef __cplusplus
extern "C" {
# endif
extern void *of_tlskey_get(of_tlskey_t key);
extern bool of_tlskey_set(of_tlskey_t key, void *ptr);
# ifdef __cplusplus
}
# endif
#endif

<
<
|














>
>












|


|
>
>
>

|

|
|





|
|








|




|
|

|



|




|
|

|
>
>
>
>
>
>
>
>
>
>
>
>






|
|




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


 * 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 "objfw-defs.h"

#include <errno.h>

#include "platform.h"

#if !defined(OF_HAVE_THREADS) || \
    (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS))
# error No thread-local storage available!
#endif

#import "macros.h"

#if defined(OF_HAVE_PTHREADS)
# include <pthread.h>
typedef pthread_key_t OFTLSKey;
#elif defined(OF_WINDOWS)
# include <windows.h>
typedef DWORD OFTLSKey;
#elif defined(OF_MORPHOS)
# include <proto/exec.h>
typedef ULONG OFTLSKey;
#elif defined(OF_AMIGAOS)
typedef struct _OFTLSKey {
	struct objc_hashtable *table;
	struct _OFTLSKey *next, *previous;
} *OFTLSKey;
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern int OFTLSKeyNew(OFTLSKey *key);
extern int OFTLSKeyFree(OFTLSKey key);
#ifdef __cplusplus
}
#endif

/* TLS keys are inlined for performance. */

#if defined(OF_HAVE_PTHREADS)
static OF_INLINE void *
OFTLSKeyGet(OFTLSKey key)
{
	return pthread_getspecific(key);
}

static OF_INLINE int
OFTLSKeySet(OFTLSKey key, void *ptr)
{
	return pthread_setspecific(key, ptr);
}
#elif defined(OF_WINDOWS)
static OF_INLINE void *
OFTLSKeyGet(OFTLSKey key)
{
	return TlsGetValue(key);
}

static OF_INLINE int
OFTLSKeySet(OFTLSKey key, void *ptr)
{
	return (TlsSetValue(key, ptr) ? 0 : EINVAL);
}
#elif defined(OF_MORPHOS)
static OF_INLINE void *
OFTLSKeyGet(OFTLSKey key)
{
	return (void *)TLSGetValue(key);
}

static OF_INLINE int
OFTLSKeySet(OFTLSKey key, void *ptr)
{
	return (TLSSetValue(key, (APTR)ptr) ? 0 : EINVAL);
}
#elif defined(OF_AMIGAOS)
/* Those are too big too inline. */
# ifdef __cplusplus
extern "C" {
# endif
extern void *OFTLSKeyGet(OFTLSKey key);
extern int OFTLSKeySet(OFTLSKey key, void *ptr);
# ifdef __cplusplus
}
# endif
#endif

Renamed and modified src/tlskey.m [abedce4e93] to src/OFTLSKey.m [e3604d121c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include "platform/posix/tlskey.m"
#elif defined(OF_WINDOWS)
# include "platform/windows/tlskey.m"


#elif defined(OF_AMIGAOS)
# include "platform/amiga/tlskey.m"
#endif

<
<
|


















|

|
>
>

|

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


 * 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 "platform.h"

#if defined(OF_HAVE_PTHREADS)
# include "platform/POSIX/OFTLSKey.m"
#elif defined(OF_WINDOWS)
# include "platform/Windows/OFTLSKey.m"
#elif defined(OF_MORPHOS)
# include "platform/MorphOS/OFTLSKey.m"
#elif defined(OF_AMIGAOS)
# include "platform/AmigaOS/OFTLSKey.m"
#endif

Renamed and modified src/OFTLSSocket.h [c7eec148cd] to src/OFTLSStream.h [0ae2a154e2].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"


OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@protocol OFTLSSocket;

/**
 * @protocol OFTLSSocketDelegate OFTLSSocket.h ObjFW/OFTLSSocket.h
 *
 * @brief A delegate for classes implementing the OFTLSSocket protocol.
 */
@protocol OFTLSSocketDelegate <OFTCPSocketDelegate>
@optional
/**
 * @brief This callback is called when the TLS socket wants to know if it
 *	  should accept the received certificate.
 *
 * @note This is only used to verify certain fields of a certificate to allow
 *	 for protocol specific verification. The certificate chain is verified
 *	 using the specified CAs, or the system's CAs if no CAs have been
 *	 specified.
 *





 * @param socket The socket which wants to know if it should accept the received
 *		 certificate
 * @param certificate A dictionary with the fields of the received certificate
 * @return Whether the TLS socket should accept the received certificate chain
 */
-	     (bool)socket: (id <OFTLSSocket>)socket
  shouldAcceptCertificate: (OFDictionary *)certificate;
@end

/**
 * @protocol OFTLSSocket OFTLSSocket.h ObjFW/OFTLSSocket.h
 *
 * @brief A protocol that should be implemented by 3rd-party libraries
 *	  implementing TLS.
 */
@protocol OFTLSSocket
/**
 * @brief The delegate for the TLS socket.






 */



@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFTLSSocketDelegate> delegate;

/**
 * @brief The path to the X.509 certificate file to use.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *certificateFile;


/**
 * @brief The path to the PKCS#8 private key file to use.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *privateKeyFile;


/**
 * @brief The passphrase to decrypt the PKCS#8 private key file.
 *






 * @warning You have to ensure that this is in secure memory protected from
 *	    swapping! This is also the reason why this is not an OFString.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    const char *privateKeyPassphrase;

/**
 * @brief Whether certificates are verified.

 *
 * The default is enabled.
 */
@property (nonatomic) bool verifiesCertificates;



/**
 * @brief Initializes the TLS socket with the specified TCP socket as its
 *	  underlying socket.
 *


 * @param socket The TCP socket to use as underlying socket





 */
- (instancetype)initWithSocket: (OFTCPSocket *)socket;



/**
 * @brief Initiates the TLS handshake.
 *
 * @note This is only useful if you used @ref initWithSocket: to start TLS on
 *	 a TCP socket which is already connected!

 *
 * @param host The host to expect for certificate verification.
 *	       May be `nil` if certificate verification is disabled.
 */
- (void)startTLSWithExpectedHost: (nullable OFString *)host;

/**
 * @brief Sets the path to the X.509 certificate file to use for the specified
 *	  SNI host.
 *
 * @param SNIHost The SNI host for which the path of the X.509 certificate file
 *		  should be set
 *
 * @param certificateFile The path to the X.509 certificate file

 */
- (void)setCertificateFile: (OFString *)certificateFile
		forSNIHost: (OFString *)SNIHost;

/**
 * @brief Returns the path of the X.509 certificate file used by the TLS socket
 *	  for the specified SNI host.
 *

 * @param SNIHost The SNI host for which the path of the X.509 certificate file
 *		  should be returned
 *
 * @return The path of the X.509 certificate file used by the TLS socket for
 *	   the specified SNI host

 */
- (nullable OFString *)certificateFileForSNIHost: (OFString *)SNIHost;



/**
 * @brief Sets the path to the PKCS#8 private key file to use for the specified
 *	  SNI host.
 *
 * @param privateKeyFile The path to the PKCS#8 private key file
 * @param SNIHost The SNI host for which the path to the PKCS#8 private key
 *		  file should be set
 */
- (void)setPrivateKeyFile: (OFString *)privateKeyFile
	       forSNIHost: (OFString *)SNIHost;

/**
 * @brief Returns the path of the PKCS#8 private key file used by the TLS
 *	  socket for the specified SNI host.
 *
 * @param SNIHost The SNI host for which the path of the PKCS#8 private key
 *		  file should be returned
 *
 * @return The path of the PKCS#8 private key file used by the TLS socket for
 *	   the specified SNI host
 */
- (nullable OFString *)privateKeyFileForSNIHost: (OFString *)SNIHost;


/**
 * @brief Sets the passphrase to decrypt the PKCS#8 private key file for the
 *	  specified SNI host.
 *
 * @warning You have to ensure that this is in secure memory protected from
 *	    swapping! This is also the reason why this is not an OFString.
 *
 * @param privateKeyPassphrase The passphrase to decrypt the PKCS#8 private
 *			       key file for the specified SNI host
 * @param SNIHost The SNI host for which the passphrase to decrypt the PKCS#8
 *		  private key file should be set
 */
- (void)setPrivateKeyPassphrase: (const char *)privateKeyPassphrase

		     forSNIHost: (OFString *)SNIHost;



/**
 * @brief Returns the passphrase to decrypt the PKCS#8 private key file for the
 *	  specified SNI host.
 *


 * @warning You should not copy this to insecure memory which is swappable!
 *

 * @param SNIHost The SNI host for which the passphrase to decrypt the PKCS#8
 *		  private key file should be returned


 *

 * @return The passphrase to decrypt the PKCS#8 private key file for the
 *	   specified SNI host
 */
- (nullable const char *)privateKeyPassphraseForSNIHost: (OFString *)SNIHost;



@end

OF_ASSUME_NONNULL_END

<
<
|













|
>



|
<
<

<
|
|
<
<
<
<

|
<
|
<
<
<
|
<
>
>
>
>
>
<
<
<
<
<
<
<
<


|

<
|

|

|
>
>
>
>
>
>

>
>
>
|
<


<
<
<
>
|
<
|
<
<
|
>
|
<

>
>
>
>
>
>
|
|

|
|
|
<
<
>
|
<
<
|
>
|
>

<
|
|
>
>
|
>
>
>
>
>

<
>
>


|
|
<
<
>
|
|
<
<
<


|
|

|
|
<
<
>

<
|
|
<
<
<
|
>
|
|

|
|
>

|
>
>


|
|

|
<
<

<
|


|
|

|
|
<
<
<

|
>


<
|

<
<
<
|
<
<
<

|
>
|
>
>
|

|
<

>
>
|
|
>
|
<
>
>

>
|
<

|
>
>
>
|


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


 * 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 "OFStream.h"
#import "OFRunLoop.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */




@class OFTLSStream;





/**
 * @brief An enum representing an error of an OFTLSStream.

 */



typedef enum {

	/** @brief An unknown error. */
	OFTLSStreamErrorCodeUnknown,
	/** @brief Initialization of the TLS context failed. */
	OFTLSStreamErrorCodeInitializationFailed
} OFTLSStreamErrorCode;









/**
 * @protocol OFTLSStreamDelegate OFTLSStream.h ObjFW/OFTLSStream.h
 *

 * A delegate for OFTLSStream.
 */
@protocol OFTLSStreamDelegate <OFStreamDelegate>
/**
 * @brief A method which is called when a TLS stream performed the client
 *	  handshake.
 *
 * @param stream The TLS stream which performed the handshake
 * @param host The host for which the handshake was performed
 * @param exception An exception that occurred during the handshake, or nil on
 *		    success
 */
-		       (void)stream: (OFTLSStream *)stream
  didPerformClientHandshakeWithHost: (OFString *)host
			  exception: (nullable id)exception;
@end


/**



 * @class OFTLSStream OFTLSStream.h ObjFW/OFTLSStream.h
 *

 * @brief A class that provides Transport Layer Security on top of a stream.


 *
 * This class is a class cluster and returns a suitable OFTLSStream subclass,
 * if available.

 *
 * Subclasses need to override @ref lowlevelReadIntoBuffer:length:,
 * @ref lowlevelWriteBuffer:length: and
 * @ref asyncPerformClientHandshakeWithHost:runLoopMode:. The method
 * @ref hasDataInReadBuffer should be overridden to return `true` if the TLS
 * stream has cached unprocessed data internally, while returning
 * `self.underlyingStream.hasDataInReadBuffer` if it does not have any
 * unprocessed data. In order to get access to the underlying stream,
 * @ref underlyingStream can be used.
 */
@interface OFTLSStream: OFStream <OFReadyForReadingObserving,
    OFReadyForWritingObserving>
{


	OFStream <OFReadyForReadingObserving, OFReadyForWritingObserving>
	    *_underlyingStream;


	bool _verifiesCertificates;
	OF_RESERVE_IVARS(OFTLSStream, 4)
}

/**

 * @brief The underlying stream.
 */
@property (readonly, nonatomic) OFStream <OFReadyForReadingObserving,
    OFReadyForWritingObserving> *underlyingStream;

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

@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFTLSStreamDelegate> delegate;

/**
 * @brief Whether certificates are verified. Default is true.
 */


@property (nonatomic) bool verifiesCertificates;

- (instancetype)init OF_UNAVAILABLE;




/**
 * @brief Creates a new TLS stream with the specified stream as its underlying
 *	  stream.
 *
 * @param stream The stream to use as underlying stream. Must not be closed
 *		 before the TLS stream is closed.


 * @return A new, autoreleased TLS stream
 */

+ (instancetype)streamWithStream: (OFStream <OFReadyForReadingObserving,
				       OFReadyForWritingObserving> *)stream;




/**
 * @brief Initializes the TLS stream with the specified stream as its
 *	  underlying stream.
 *
 * @param stream The stream to use as underlying stream. Must not be closed
 *		 before the TLS stream is closed.
 * @return An initialized TLS stream
 */
- (instancetype)initWithStream: (OFStream <OFReadyForReadingObserving,
				     OFReadyForWritingObserving> *)stream
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Asynchronously performs the TLS client handshake for the specified
 *	  host and calls the delegate afterwards.
 *
 * @param host The host to perform the handshake with


 */

- (void)asyncPerformClientHandshakeWithHost: (OFString *)host;

/**
 * @brief Asynchronously performs the TLS client handshake for the specified
 *	  host and calls the delegate afterwards.
 *
 * @param host The host to perform the handshake with
 * @param runLoopMode The run loop mode in which to perform the async handshake



 */
- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
				runLoopMode: (OFRunLoopMode)runLoopMode;

/**

 * @brief Performs the TLS client handshake for the specified host.
 *



 * @param host The host to perform the handshake with



 */
- (void)performClientHandshakeWithHost: (OFString *)host;
@end

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief The implementation for OFTLSStream to use.

 *
 * This can be set to a class that is always used for OFTLSStream. This is
 * useful to either force a specific implementation or use one that ObjFW does
 * not know about.
 */
extern Class OFTLSStreamImplementation;


/**
 * @brief Returns a string description for the TLS stream error code.
 *
 * @param errorCode The error code to return the description for
 * @return A string description for the TLS stream error code

 */
extern OFString *OFTLSStreamErrorCodeDescription(
    OFTLSStreamErrorCode errorCode);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Added src/OFTLSStream.m version [01bd29e506].





















































































































































































































































































































































































































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

#import "OFTLSStream.h"
#import "OFDate.h"

#import "OFNotImplementedException.h"
#import "OFTLSHandshakeFailedException.h"

@interface OFTLSStreamHandshakeDelegate: OFObject <OFTLSStreamDelegate>
{
@public
	bool _done;
	id _exception;
}
@end

Class OFTLSStreamImplementation = Nil;
static const OFRunLoopMode handshakeRunLoopMode =
    @"OFTLSStreamHandshakeRunLoopMode";

/*
 * References to exceptions. This is needed because they are only used by
 * subclasses that are in a different library.
 */
void
_references_to_exceptions_of_OFTLSStream(void)
{
	_OFTLSHandshakeFailedException_reference = 1;
}

OFString *
OFTLSStreamErrorCodeDescription(OFTLSStreamErrorCode errorCode)
{
	switch (errorCode) {
	case OFTLSStreamErrorCodeInitializationFailed:
		return @"Initialization of TLS context failed";
	default:
		return @"Unknown error";
	}
}

@implementation OFTLSStreamHandshakeDelegate
- (void)dealloc
{
	[_exception release];

	[super dealloc];
}

-		       (void)stream: (OFTLSStream *)stream
  didPerformClientHandshakeWithHost: (OFString *)host
			  exception: (id)exception
{
	_done = true;
	_exception = [exception retain];
}
@end

@implementation OFTLSStream
@synthesize underlyingStream = _underlyingStream;
@dynamic delegate;
@synthesize verifiesCertificates = _verifiesCertificates;

+ (instancetype)alloc
{
	if (self == [OFTLSStream class]) {
		if (OFTLSStreamImplementation != Nil)
			return [OFTLSStreamImplementation alloc];

		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];
	}

	return [super alloc];
}

+ (instancetype)streamWithStream: (OFStream <OFReadyForReadingObserving,
				       OFReadyForWritingObserving> *)stream
{
	return [[[self alloc] initWithStream: stream] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OFStream <OFReadyForReadingObserving,
				     OFReadyForWritingObserving> *)stream
{
	self = [super init];

	@try {
		_underlyingStream = [stream retain];
		_verifiesCertificates = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_underlyingStream release];

	[super dealloc];
}

- (void)close
{
	[_underlyingStream release];
	_underlyingStream = nil;

	[super close];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)hasDataInReadBuffer
{
	return (super.hasDataInReadBuffer ||
	    _underlyingStream.hasDataInReadBuffer);
}

- (bool)lowlevelIsAtEndOfStream
{
	return _underlyingStream.atEndOfStream;
}

- (int)fileDescriptorForReading
{
	return _underlyingStream.fileDescriptorForReading;
}

- (int)fileDescriptorForWriting
{
	return _underlyingStream.fileDescriptorForWriting;
}

- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
{
	[self asyncPerformClientHandshakeWithHost: host
				      runLoopMode: OFDefaultRunLoopMode];
}

- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
				runLoopMode: (OFRunLoopMode)runLoopMode
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)performClientHandshakeWithHost: (OFString *)host
{
	void *pool = objc_autoreleasePoolPush();
	id <OFTLSStreamDelegate> delegate = _delegate;
	OFTLSStreamHandshakeDelegate *handshakeDelegate =
	    [[[OFTLSStreamHandshakeDelegate alloc] init] autorelease];
	OFRunLoop *runLoop = [OFRunLoop currentRunLoop];

	_delegate = handshakeDelegate;
	[self asyncPerformClientHandshakeWithHost: host
				      runLoopMode: handshakeRunLoopMode];

	while (!handshakeDelegate->_done)
		[runLoop runMode: handshakeRunLoopMode beforeDate: nil];

	/* Cleanup */
	[runLoop runMode: handshakeRunLoopMode beforeDate: [OFDate date]];

	_delegate = delegate;

	if (handshakeDelegate->_exception != nil)
		@throw handshakeDelegate->_exception;

	objc_autoreleasePoolPop(pool);
}
@end

Modified src/OFTarArchive.h from [7e4a70a4fc] to [a9bc537672].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *
 * @brief A class for accessing and manipulating tar archives.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFTarArchive: OFObject
{
	OFStream *_stream;
	enum {
		OF_TAR_ARCHIVE_MODE_READ,
		OF_TAR_ARCHIVE_MODE_WRITE,
		OF_TAR_ARCHIVE_MODE_APPEND
	} _mode;
	of_string_encoding_t _encoding;
	OFStream *_Nullable _lastReturnedStream;
}

/**
 * @brief The encoding to use for the archive. Defaults to UTF-8.
 */
@property (nonatomic) of_string_encoding_t 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







|
|
|
|

|






|







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
 *
 * @brief A class for accessing and manipulating tar archives.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFTarArchive: OFObject
{
	OFStream *_stream;
	enum OFTarArchiveMode {
		OFTarArchiveModeRead,
		OFTarArchiveModeWrite,
		OFTarArchiveModeAppend
	} _mode;
	OFStringEncoding _encoding;
	OFStream *_Nullable _lastReturnedStream;
}

/**
 * @brief The encoding to use for the archive. Defaults to UTF-8.
 */
@property (nonatomic) OFStringEncoding encoding;

/**
 * @brief A stream for reading the current entry.
 *
 * @note This is only available in read mode.
 *
 * @note The returned stream conforms to @ref OFReadyForReadingObserving if the
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
 * @param stream A stream from which the tar archive will be read.
 *		 For append mode, this needs to be an OFSeekableStream.
 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFTarArchive
 */
+ (instancetype)archiveWithStream: (OFStream *)stream
			     mode: (OFString *)mode;

#ifdef OF_HAVE_FILES
/**
 * @brief Creates a new OFTarArchive object with the specified file.
 *
 * @param path The path to the tar archive
 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFTarArchive
 */
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFTarArchive object with the
 *	  specified stream.







|
<











|
<







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
 * @param stream A stream from which the tar archive will be read.
 *		 For append mode, this needs to be an OFSeekableStream.
 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFTarArchive
 */
+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode;


#ifdef OF_HAVE_FILES
/**
 * @brief Creates a new OFTarArchive object with the specified file.
 *
 * @param path The path to the tar archive
 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFTarArchive
 */
+ (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode;

#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFTarArchive object with the
 *	  specified stream.
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 *
 * @param path The path to the tar archive
 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return An initialized OFTarArchive
 */
- (instancetype)initWithPath: (OFString *)path
			mode: (OFString *)mode;
#endif

/**
 * @brief Returns the next entry from the tar archive or `nil` if all entries
 *	  have been read.
 *
 * @note This is only available in read mode.







|
<







103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
 *
 * @param path The path to the tar archive
 * @param mode The mode for the tar file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return An initialized OFTarArchive
 */
- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode;

#endif

/**
 * @brief Returns the next entry from the tar archive or `nil` if all entries
 *	  have been read.
 *
 * @note This is only available in read mode.

Modified src/OFTarArchive.m from [6889b3aa0d] to [5a61b65e4b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"



#import "OFTarArchive.h"
#import "OFTarArchiveEntry.h"
#import "OFTarArchiveEntry+Private.h"
#import "OFDate.h"
#import "OFSeekableStream.h"
#import "OFStream.h"

<
<
|














>
>







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


 * 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 "OFTarArchive.h"
#import "OFTarArchiveEntry.h"
#import "OFTarArchiveEntry+Private.h"
#import "OFDate.h"
#import "OFSeekableStream.h"
#import "OFStream.h"
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
- (instancetype)of_initWithStream: (OFStream *)stream
			    entry: (OFTarArchiveEntry *)entry;
@end

@implementation OFTarArchive: OFObject
@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
{
	self = [super init];

	@try {
		_stream = [stream retain];

		if ([mode isEqual: @"r"])
			_mode = OF_TAR_ARCHIVE_MODE_READ;
		else if ([mode isEqual: @"w"])
			_mode = OF_TAR_ARCHIVE_MODE_WRITE;
		else if ([mode isEqual: @"a"])
			_mode = OF_TAR_ARCHIVE_MODE_APPEND;
		else
			@throw [OFInvalidArgumentException exception];

		if (_mode == OF_TAR_ARCHIVE_MODE_APPEND) {
			uint32_t buffer[1024 / sizeof(uint32_t)];
			bool empty = true;

			if (![_stream isKindOfClass: [OFSeekableStream class]])
				@throw [OFInvalidArgumentException exception];

			[(OFSeekableStream *)_stream seekToOffset: -1024
							   whence: SEEK_END];
			[_stream readIntoBuffer: buffer
				    exactLength: 1024];

			for (size_t i = 0; i < 1024 / sizeof(uint32_t); i++)
				if (buffer[i] != 0)
					empty = false;

			if (!empty)
				@throw [OFInvalidFormatException exception];

			[(OFSeekableStream *)stream seekToOffset: -1024
							  whence: SEEK_END];
		}

		_encoding = OF_STRING_ENCODING_UTF_8;
	} @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







|
<

|
<



|
<

|
<








|
<







|

|

|



|








|
<












|









|
<




|
<

|
<


|
<







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
- (instancetype)of_initWithStream: (OFStream *)stream
			    entry: (OFTarArchiveEntry *)entry;
@end

@implementation OFTarArchive: OFObject
@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

{
	self = [super init];

	@try {
		_stream = [stream retain];

		if ([mode isEqual: @"r"])
			_mode = OFTarArchiveModeRead;
		else if ([mode isEqual: @"w"])
			_mode = OFTarArchiveModeWrite;
		else if ([mode isEqual: @"a"])
			_mode = OFTarArchiveModeAppend;
		else
			@throw [OFInvalidArgumentException exception];

		if (_mode == OFTarArchiveModeAppend) {
			uint32_t buffer[1024 / sizeof(uint32_t)];
			bool empty = true;

			if (![_stream isKindOfClass: [OFSeekableStream class]])
				@throw [OFInvalidArgumentException exception];

			[(OFSeekableStream *)_stream seekToOffset: -1024
							   whence: SEEK_END];
			[_stream readIntoBuffer: buffer exactLength: 1024];


			for (size_t i = 0; i < 1024 / sizeof(uint32_t); i++)
				if (buffer[i] != 0)
					empty = false;

			if (!empty)
				@throw [OFInvalidFormatException exception];

			[(OFSeekableStream *)stream seekToOffset: -1024
							  whence: SEEK_END];
		}

		_encoding = OFStringEncodingUTF8;
	} @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
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

- (OFTarArchiveEntry *)nextEntry
{
	OFTarArchiveEntry *entry;
	uint32_t buffer[512 / sizeof(uint32_t)];
	bool empty = true;

	if (_mode != OF_TAR_ARCHIVE_MODE_READ)
		@throw [OFInvalidArgumentException exception];

	[(OFTarArchiveFileReadStream *)_lastReturnedStream of_skip];
	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	if (_stream.atEndOfStream)
		return nil;

	[_stream readIntoBuffer: buffer
		    exactLength: 512];

	for (size_t i = 0; i < 512 / sizeof(uint32_t); i++)
		if (buffer[i] != 0)
			empty = false;

	if (empty) {
		[_stream readIntoBuffer: buffer
			    exactLength: 512];

		for (size_t i = 0; i < 512 / sizeof(uint32_t); i++)
			if (buffer[i] != 0)
				@throw [OFInvalidFormatException exception];

		return nil;
	}







|














|
<






|
<







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

- (OFTarArchiveEntry *)nextEntry
{
	OFTarArchiveEntry *entry;
	uint32_t buffer[512 / sizeof(uint32_t)];
	bool empty = true;

	if (_mode != OFTarArchiveModeRead)
		@throw [OFInvalidArgumentException exception];

	[(OFTarArchiveFileReadStream *)_lastReturnedStream of_skip];
	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	if (_stream.atEndOfStream)
		return nil;

	[_stream readIntoBuffer: buffer exactLength: 512];


	for (size_t i = 0; i < 512 / sizeof(uint32_t); i++)
		if (buffer[i] != 0)
			empty = false;

	if (empty) {
		[_stream readIntoBuffer: buffer exactLength: 512];


		for (size_t i = 0; i < 512 / sizeof(uint32_t); i++)
			if (buffer[i] != 0)
				@throw [OFInvalidFormatException exception];

		return nil;
	}
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
			entry: entry];

	return entry;
}

- (OFStream *)streamForReadingCurrentEntry
{
	if (_mode != OF_TAR_ARCHIVE_MODE_READ)
		@throw [OFInvalidArgumentException exception];

	if (_lastReturnedStream == nil)
		@throw [OFInvalidArgumentException exception];

	return [[(OFTarArchiveFileReadStream *)_lastReturnedStream
	    retain] autorelease];
}

- (OFStream *)streamForWritingEntry: (OFTarArchiveEntry *)entry
{
	void *pool;

	if (_mode != OF_TAR_ARCHIVE_MODE_WRITE &&
	    _mode != OF_TAR_ARCHIVE_MODE_APPEND)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	[entry of_writeToStream: _stream
		       encoding: _encoding];

	_lastReturnedStream = [[OFTarArchiveFileWriteStream alloc]
	    of_initWithStream: _stream
			entry: entry];

	objc_autoreleasePoolPop(pool);








|













|
<












|
<







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
			entry: entry];

	return entry;
}

- (OFStream *)streamForReadingCurrentEntry
{
	if (_mode != OFTarArchiveModeRead)
		@throw [OFInvalidArgumentException exception];

	if (_lastReturnedStream == nil)
		@throw [OFInvalidArgumentException exception];

	return [[(OFTarArchiveFileReadStream *)_lastReturnedStream
	    retain] autorelease];
}

- (OFStream *)streamForWritingEntry: (OFTarArchiveEntry *)entry
{
	void *pool;

	if (_mode != OFTarArchiveModeWrite && _mode != OFTarArchiveModeAppend)

		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	[entry of_writeToStream: _stream encoding: _encoding];


	_lastReturnedStream = [[OFTarArchiveFileWriteStream alloc]
	    of_initWithStream: _stream
			entry: entry];

	objc_autoreleasePoolPop(pool);

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	if (_mode == OF_TAR_ARCHIVE_MODE_WRITE ||
	    _mode == OF_TAR_ARCHIVE_MODE_APPEND) {
		char buffer[1024];
		memset(buffer, '\0', 1024);
		[_stream writeBuffer: buffer
			      length: 1024];
	}

	[_stream release];
	_stream = nil;
}
@end








|
<


|
<







252
253
254
255
256
257
258
259

260
261
262

263
264
265
266
267
268
269
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}
	[_lastReturnedStream release];
	_lastReturnedStream = nil;

	if (_mode == OFTarArchiveModeWrite || _mode == OFTarArchiveModeAppend) {

		char buffer[1024];
		memset(buffer, '\0', 1024);
		[_stream writeBuffer: buffer length: 1024];

	}

	[_stream release];
	_stream = nil;
}
@end

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
	if (length > UINT64_MAX)
		@throw [OFOutOfRangeException exception];
#endif

	if ((uint64_t)length > _toRead)
		length = (size_t)_toRead;

	ret = [_stream readIntoBuffer: buffer
			       length: length];

	if (ret == 0)
		_atEndOfStream = true;

	_toRead -= ret;

	return ret;
}







|
<
<







310
311
312
313
314
315
316
317


318
319
320
321
322
323
324
	if (length > UINT64_MAX)
		@throw [OFOutOfRangeException exception];
#endif

	if ((uint64_t)length > _toRead)
		length = (size_t)_toRead;

	ret = [_stream readIntoBuffer: buffer length: length];


	if (ret == 0)
		_atEndOfStream = true;

	_toRead -= ret;

	return ret;
}
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

- (void)of_skip
{
	if (_stream == nil || _skipped)
		return;

	if ([_stream isKindOfClass: [OFSeekableStream class]] &&
	    _toRead <= INT64_MAX && (of_offset_t)_toRead == (int64_t)_toRead) {
		uint64_t size;

		[(OFSeekableStream *)_stream seekToOffset: (of_offset_t)_toRead
						   whence: SEEK_CUR];

		_toRead = 0;

		size = _entry.size;

		if (size % 512 != 0)
			[(OFSeekableStream *)_stream
			    seekToOffset: 512 - (size % 512)
				  whence: SEEK_CUR];
	} else {
		char buffer[512];
		uint64_t size;

		while (_toRead >= 512) {
			[_stream readIntoBuffer: buffer
				    exactLength: 512];
			_toRead -= 512;
		}

		if (_toRead > 0) {
			[_stream readIntoBuffer: buffer
				    exactLength: (size_t)_toRead];
			_toRead = 0;







|


|















|
<







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

- (void)of_skip
{
	if (_stream == nil || _skipped)
		return;

	if ([_stream isKindOfClass: [OFSeekableStream class]] &&
	    _toRead <= INT64_MAX && (OFFileOffset)_toRead == (int64_t)_toRead) {
		uint64_t size;

		[(OFSeekableStream *)_stream seekToOffset: (OFFileOffset)_toRead
						   whence: SEEK_CUR];

		_toRead = 0;

		size = _entry.size;

		if (size % 512 != 0)
			[(OFSeekableStream *)_stream
			    seekToOffset: 512 - (size % 512)
				  whence: SEEK_CUR];
	} else {
		char buffer[512];
		uint64_t size;

		while (_toRead >= 512) {
			[_stream readIntoBuffer: buffer exactLength: 512];

			_toRead -= 512;
		}

		if (_toRead > 0) {
			[_stream readIntoBuffer: buffer
				    exactLength: (size_t)_toRead];
			_toRead = 0;
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
		[self close];

	[_entry release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	size_t bytesWritten;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if ((uint64_t)length > _toWrite)
		@throw [OFOutOfRangeException exception];

	@try {
		bytesWritten = [_stream writeBuffer: buffer
					     length: length];
	} @catch (OFWriteFailedException *e) {


		_toWrite -= e.bytesWritten;




		@throw e;
	}

	_toWrite -= bytesWritten;

	return bytesWritten;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];








|
<

<
<







|
<

>
>

>
>
>
>



|

|







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
		[self close];

	[_entry release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{


	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if ((uint64_t)length > _toWrite)
		@throw [OFOutOfRangeException exception];

	@try {
		[_stream writeBuffer: buffer length: length];

	} @catch (OFWriteFailedException *e) {
		OFEnsure(e.bytesWritten <= length);

		_toWrite -= e.bytesWritten;

		if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN)
			return e.bytesWritten;

		@throw e;
	}

	_toWrite -= length;

	return length;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

Modified src/OFTarArchiveEntry+Private.h from [13ddd96cdf] to [92ff63bc6f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28
29
30
31
32
33
34
OF_ASSUME_NONNULL_BEGIN

@class OFStream;

OF_DIRECT_MEMBERS
@interface OFTarArchiveEntry ()
- (instancetype)of_initWithHeader: (unsigned char [_Nonnull 512])header
			 encoding: (of_string_encoding_t)encoding
    OF_METHOD_FAMILY(init);
- (void)of_writeToStream: (OFStream *)stream
		encoding: (of_string_encoding_t)encoding;
@end

OF_ASSUME_NONNULL_END







|


|



19
20
21
22
23
24
25
26
27
28
29
30
31
32
OF_ASSUME_NONNULL_BEGIN

@class OFStream;

OF_DIRECT_MEMBERS
@interface OFTarArchiveEntry ()
- (instancetype)of_initWithHeader: (unsigned char [_Nonnull 512])header
			 encoding: (OFStringEncoding)encoding
    OF_METHOD_FAMILY(init);
- (void)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding;
@end

OF_ASSUME_NONNULL_END

Modified src/OFTarArchiveEntry.h from [05098921c4] to [68a10bc9a2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

@class OFDate;

/**
 * @brief The type of the archive entry.
 */
typedef enum of_tar_archive_entry_type_t {
	/** Normal file */
	OF_TAR_ARCHIVE_ENTRY_TYPE_FILE		   = '0',
	/** Hard link */
	OF_TAR_ARCHIVE_ENTRY_TYPE_LINK		   = '1',
	/** Symbolic link */
	OF_TAR_ARCHIVE_ENTRY_TYPE_SYMLINK	   = '2',
	/** Character device */
	OF_TAR_ARCHIVE_ENTRY_TYPE_CHARACTER_DEVICE = '3',
	/** Block device */
	OF_TAR_ARCHIVE_ENTRY_TYPE_BLOCK_DEVICE	   = '4',
	/** Directory */
	OF_TAR_ARCHIVE_ENTRY_TYPE_DIRECTORY	   = '5',
	/** FIFO */
	OF_TAR_ARCHIVE_ENTRY_TYPE_FIFO		   = '6',
	/** Contiguous file */
	OF_TAR_ARCHIVE_ENTRY_TYPE_CONTIGUOUS_FILE  = '7',
} of_tar_archive_entry_type_t;

/**
 * @class OFTarArchiveEntry OFTarArchiveEntry.h ObjFW/OFTarArchiveEntry.h
 *
 * @brief A class which represents an entry of a tar archive.
 */
@interface OFTarArchiveEntry: OFObject <OFCopying, OFMutableCopying>
{
	OFString *_fileName;
	unsigned long _mode;
	unsigned long long _size;
	unsigned long _UID, _GID;
	OFDate *_modificationDate;
	of_tar_archive_entry_type_t _type;
	OFString *_Nullable _targetFileName;
	OFString *_Nullable _owner, *_Nullable _group;
	unsigned long _deviceMajor, _deviceMinor;
	OF_RESERVE_IVARS(OFTarArchiveEntry, 4)
}

/**







|

|

|

|

|

|

|

|

|
|













|







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

@class OFDate;

/**
 * @brief The type of the archive entry.
 */
typedef enum {
	/** Normal file */
	OFTarArchiveEntryTypeFile	     = '0',
	/** Hard link */
	OFTarArchiveEntryTypeLink	     = '1',
	/** Symbolic link */
	OFTarArchiveEntryTypeSymlink	     = '2',
	/** Character device */
	OFTarArchiveEntryTypeCharacterDevice = '3',
	/** Block device */
	OFTarArchiveEntryTypeBlockDevice     = '4',
	/** Directory */
	OFTarArchiveEntryTypeDirectory	     = '5',
	/** FIFO */
	OFTarArchiveEntryTypeFIFO	     = '6',
	/** Contiguous file */
	OFTarArchiveEntryTypeContiguousFile  = '7',
} OFTarArchiveEntryType;

/**
 * @class OFTarArchiveEntry OFTarArchiveEntry.h ObjFW/OFTarArchiveEntry.h
 *
 * @brief A class which represents an entry of a tar archive.
 */
@interface OFTarArchiveEntry: OFObject <OFCopying, OFMutableCopying>
{
	OFString *_fileName;
	unsigned long _mode;
	unsigned long long _size;
	unsigned long _UID, _GID;
	OFDate *_modificationDate;
	OFTarArchiveEntryType _type;
	OFString *_Nullable _targetFileName;
	OFString *_Nullable _owner, *_Nullable _group;
	unsigned long _deviceMajor, _deviceMinor;
	OF_RESERVE_IVARS(OFTarArchiveEntry, 4)
}

/**
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
 * @brief The date of the last modification of the file.
 */
@property (readonly, retain, nonatomic) OFDate *modificationDate;

/**
 * @brief The type of the archive entry.
 *
 * See @ref of_tar_archive_entry_type_t.
 */
@property (readonly, nonatomic) of_tar_archive_entry_type_t type;

/**
 * @brief The file name of the target (for a hard link or symbolic link).
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
    OFString *targetFileName;








|

|







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
 * @brief The date of the last modification of the file.
 */
@property (readonly, retain, nonatomic) OFDate *modificationDate;

/**
 * @brief The type of the archive entry.
 *
 * See @ref OFTarArchiveEntryType.
 */
@property (readonly, nonatomic) OFTarArchiveEntryType type;

/**
 * @brief The file name of the target (for a hard link or symbolic link).
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
    OFString *targetFileName;

Modified src/OFTarArchiveEntry.m from [5e09222260] to [1a8a203480].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFStream.h"
#import "OFString.h"

#import "OFOutOfRangeException.h"

static OFString *
stringFromBuffer(const unsigned char *buffer, size_t length,
    of_string_encoding_t encoding)
{
	for (size_t i = 0; i < length; i++)
		if (buffer[i] == '\0')
			length = i;

	return [OFString stringWithCString: (const char *)buffer
				  encoding: encoding
				    length: length];
}

static void
stringToBuffer(unsigned char *buffer, OFString *string, size_t length,
    of_string_encoding_t encoding)
{
	size_t cStringLength = [string cStringLengthWithEncoding: encoding];

	if (cStringLength > length)
		@throw [OFOutOfRangeException exception];

	memcpy(buffer, [string cStringWithEncoding: encoding], cStringLength);







|












|







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
#import "OFStream.h"
#import "OFString.h"

#import "OFOutOfRangeException.h"

static OFString *
stringFromBuffer(const unsigned char *buffer, size_t length,
    OFStringEncoding encoding)
{
	for (size_t i = 0; i < length; i++)
		if (buffer[i] == '\0')
			length = i;

	return [OFString stringWithCString: (const char *)buffer
				  encoding: encoding
				    length: length];
}

static void
stringToBuffer(unsigned char *buffer, OFString *string, size_t length,
    OFStringEncoding encoding)
{
	size_t cStringLength = [string cStringLengthWithEncoding: encoding];

	if (cStringLength > length)
		@throw [OFOutOfRangeException exception];

	memcpy(buffer, [string cStringWithEncoding: encoding], cStringLength);
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
		return 0;

	if (buffer[0] == 0x80) {
		for (size_t i = 1; i < length; i++)
			value = (value << 8) | buffer[i];
	} else
		value = [stringFromBuffer(buffer, length,
		    OF_STRING_ENCODING_ASCII) unsignedLongLongValueWithBase: 8];

	if (value > max)
		@throw [OFOutOfRangeException exception];

	return value;
}

@implementation OFTarArchiveEntry
+ (instancetype)entryWithFileName: (OFString *)fileName
{
	return [[[self alloc] initWithFileName: fileName] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithHeader: (unsigned char [512])header
			 encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFString *targetFileName;

		_fileName = [stringFromBuffer(header, 100, encoding) copy];
		_mode = (unsigned long)octalValueFromBuffer(
		    header + 100, 8, ULONG_MAX);
		_UID = (unsigned long)octalValueFromBuffer(
		    header + 108, 8, ULONG_MAX);
		_GID = (unsigned long)octalValueFromBuffer(
		    header + 116, 8, ULONG_MAX);
		_size = (unsigned long long)octalValueFromBuffer(
		    header + 124, 12, ULLONG_MAX);
		_modificationDate = [[OFDate alloc]
		    initWithTimeIntervalSince1970:
		    (of_time_interval_t)octalValueFromBuffer(
		    header + 136, 12, ULLONG_MAX)];
		_type = header[156];

		targetFileName = stringFromBuffer(header + 157, 100, encoding);
		if (targetFileName.length > 0)
			_targetFileName = [targetFileName copy];

		if (_type == '\0')
			_type = OF_TAR_ARCHIVE_ENTRY_TYPE_FILE;

		if (memcmp(header + 257, "ustar\0" "00", 8) == 0) {
			OFString *prefix;

			_owner = [stringFromBuffer(header + 265, 32, encoding)
			    copy];
			_group = [stringFromBuffer(header + 297, 32, encoding)







|



















|


















|








|







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
		return 0;

	if (buffer[0] == 0x80) {
		for (size_t i = 1; i < length; i++)
			value = (value << 8) | buffer[i];
	} else
		value = [stringFromBuffer(buffer, length,
		    OFStringEncodingASCII) unsignedLongLongValueWithBase: 8];

	if (value > max)
		@throw [OFOutOfRangeException exception];

	return value;
}

@implementation OFTarArchiveEntry
+ (instancetype)entryWithFileName: (OFString *)fileName
{
	return [[[self alloc] initWithFileName: fileName] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithHeader: (unsigned char [512])header
			 encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFString *targetFileName;

		_fileName = [stringFromBuffer(header, 100, encoding) copy];
		_mode = (unsigned long)octalValueFromBuffer(
		    header + 100, 8, ULONG_MAX);
		_UID = (unsigned long)octalValueFromBuffer(
		    header + 108, 8, ULONG_MAX);
		_GID = (unsigned long)octalValueFromBuffer(
		    header + 116, 8, ULONG_MAX);
		_size = (unsigned long long)octalValueFromBuffer(
		    header + 124, 12, ULLONG_MAX);
		_modificationDate = [[OFDate alloc]
		    initWithTimeIntervalSince1970:
		    (OFTimeInterval)octalValueFromBuffer(
		    header + 136, 12, ULLONG_MAX)];
		_type = header[156];

		targetFileName = stringFromBuffer(header + 157, 100, encoding);
		if (targetFileName.length > 0)
			_targetFileName = [targetFileName copy];

		if (_type == '\0')
			_type = OFTarArchiveEntryTypeFile;

		if (memcmp(header + 257, "ustar\0" "00", 8) == 0) {
			OFString *prefix;

			_owner = [stringFromBuffer(header + 265, 32, encoding)
			    copy];
			_group = [stringFromBuffer(header + 297, 32, encoding)
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

- (instancetype)initWithFileName: (OFString *)fileName
{
	self = [super init];

	@try {
		_fileName = [fileName copy];
		_type = OF_TAR_ARCHIVE_ENTRY_TYPE_FILE;
		_mode = 0644;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;







|







149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

- (instancetype)initWithFileName: (OFString *)fileName
{
	self = [super init];

	@try {
		_fileName = [fileName copy];
		_type = OFTarArchiveEntryTypeFile;
		_mode = 0644;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
}

- (OFDate *)modificationDate
{
	return _modificationDate;
}

- (of_tar_archive_entry_type_t)type
{
	return _type;
}

- (OFString *)targetFileName
{
	return _targetFileName;







|







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
}

- (OFDate *)modificationDate
{
	return _modificationDate;
}

- (OFTarArchiveEntryType)type
{
	return _type;
}

- (OFString *)targetFileName
{
	return _targetFileName;
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

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (void)of_writeToStream: (OFStream *)stream
		encoding: (of_string_encoding_t)encoding
{
	unsigned char buffer[512];
	unsigned long long modificationDate;
	uint16_t checksum = 0;

	stringToBuffer(buffer, _fileName, 100, encoding);
	stringToBuffer(buffer + 100,
	    [OFString stringWithFormat: @"%06" PRIo16 " ", _mode], 8,
	    OF_STRING_ENCODING_ASCII);
	stringToBuffer(buffer + 108,
	    [OFString stringWithFormat: @"%06" PRIo16 " ", _UID], 8,
	    OF_STRING_ENCODING_ASCII);
	stringToBuffer(buffer + 116,
	    [OFString stringWithFormat: @"%06" PRIo16 " ", _GID], 8,
	    OF_STRING_ENCODING_ASCII);
	stringToBuffer(buffer + 124,
	    [OFString stringWithFormat: @"%011" PRIo64 " ", _size], 12,
	    OF_STRING_ENCODING_ASCII);
	modificationDate = _modificationDate.timeIntervalSince1970;
	stringToBuffer(buffer + 136,
	    [OFString stringWithFormat: @"%011llo", modificationDate],
	    12, OF_STRING_ENCODING_ASCII);

	/*
	 * During checksumming, the checksum field is expected to be set to 8
	 * spaces.
	 */
	memset(buffer + 148, ' ', 8);

	buffer[156] = _type;
	stringToBuffer(buffer + 157, _targetFileName, 100, encoding);

	/* ustar */
	memcpy(buffer + 257, "ustar\0" "00", 8);
	stringToBuffer(buffer + 265, _owner, 32, encoding);
	stringToBuffer(buffer + 297, _group, 32, encoding);
	stringToBuffer(buffer + 329,
	    [OFString stringWithFormat: @"%06" PRIo32 " ", _deviceMajor], 8,
	    OF_STRING_ENCODING_ASCII);
	stringToBuffer(buffer + 337,
	    [OFString stringWithFormat: @"%06" PRIo32 " ", _deviceMinor], 8,
	    OF_STRING_ENCODING_ASCII);
	memset(buffer + 345, '\0', 155 + 12);

	/* Fill in the checksum */
	for (size_t i = 0; i < 500; i++)
		checksum += buffer[i];
	stringToBuffer(buffer + 148,
	    [OFString stringWithFormat: @"%06" PRIo16, checksum], 7,
	    OF_STRING_ENCODING_ASCII);

	[stream writeBuffer: buffer
		     length: sizeof(buffer)];
}
@end







|








|


|


|


|



|
















|


|







|

|
<


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

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (void)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding
{
	unsigned char buffer[512];
	unsigned long long modificationDate;
	uint16_t checksum = 0;

	stringToBuffer(buffer, _fileName, 100, encoding);
	stringToBuffer(buffer + 100,
	    [OFString stringWithFormat: @"%06" PRIo16 " ", _mode], 8,
	    OFStringEncodingASCII);
	stringToBuffer(buffer + 108,
	    [OFString stringWithFormat: @"%06" PRIo16 " ", _UID], 8,
	    OFStringEncodingASCII);
	stringToBuffer(buffer + 116,
	    [OFString stringWithFormat: @"%06" PRIo16 " ", _GID], 8,
	    OFStringEncodingASCII);
	stringToBuffer(buffer + 124,
	    [OFString stringWithFormat: @"%011" PRIo64 " ", _size], 12,
	    OFStringEncodingASCII);
	modificationDate = _modificationDate.timeIntervalSince1970;
	stringToBuffer(buffer + 136,
	    [OFString stringWithFormat: @"%011llo", modificationDate],
	    12, OFStringEncodingASCII);

	/*
	 * During checksumming, the checksum field is expected to be set to 8
	 * spaces.
	 */
	memset(buffer + 148, ' ', 8);

	buffer[156] = _type;
	stringToBuffer(buffer + 157, _targetFileName, 100, encoding);

	/* ustar */
	memcpy(buffer + 257, "ustar\0" "00", 8);
	stringToBuffer(buffer + 265, _owner, 32, encoding);
	stringToBuffer(buffer + 297, _group, 32, encoding);
	stringToBuffer(buffer + 329,
	    [OFString stringWithFormat: @"%06" PRIo32 " ", _deviceMajor], 8,
	    OFStringEncodingASCII);
	stringToBuffer(buffer + 337,
	    [OFString stringWithFormat: @"%06" PRIo32 " ", _deviceMinor], 8,
	    OFStringEncodingASCII);
	memset(buffer + 345, '\0', 155 + 12);

	/* Fill in the checksum */
	for (size_t i = 0; i < 500; i++)
		checksum += buffer[i];
	stringToBuffer(buffer + 148,
	    [OFString stringWithFormat: @"%06" PRIo16, checksum], 7,
	    OFStringEncodingASCII);

	[stream writeBuffer: buffer length: sizeof(buffer)];

}
@end

Modified src/OFThread+Private.h from [5ab3745dfa] to [b87af72459].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFThread.h from [a47e26d1ae] to [edfcfa680d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <setjmp.h>

#import "OFObject.h"

#ifdef OF_HAVE_THREADS
# import "thread.h"
#endif

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFDate;
#ifdef OF_HAVE_SOCKETS
@class OFDNSResolver;
#endif
@class OFRunLoop;
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);

#if defined(OF_HAVE_THREADS) && defined(OF_HAVE_BLOCKS)
/**
 * @brief A block to be executed in a new thread.
 *
 * @return The object which should be returned when the thread is joined
 */
typedef id _Nullable (^of_thread_block_t)(void);
#endif

/**
 * @class OFThread OFThread.h ObjFW/OFThread.h
 *
 * @brief A class which provides portable threads.
 *

<
<
|
















<

|



















|







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


 * 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 <setjmp.h>

#import "OFObject.h"

#ifdef OF_HAVE_THREADS
# import "OFPlainThread.h"
#endif

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFDate;
#ifdef OF_HAVE_SOCKETS
@class OFDNSResolver;
#endif
@class OFRunLoop;
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);

#if defined(OF_HAVE_THREADS) && defined(OF_HAVE_BLOCKS)
/**
 * @brief A block to be executed in a new thread.
 *
 * @return The object which should be returned when the thread is joined
 */
typedef id _Nullable (^OFThreadBlock)(void);
#endif

/**
 * @class OFThread OFThread.h ObjFW/OFThread.h
 *
 * @brief A class which provides portable threads.
 *
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
 *	    so context can be associated with a thread.
 */
@interface OFThread: OFObject
#ifdef OF_HAVE_THREADS
    <OFCopying>
{
@private
	of_thread_t _thread;
	of_thread_attr_t _attr;
	enum of_thread_running {
		OF_THREAD_NOT_RUNNING,
		OF_THREAD_RUNNING,
		OF_THREAD_WAITING_FOR_JOIN
	} _running;
# ifndef OF_OBJFW_RUNTIME
	void *_pool;
# endif
# ifdef OF_HAVE_BLOCKS
	of_thread_block_t _Nullable _threadBlock;
# endif
	jmp_buf _exitEnv;
	id _returnValue;
	bool _supportsSockets;
	OFRunLoop *_Nullable _runLoop;
	OFMutableDictionary *_threadDictionary;
	OFString *_Nullable _name;







|
|
|
|
|
|





|







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
 *	    so context can be associated with a thread.
 */
@interface OFThread: OFObject
#ifdef OF_HAVE_THREADS
    <OFCopying>
{
@private
	OFPlainThread _thread;
	OFPlainThreadAttributes _attr;
	enum OFThreadState {
		OFThreadStateNotRunning,
		OFThreadStateRunning,
		OFThreadStateWaitingForJoin
	} _running;
# ifndef OF_OBJFW_RUNTIME
	void *_pool;
# endif
# ifdef OF_HAVE_BLOCKS
	OFThreadBlock _Nullable _threadBlock;
# endif
	jmp_buf _exitEnv;
	id _returnValue;
	bool _supportsSockets;
	OFRunLoop *_Nullable _runLoop;
	OFMutableDictionary *_threadDictionary;
	OFString *_Nullable _name;
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
 */
@property OF_NULLABLE_PROPERTY (copy) OFString *name;

# ifdef OF_HAVE_BLOCKS
/**
 * @brief The block to execute in the thread.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
    of_thread_block_t threadBlock;
# endif

/**
 * @brief The run loop for the thread.
 */
@property (readonly, nonatomic) OFRunLoop *runLoop;








|
<







114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
 */
@property OF_NULLABLE_PROPERTY (copy) OFString *name;

# ifdef OF_HAVE_BLOCKS
/**
 * @brief The block to execute in the thread.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThreadBlock threadBlock;

# endif

/**
 * @brief The run loop for the thread.
 */
@property (readonly, nonatomic) OFRunLoop *runLoop;

167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# ifdef OF_HAVE_BLOCKS
/**
 * @brief Creates a new thread with the specified block.
 *
 * @param threadBlock A block which is executed by the thread
 * @return A new, autoreleased thread
 */
+ (instancetype)threadWithThreadBlock: (of_thread_block_t)threadBlock;
# endif

/**
 * @brief Returns the current thread.
 *
 * @return The current thread
 */







|







163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# ifdef OF_HAVE_BLOCKS
/**
 * @brief Creates a new thread with the specified block.
 *
 * @param threadBlock A block which is executed by the thread
 * @return A new, autoreleased thread
 */
+ (instancetype)threadWithThreadBlock: (OFThreadBlock)threadBlock;
# endif

/**
 * @brief Returns the current thread.
 *
 * @return The current thread
 */
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

/**
 * @brief Suspends execution of the current thread for the specified time
 *	  interval.
 *
 * @param timeInterval The number of seconds to sleep
 */
+ (void)sleepForTimeInterval: (of_time_interval_t)timeInterval;

/**
 * @brief Suspends execution of the current thread until the specified date.
 *
 * @param date The date to wait for
 */
+ (void)sleepUntilDate: (OFDate *)date;







|







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

/**
 * @brief Suspends execution of the current thread for the specified time
 *	  interval.
 *
 * @param timeInterval The number of seconds to sleep
 */
+ (void)sleepForTimeInterval: (OFTimeInterval)timeInterval;

/**
 * @brief Suspends execution of the current thread until the specified date.
 *
 * @param date The date to wait for
 */
+ (void)sleepUntilDate: (OFDate *)date;
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# ifdef OF_HAVE_BLOCKS
/**
 * @brief Initializes an already allocated thread with the specified block.
 *
 * @param threadBlock A block which is executed by the thread
 * @return An initialized OFThread.
 */
- (instancetype)initWithThreadBlock: (of_thread_block_t)threadBlock;
# endif

/**
 * @brief The main routine of the thread. You need to reimplement this!
 *
 * @return The object the join method should return when called for this thread
 */







|







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# ifdef OF_HAVE_BLOCKS
/**
 * @brief Initializes an already allocated thread with the specified block.
 *
 * @param threadBlock A block which is executed by the thread
 * @return An initialized OFThread.
 */
- (instancetype)initWithThreadBlock: (OFThreadBlock)threadBlock;
# endif

/**
 * @brief The main routine of the thread. You need to reimplement this!
 *
 * @return The object the join method should return when called for this thread
 */

Modified src/OFThread.m from [690c42ccaa] to [9e6c736b19].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
46
47
48
49
50
51
52



53
54
55
56
57
58
59

#ifdef OF_NINTENDO_3DS
# include <3ds/svc.h>
#endif

#import "OFThread.h"
#import "OFThread+Private.h"



#import "OFDate.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_SOCKETS
# import "OFDNSResolver.h"
#endif
#import "OFLocale.h"
#import "OFRunLoop.h"







>
>
>







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

#ifdef OF_NINTENDO_3DS
# include <3ds/svc.h>
#endif

#import "OFThread.h"
#import "OFThread+Private.h"
#ifdef OF_HAVE_ATOMIC_OPS
# import "OFAtomic.h"
#endif
#import "OFDate.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_SOCKETS
# import "OFDNSResolver.h"
#endif
#import "OFLocale.h"
#import "OFRunLoop.h"
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
#import "OFOutOfRangeException.h"
#ifdef OF_HAVE_THREADS
# import "OFThreadJoinFailedException.h"
# import "OFThreadStartFailedException.h"
# import "OFThreadStillRunningException.h"
#endif

#ifdef OF_HAVE_ATOMIC_OPS
# import "atomic.h"

#endif

#if defined(OF_HAVE_THREADS)
# import "tlskey.h"
# if defined(OF_AMIGAOS) && defined(OF_HAVE_SOCKETS)
#  import "socket.h"
# endif

static of_tlskey_t threadSelfKey;
static OFThread *mainThread;
#elif defined(OF_HAVE_SOCKETS)
static OFDNSResolver *DNSResolver;
#endif

@implementation OFThread
#ifdef OF_HAVE_THREADS
static void
callMain(id object)
{
	OFThread *thread = (OFThread *)object;
	OFString *name;

	if (!of_tlskey_set(threadSelfKey, thread))
		@throw [OFInitializationFailedException
		    exceptionWithClass: thread.class];

#ifndef OF_OBJFW_RUNTIME
	thread->_pool = objc_autoreleasePoolPush();
#endif

	name = thread.name;
	if (name != nil)
		of_thread_set_name(
		    [name cStringWithEncoding: [OFLocale encoding]]);
	else
		of_thread_set_name(object_getClassName(thread));

#if defined(OF_AMIGAOS) && defined(OF_HAVE_SOCKETS)
	if (thread.supportsSockets)
		if (!of_socket_init())
			@throw [OFInitializationFailedException
			    exceptionWithClass: thread.class];
#endif

	/*
	 * Nasty workaround for thread implementations which can't return a
	 * pointer on join, or don't have a way to exit a thread.







|
|
>



|

|


|













|









|


|



|







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 "OFOutOfRangeException.h"
#ifdef OF_HAVE_THREADS
# import "OFThreadJoinFailedException.h"
# import "OFThreadStartFailedException.h"
# import "OFThreadStillRunningException.h"
#endif

#ifdef OF_MINT
/* freemint-gcc does not have trunc() */
# define trunc(x) ((int64_t)(x))
#endif

#if defined(OF_HAVE_THREADS)
# import "OFTLSKey.h"
# if defined(OF_AMIGAOS) && defined(OF_HAVE_SOCKETS)
#  import "OFSocket.h"
# endif

static OFTLSKey threadSelfKey;
static OFThread *mainThread;
#elif defined(OF_HAVE_SOCKETS)
static OFDNSResolver *DNSResolver;
#endif

@implementation OFThread
#ifdef OF_HAVE_THREADS
static void
callMain(id object)
{
	OFThread *thread = (OFThread *)object;
	OFString *name;

	if (OFTLSKeySet(threadSelfKey, thread) != 0)
		@throw [OFInitializationFailedException
		    exceptionWithClass: thread.class];

#ifndef OF_OBJFW_RUNTIME
	thread->_pool = objc_autoreleasePoolPush();
#endif

	name = thread.name;
	if (name != nil)
		OFSetThreadName(
		    [name cStringWithEncoding: [OFLocale encoding]]);
	else
		OFSetThreadName(object_getClassName(thread));

#if defined(OF_AMIGAOS) && defined(OF_HAVE_SOCKETS)
	if (thread.supportsSockets)
		if (!OFSocketInit())
			@throw [OFInitializationFailedException
			    exceptionWithClass: thread.class];
#endif

	/*
	 * Nasty workaround for thread implementations which can't return a
	 * pointer on join, or don't have a way to exit a thread.
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
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

#ifdef OF_OBJFW_RUNTIME
	objc_autoreleasePoolPop((void *)(uintptr_t)-1);
#else
	objc_autoreleasePoolPop(thread->_pool);
#endif

#if defined(OF_AMIGAOS) && defined(OF_HAVE_SOCKETS)
	if (thread.supportsSockets)
		of_socket_deinit();
#endif

	thread->_running = OF_THREAD_WAITING_FOR_JOIN;

	[thread release];
}

@synthesize name = _name;
# ifdef OF_HAVE_BLOCKS
@synthesize threadBlock = _threadBlock;
# endif

+ (void)initialize
{
	if (self != [OFThread class])
		return;

	if (!of_tlskey_new(&threadSelfKey))
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)thread
{
	return [[[self alloc] init] autorelease];
}

# ifdef OF_HAVE_BLOCKS
+ (instancetype)threadWithThreadBlock: (of_thread_block_t)threadBlock
{
	return [[[self alloc] initWithThreadBlock: threadBlock] autorelease];
}
# endif

+ (OFThread *)currentThread
{
	return of_tlskey_get(threadSelfKey);
}

+ (OFThread *)mainThread
{
	return mainThread;
}

+ (bool)isMainThread
{
	if (mainThread == nil)
		return false;

	return (of_tlskey_get(threadSelfKey) == mainThread);
}

+ (OFMutableDictionary *)threadDictionary
{
	OFThread *thread = of_tlskey_get(threadSelfKey);

	if (thread == nil)
		return nil;

	if (thread->_threadDictionary == nil)
		thread->_threadDictionary = [[OFMutableDictionary alloc] init];

	return thread->_threadDictionary;
}
#endif

#ifdef OF_HAVE_SOCKETS
+ (OFDNSResolver *)DNSResolver
{
# ifdef OF_HAVE_THREADS
	OFThread *thread = of_tlskey_get(threadSelfKey);

	if (thread == nil)
		return nil;

	if (thread->_DNSResolver == nil)
		thread->_DNSResolver = [[OFDNSResolver alloc] init];

	return thread->_DNSResolver;
# else
	if (DNSResolver == nil)
		DNSResolver = [[OFDNSResolver alloc] init];

	return DNSResolver;
# endif
}
#endif

+ (void)sleepForTimeInterval: (of_time_interval_t)timeInterval
{
	if (timeInterval < 0)
		return;

#if defined(OF_WINDOWS)
	if (timeInterval * 1000 > UINT_MAX)
		@throw [OFOutOfRangeException exception];

	Sleep((unsigned int)(timeInterval * 1000));
#elif defined(OF_NINTENDO_3DS)
	if (timeInterval * 1000000000 > INT64_MAX)
		@throw [OFOutOfRangeException exception];

	svcSleepThread((int64_t)(timeInterval * 1000000000));











#elif defined(HAVE_NANOSLEEP)
	struct timespec rqtp;

	rqtp.tv_sec = (time_t)timeInterval;
	rqtp.tv_nsec = (long)((timeInterval - rqtp.tv_sec) * 1000000000);

	if (rqtp.tv_sec != trunc(timeInterval))
		@throw [OFOutOfRangeException exception];

	nanosleep(&rqtp, NULL);
#elif defined(OF_AMIGAOS)
	if (timeInterval * 50 > ULONG_MAX)
		@throw [OFOutOfRangeException exception];

	Delay(timeInterval * 50);
#elif defined(OF_NINTENDO_DS)
	uint64_t counter;

	if (timeInterval > UINT64_MAX / 60)
		@throw [OFOutOfRangeException exception];

	counter = timeInterval * 60;
	while (counter--)
		swiWaitForVBlank();
#else
	if (timeInterval > UINT_MAX)
		@throw [OFOutOfRangeException exception];

	sleep((unsigned int)timeInterval);
	usleep((unsigned int)
	    (timeInterval - (unsigned int)timeInterval) * 1000000);
#endif
}

+ (void)sleepUntilDate: (OFDate *)date
{
	[self sleepForTimeInterval: date.timeIntervalSinceNow];
}







|

|


|














|










|







|












|




|















|

















|














>
>
>
>
>
>
>
>
>
>
>










<
<
<
<
<















|







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

#ifdef OF_OBJFW_RUNTIME
	objc_autoreleasePoolPop((void *)(uintptr_t)-1);
#else
	objc_autoreleasePoolPop(thread->_pool);
#endif

#if defined(OF_AMIGAOS) && !defined(OF_MORPHOS) && defined(OF_HAVE_SOCKETS)
	if (thread.supportsSockets)
		OFSocketDeinit();
#endif

	thread->_running = OFThreadStateWaitingForJoin;

	[thread release];
}

@synthesize name = _name;
# ifdef OF_HAVE_BLOCKS
@synthesize threadBlock = _threadBlock;
# endif

+ (void)initialize
{
	if (self != [OFThread class])
		return;

	if (OFTLSKeyNew(&threadSelfKey) != 0)
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)thread
{
	return [[[self alloc] init] autorelease];
}

# ifdef OF_HAVE_BLOCKS
+ (instancetype)threadWithThreadBlock: (OFThreadBlock)threadBlock
{
	return [[[self alloc] initWithThreadBlock: threadBlock] autorelease];
}
# endif

+ (OFThread *)currentThread
{
	return OFTLSKeyGet(threadSelfKey);
}

+ (OFThread *)mainThread
{
	return mainThread;
}

+ (bool)isMainThread
{
	if (mainThread == nil)
		return false;

	return (OFTLSKeyGet(threadSelfKey) == mainThread);
}

+ (OFMutableDictionary *)threadDictionary
{
	OFThread *thread = OFTLSKeyGet(threadSelfKey);

	if (thread == nil)
		return nil;

	if (thread->_threadDictionary == nil)
		thread->_threadDictionary = [[OFMutableDictionary alloc] init];

	return thread->_threadDictionary;
}
#endif

#ifdef OF_HAVE_SOCKETS
+ (OFDNSResolver *)DNSResolver
{
# ifdef OF_HAVE_THREADS
	OFThread *thread = OFTLSKeyGet(threadSelfKey);

	if (thread == nil)
		return nil;

	if (thread->_DNSResolver == nil)
		thread->_DNSResolver = [[OFDNSResolver alloc] init];

	return thread->_DNSResolver;
# else
	if (DNSResolver == nil)
		DNSResolver = [[OFDNSResolver alloc] init];

	return DNSResolver;
# endif
}
#endif

+ (void)sleepForTimeInterval: (OFTimeInterval)timeInterval
{
	if (timeInterval < 0)
		return;

#if defined(OF_WINDOWS)
	if (timeInterval * 1000 > UINT_MAX)
		@throw [OFOutOfRangeException exception];

	Sleep((unsigned int)(timeInterval * 1000));
#elif defined(OF_NINTENDO_3DS)
	if (timeInterval * 1000000000 > INT64_MAX)
		@throw [OFOutOfRangeException exception];

	svcSleepThread((int64_t)(timeInterval * 1000000000));
#elif defined(OF_AMIGAOS)
	struct timerequest request = *DOSBase->dl_TimeReq;

	request.tr_node.io_Message.mn_ReplyPort =
	    &((struct Process *)FindTask(NULL))->pr_MsgPort;
	request.tr_node.io_Command = TR_ADDREQUEST;
	request.tr_time.tv_secs = (ULONG)timeInterval;
	request.tr_time.tv_micro = (ULONG)
	    ((timeInterval - (unsigned int)timeInterval) * 1000000);

	DoIO((struct IORequest *)&request);
#elif defined(HAVE_NANOSLEEP)
	struct timespec rqtp;

	rqtp.tv_sec = (time_t)timeInterval;
	rqtp.tv_nsec = (long)((timeInterval - rqtp.tv_sec) * 1000000000);

	if (rqtp.tv_sec != trunc(timeInterval))
		@throw [OFOutOfRangeException exception];

	nanosleep(&rqtp, NULL);





#elif defined(OF_NINTENDO_DS)
	uint64_t counter;

	if (timeInterval > UINT64_MAX / 60)
		@throw [OFOutOfRangeException exception];

	counter = timeInterval * 60;
	while (counter--)
		swiWaitForVBlank();
#else
	if (timeInterval > UINT_MAX)
		@throw [OFOutOfRangeException exception];

	sleep((unsigned int)timeInterval);
	usleep((unsigned int)
	    ((timeInterval - (unsigned int)timeInterval) * 1000000));
#endif
}

+ (void)sleepUntilDate: (OFDate *)date
{
	[self sleepForTimeInterval: date.timeIntervalSinceNow];
}
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
	 * returns while being declared OF_NO_RETURN.
	 */
	OF_UNREACHABLE
}

+ (void)terminateWithObject: (id)object
{
	OFThread *thread = of_tlskey_get(threadSelfKey);

	if (thread == mainThread)
		@throw [OFInvalidArgumentException exception];

	OF_ENSURE(thread != nil);

	thread->_returnValue = [object retain];
	longjmp(thread->_exitEnv, 1);

	OF_UNREACHABLE
}

+ (void)setName: (OFString *)name
{
	[OFThread currentThread].name = name;

	if (name != nil)
		of_thread_set_name(
		    [name cStringWithEncoding: [OFLocale encoding]]);
	else
		of_thread_set_name(class_getName([self class]));
}

+ (OFString *)name
{
	return [OFThread currentThread].name;
}

+ (void)of_createMainThread
{
	mainThread = [[OFThread alloc] init];
	mainThread->_thread = of_thread_current();
	mainThread->_running = OF_THREAD_RUNNING;

	if (!of_tlskey_set(threadSelfKey, mainThread))
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

- (instancetype)init
{
	self = [super init];

	@try {
		if (!of_thread_attr_init(&_attr))
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

# ifdef OF_HAVE_BLOCKS
- (instancetype)initWithThreadBlock: (of_thread_block_t)threadBlock
{
	self = [self init];

	@try {
		_threadBlock = [threadBlock copy];
	} @catch (id e) {
		[self release];







|




|












|


|










|
|

|









|











|







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
	 * returns while being declared OF_NO_RETURN.
	 */
	OF_UNREACHABLE
}

+ (void)terminateWithObject: (id)object
{
	OFThread *thread = OFTLSKeyGet(threadSelfKey);

	if (thread == mainThread)
		@throw [OFInvalidArgumentException exception];

	OFEnsure(thread != nil);

	thread->_returnValue = [object retain];
	longjmp(thread->_exitEnv, 1);

	OF_UNREACHABLE
}

+ (void)setName: (OFString *)name
{
	[OFThread currentThread].name = name;

	if (name != nil)
		OFSetThreadName(
		    [name cStringWithEncoding: [OFLocale encoding]]);
	else
		OFSetThreadName(class_getName([self class]));
}

+ (OFString *)name
{
	return [OFThread currentThread].name;
}

+ (void)of_createMainThread
{
	mainThread = [[OFThread alloc] init];
	mainThread->_thread = OFCurrentPlainThread();
	mainThread->_running = OFThreadStateRunning;

	if (OFTLSKeySet(threadSelfKey, mainThread) != 0)
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

- (instancetype)init
{
	self = [super init];

	@try {
		if (OFPlainThreadAttributesInit(&_attr) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

# ifdef OF_HAVE_BLOCKS
- (instancetype)initWithThreadBlock: (OFThreadBlock)threadBlock
{
	self = [self init];

	@try {
		_threadBlock = [threadBlock copy];
	} @catch (id e) {
		[self release];
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
	[_DNSResolver release];
	_DNSResolver = nil;
# endif
}

- (void)start
{


	if (_running == OF_THREAD_RUNNING)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	if (_running == OF_THREAD_WAITING_FOR_JOIN) {
		of_thread_detach(_thread);
		[_returnValue release];
	}

	[self retain];

	_running = OF_THREAD_RUNNING;

	if (!of_thread_new(&_thread,
	    [_name cStringWithEncoding: [OFLocale encoding]], callMain, self,
	    &_attr)) {
		[self release];
		@throw [OFThreadStartFailedException
		    exceptionWithThread: self
				  errNo: errno];
	}
}

- (id)join
{


	if (_running == OF_THREAD_NOT_RUNNING)
		@throw [OFThreadJoinFailedException
		    exceptionWithThread: self
				  errNo: EINVAL];

	if (!of_thread_join(_thread))
		@throw [OFThreadJoinFailedException exceptionWithThread: self
								  errNo: errno];

	_running = OF_THREAD_NOT_RUNNING;

	return _returnValue;
}

- (id)copy
{
	return [self retain];
}

- (OFRunLoop *)runLoop
{
# if defined(OF_HAVE_ATOMIC_OPS) && !defined(__clang_analyzer__)
	if (_runLoop == nil) {
		OFRunLoop *tmp = [[OFRunLoop alloc] init];


		if (!of_atomic_ptr_cmpswap((void **)&_runLoop, nil, tmp))
			[tmp release];
	}
# else
	@synchronized (self) {
		if (_runLoop == nil)
			_runLoop = [[OFRunLoop alloc] init];
	}
# endif

	return _runLoop;
}

- (float)priority
{
	return _attr.priority;
}

- (void)setPriority: (float)priority
{
	if (_running == OF_THREAD_RUNNING)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	_attr.priority = priority;
}

- (size_t)stackSize
{
	return _attr.stackSize;
}

- (void)setStackSize: (size_t)stackSize
{
	if (_running == OF_THREAD_RUNNING)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	_attr.stackSize = stackSize;
}

- (bool)supportsSockets
{
	return _supportsSockets;
}

- (void)setSupportsSockets: (bool)supportsSockets
{
	if (_running == OF_THREAD_RUNNING)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	_supportsSockets = supportsSockets;
}

- (void)dealloc
{
	if (_running == OF_THREAD_RUNNING)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	/*
	 * We should not be running anymore, but call detach in order to free
	 * the resources.
	 */
	if (_running == OF_THREAD_WAITING_FOR_JOIN)
		of_thread_detach(_thread);

	[_returnValue release];
# ifdef OF_HAVE_BLOCKS
	[_threadBlock release];
# endif

	[super dealloc];







>
>
|



|
|





|

|
|
<



|





>
>
|




|

|

|















>
|



















|













|













|








|







|
|







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
	[_DNSResolver release];
	_DNSResolver = nil;
# endif
}

- (void)start
{
	int error;

	if (_running == OFThreadStateRunning)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	if (_running == OFThreadStateWaitingForJoin) {
		OFPlainThreadDetach(_thread);
		[_returnValue release];
	}

	[self retain];

	_running = OFThreadStateRunning;

	if ((error = OFPlainThreadNew(&_thread, [_name cStringWithEncoding:
	    [OFLocale encoding]], callMain, self, &_attr)) != 0) {

		[self release];
		@throw [OFThreadStartFailedException
		    exceptionWithThread: self
				  errNo: error];
	}
}

- (id)join
{
	int error;

	if (_running == OFThreadStateNotRunning)
		@throw [OFThreadJoinFailedException
		    exceptionWithThread: self
				  errNo: EINVAL];

	if ((error = OFPlainThreadJoin(_thread)) != 0)
		@throw [OFThreadJoinFailedException exceptionWithThread: self
								  errNo: error];

	_running = OFThreadStateNotRunning;

	return _returnValue;
}

- (id)copy
{
	return [self retain];
}

- (OFRunLoop *)runLoop
{
# if defined(OF_HAVE_ATOMIC_OPS) && !defined(__clang_analyzer__)
	if (_runLoop == nil) {
		OFRunLoop *tmp = [[OFRunLoop alloc] init];

		if (!OFAtomicPointerCompareAndSwap(
		    (void **)&_runLoop, nil, tmp))
			[tmp release];
	}
# else
	@synchronized (self) {
		if (_runLoop == nil)
			_runLoop = [[OFRunLoop alloc] init];
	}
# endif

	return _runLoop;
}

- (float)priority
{
	return _attr.priority;
}

- (void)setPriority: (float)priority
{
	if (_running == OFThreadStateRunning)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	_attr.priority = priority;
}

- (size_t)stackSize
{
	return _attr.stackSize;
}

- (void)setStackSize: (size_t)stackSize
{
	if (_running == OFThreadStateRunning)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	_attr.stackSize = stackSize;
}

- (bool)supportsSockets
{
	return _supportsSockets;
}

- (void)setSupportsSockets: (bool)supportsSockets
{
	if (_running == OFThreadStateRunning)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	_supportsSockets = supportsSockets;
}

- (void)dealloc
{
	if (_running == OFThreadStateRunning)
		@throw [OFThreadStillRunningException
		    exceptionWithThread: self];

	/*
	 * We should not be running anymore, but call detach in order to free
	 * the resources.
	 */
	if (_running == OFThreadStateWaitingForJoin)
		OFPlainThreadDetach(_thread);

	[_returnValue release];
# ifdef OF_HAVE_BLOCKS
	[_threadBlock release];
# endif

	[super dealloc];

Deleted src/OFThreadPool.h version [9825eb68b1].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

OF_ASSUME_NONNULL_BEGIN

/** @file */

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block for a job which should be executed in a thread pool.
 */
typedef void (^of_thread_pool_block_t)(void);
#endif

@class OFCondition;
@class OFList OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFThreadPoolJob;

/**
 * @class OFThreadPool OFThreadPool.h ObjFW/OFThreadPool.h
 *
 * @brief A class providing a pool of reusable threads.
 *
 * @note When the thread pool is released, all threads will terminate after
 *	 they finish the job they are currently processing.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFThreadPool: OFObject
{
	size_t _size;
	OFMutableArray *_threads;
	volatile int _count;
#ifdef OF_THREAD_POOL_M
@public
#endif
	OFList *_queue;
	OFCondition *_queueCondition;
	volatile int _doneCount;
	OFCondition *_countCondition;
}

/**
 * @brief The size of the thread pool.
 */
@property (readonly, nonatomic) size_t size;

/**
 * @brief Returns a new thread pool with one thread for each core in the system.
 *
 * @warning If for some reason the number of cores in the system could not be
 *	    determined, the pool will only have one thread!
 *
 * @return A new thread pool with one thread for each core in the system
 */
+ (instancetype)threadPool;

/**
 * @brief Returns a new thread pool with the specified number of threads.
 *
 * @param size The number of threads for the pool
 * @return A new thread pool with the specified number of threads
 */
+ (instancetype)threadPoolWithSize: (size_t)size;

/**
 * @brief Initializes an already allocated OFThreadPool with the specified
 *	  number of threads.
 *
 * @param size The number of threads for the pool
 * @return An initialized OFThreadPool with the specified number of threads
 */
- (instancetype)initWithSize: (size_t)size OF_DESIGNATED_INITIALIZER;

/**
 * @brief Execute the specified selector on the specified target with the
 *	  specified object as soon as a thread is ready.
 *
 * @param target The target on which to perform the selector
 * @param selector The selector to perform on the target
 * @param object The object with which the selector is performed on the target
 */
- (void)dispatchWithTarget: (id)target
		  selector: (SEL)selector
		    object: (nullable id)object;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Executes the specified block as soon as a thread is ready.
 *
 * @param block The block to execute
 */
- (void)dispatchWithBlock: (of_thread_pool_block_t)block;
#endif

/**
 * @brief Waits until all jobs are done.
 */
- (void)waitUntilDone;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































































































































































































Deleted src/OFThreadPool.m version [29bc99edba].

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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#define OF_THREAD_POOL_M

#import "OFThreadPool.h"
#import "OFArray.h"
#import "OFList.h"
#import "OFThread.h"
#import "OFCondition.h"
#import "OFSystemInfo.h"

OF_DIRECT_MEMBERS
@interface OFThreadPoolJob: OFObject
{
	id _target;
	SEL _selector;
	id _object;
#ifdef OF_HAVE_BLOCKS
	of_thread_pool_block_t _block;
#endif
}

- (instancetype)initWithTarget: (id)target
		      selector: (SEL)selector
			object: (id)object;
#ifdef OF_HAVE_BLOCKS
- (instancetype)initWithBlock: (of_thread_pool_block_t)block;
#endif
- (void)perform;
@end

@implementation OFThreadPoolJob
- (instancetype)initWithTarget: (id)target
		      selector: (SEL)selector
			object: (id)object
{
	self = [super init];

	@try {
		_target = [target retain];
		_selector = selector;
		_object = [object retain];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

#ifdef OF_HAVE_BLOCKS
- (instancetype)initWithBlock: (of_thread_pool_block_t)block
{
	self = [super init];

	@try {
		_block = [block copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
#endif

- (void)dealloc
{
	[_target release];
	[_object release];
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif

	[super dealloc];
}

- (void)perform
{
#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block();
	else
#endif
		[_target performSelector: _selector
			      withObject: _object];
}
@end

OF_DIRECT_MEMBERS
@interface OFThreadPoolThread: OFThread
{
	OFList *_queue;
	OFCondition *_queueCondition, *_countCondition;
@public
	volatile bool _terminate;
	volatile int *_doneCount;
}

+ (instancetype)threadWithThreadPool: (OFThreadPool *)threadPool;
- (instancetype)initWithThreadPool: (OFThreadPool *)threadPool;
@end

@implementation OFThreadPoolThread
+ (instancetype)threadWithThreadPool: (OFThreadPool *)threadPool
{
	return [[(OFThreadPoolThread *)[self alloc]
	    initWithThreadPool: threadPool] autorelease];
}

- (instancetype)initWithThreadPool: (OFThreadPool *)threadPool
{
	self = [super init];

	@try {
		_queue = [threadPool->_queue retain];
		_queueCondition = [threadPool->_queueCondition retain];
		_countCondition = [threadPool->_countCondition retain];
		_doneCount = &threadPool->_doneCount;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_queue release];
	[_queueCondition release];
	[_countCondition release];

	[super dealloc];
}

- (id)main
{
	void *pool;

	if (_terminate)
		return nil;

	pool = objc_autoreleasePoolPush();

	for (;;) {
		OFThreadPoolJob *job;

		[_queueCondition lock];
		@try {
			of_list_object_t *listObject;

			if (_terminate) {
				objc_autoreleasePoolPop(pool);
				return nil;
			}

			listObject = _queue.firstListObject;

			while (listObject == NULL) {
				[_queueCondition wait];

				if (_terminate) {
					objc_autoreleasePoolPop(pool);
					return nil;
				}

				listObject = _queue.firstListObject;
			}

			job = [[listObject->object retain] autorelease];
			[_queue removeListObject: listObject];
		} @finally {
			[_queueCondition unlock];
		}

		if (_terminate) {
			objc_autoreleasePoolPop(pool);
			return nil;
		}

		[job perform];

		if (_terminate) {
			objc_autoreleasePoolPop(pool);
			return nil;
		}

		objc_autoreleasePoolPop(pool);
		pool = objc_autoreleasePoolPush();

		[_countCondition lock];
		@try {
			if (_terminate) {
				objc_autoreleasePoolPop(pool);
				return nil;
			}

			(*_doneCount)++;

			[_countCondition signal];
		} @finally {
			[_countCondition unlock];
		}
	}
}
@end

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

+ (instancetype)threadPoolWithSize: (size_t)size
{
	return [[[self alloc] initWithSize: size] autorelease];
}

- (instancetype)init
{
	return [self initWithSize: [OFSystemInfo numberOfCPUs]];
}

- (instancetype)initWithSize: (size_t)size
{
	self = [super init];

	@try {
		_size = size;
		_threads = [[OFMutableArray alloc] init];
		_queue = [[OFList alloc] init];
		_queueCondition = [[OFCondition alloc] init];
		_countCondition = [[OFCondition alloc] init];

		for (size_t i = 0; i < size; i++) {
			void *pool = objc_autoreleasePoolPush();

			OFThreadPoolThread *thread =
			    [OFThreadPoolThread threadWithThreadPool: self];

			[_threads addObject: thread];

			objc_autoreleasePoolPop(pool);
		}

		/*
		 * We need to start the threads in a separate loop to make sure
		 * _threads is not modified anymore to prevent a race condition.
		 */
		for (size_t i = 0; i < size; i++)
			[[_threads objectAtIndex: i] start];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_queueCondition lock];
	@try {
		[_countCondition lock];
		@try {
			for (OFThreadPoolThread *thread in _threads)
				thread->_terminate = true;
		} @finally {
			[_countCondition unlock];
		}

		[_queueCondition broadcast];
	} @finally {
		[_queueCondition unlock];
	}

	[_threads release];
	[_queue release];
	[_queueCondition release];
	[_countCondition release];

	[super dealloc];
}

- (void)of_dispatchJob: (OFThreadPoolJob *)job OF_DIRECT
{
	[_countCondition lock];
	_count++;
	[_countCondition unlock];

	[_queueCondition lock];
	@try {
		[_queue appendObject: job];
		[_queueCondition signal];
	} @finally {
		[_queueCondition unlock];
	}
}

- (void)waitUntilDone
{
	for (;;) {
		[_countCondition lock];
		@try {
			if (_doneCount == _count)
				return;

			[_countCondition wait];
		} @finally {
			[_countCondition unlock];
		}
	}
}

- (void)dispatchWithTarget: (id)target
		  selector: (SEL)selector
		    object: (id)object
{
	OFThreadPoolJob *job = [[OFThreadPoolJob alloc] initWithTarget: target
							      selector: selector
								object: object];
	@try {
		[self of_dispatchJob: job];
	} @finally {
		[job release];
	}
}

#ifdef OF_HAVE_BLOCKS
- (void)dispatchWithBlock: (of_thread_pool_block_t)block
{
	OFThreadPoolJob *job = [[OFThreadPoolJob alloc] initWithBlock: block];
	@try {
		[self of_dispatchJob: job];
	} @finally {
		[job release];
	}
}
#endif

- (size_t)size
{
	return _size;
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































































































































































































































































































































































































































































Modified src/OFTimer+Private.h from [3bdcab087f] to [37a8d06411].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFTimer.h"

OF_ASSUME_NONNULL_BEGIN

OF_DIRECT_MEMBERS
@interface OFTimer ()
- (void)of_setInRunLoop: (nullable OFRunLoop *)runLoop
		   mode: (nullable of_run_loop_mode_t)mode;
@end

OF_ASSUME_NONNULL_END

<
<
|




















|



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


 * 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 "OFTimer.h"

OF_ASSUME_NONNULL_BEGIN

OF_DIRECT_MEMBERS
@interface OFTimer ()
- (void)of_setInRunLoop: (nullable OFRunLoop *)runLoop
		   mode: (nullable OFRunLoopMode)mode;
@end

OF_ASSUME_NONNULL_END

Modified src/OFTimer.h from [e351b12ba6] to [bedc280158].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block to execute when a timer fires.
 *
 * @param timer The timer which fired
 */
typedef void (^of_timer_block_t)(OFTimer *timer);
#endif

/**
 * @class OFTimer OFTimer.h ObjFW/OFTimer.h
 *
 * @brief A class for creating and firing timers.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFTimer: OFObject <OFComparing>
{
	OFDate *_fireDate;
	of_time_interval_t _interval;
	id _target;
	id _Nullable _object1, _object2, _object3, _object4;
	SEL _selector;
	unsigned char _arguments;
	bool _repeats;
#ifdef OF_HAVE_BLOCKS
	of_timer_block_t _block;
#endif
	bool _valid;
#ifdef OF_HAVE_THREADS
	OFCondition *_condition;
	bool _done;
#endif
	OFRunLoop *_Nullable _inRunLoop;
	of_run_loop_mode_t _Nullable _inRunLoopMode;
}

/**
 * @brief The time interval in which the timer will repeat, if it is a
 *	  repeating timer.
 */
@property (readonly, nonatomic) of_time_interval_t timeInterval;

/**
 * @brief Whether the timer is repeating.
 */
@property (readonly, nonatomic, getter=isRepeating) bool repeating;

/**
 * @brief Whether the timer is valid.
 */
@property (readonly, nonatomic, getter=isValid) bool valid;

/**







|











|






|







|






|


|

|







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

#ifdef OF_HAVE_BLOCKS
/**
 * @brief A block to execute when a timer fires.
 *
 * @param timer The timer which fired
 */
typedef void (^OFTimerBlock)(OFTimer *timer);
#endif

/**
 * @class OFTimer OFTimer.h ObjFW/OFTimer.h
 *
 * @brief A class for creating and firing timers.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFTimer: OFObject <OFComparing>
{
	OFDate *_fireDate;
	OFTimeInterval _interval;
	id _target;
	id _Nullable _object1, _object2, _object3, _object4;
	SEL _selector;
	unsigned char _arguments;
	bool _repeats;
#ifdef OF_HAVE_BLOCKS
	OFTimerBlock _block;
#endif
	bool _valid;
#ifdef OF_HAVE_THREADS
	OFCondition *_condition;
	bool _done;
#endif
	OFRunLoop *_Nullable _inRunLoop;
	OFRunLoopMode _Nullable _inRunLoopMode;
}

/**
 * @brief The time interval in which the timer will repeat, if it is a
 *	  repeating timer.
 */
@property (readonly, nonatomic) OFTimeInterval timeInterval;

/**
 * @brief Whether the timer repeats.
 */
@property (readonly, nonatomic) bool repeats;

/**
 * @brief Whether the timer is valid.
 */
@property (readonly, nonatomic, getter=isValid) bool valid;

/**
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
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
				       repeats: (bool)repeats;

/**
 * @brief Creates and schedules a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object An object to pass when calling the selector on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (nullable id)object
				       repeats: (bool)repeats;

/**
 * @brief Creates and schedules a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object1 The first object to pass when calling the selector on the
 *		  target
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (nullable id)object1
					object: (nullable id)object2
				       repeats: (bool)repeats;

/**







|














|


















|







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
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
				       repeats: (bool)repeats;

/**
 * @brief Creates and schedules a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object An object to pass when calling the selector on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (nullable id)object
				       repeats: (bool)repeats;

/**
 * @brief Creates and schedules a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object1 The first object to pass when calling the selector on the
 *		  target
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (nullable id)object1
					object: (nullable id)object2
				       repeats: (bool)repeats;

/**
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (nullable id)object1
					object: (nullable id)object2
					object: (nullable id)object3
				       repeats: (bool)repeats;








|







149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (nullable id)object1
					object: (nullable id)object2
					object: (nullable id)object3
				       repeats: (bool)repeats;

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
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param object4 The fourth object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (nullable id)object1
					object: (nullable id)object2
					object: (nullable id)object3
					object: (nullable id)object4
				       repeats: (bool)repeats;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Creates and schedules a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param repeats Whether the timer repeats after it has been executed
 * @param block The block to invoke when the timer fires
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
				       repeats: (bool)repeats
					 block: (of_timer_block_t)block;
#endif

/**
 * @brief Creates a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			      repeats: (bool)repeats;

/**
 * @brief Creates a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object An object to pass when calling the selector on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (nullable id)object
			      repeats: (bool)repeats;

/**
 * @brief Creates a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object1 The first object to pass when calling the selector on the
 *		  target
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (nullable id)object1
			       object: (nullable id)object2
			      repeats: (bool)repeats;

/**







|

















|

|











|














|


















|







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
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param object4 The fourth object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (nullable id)object1
					object: (nullable id)object2
					object: (nullable id)object3
					object: (nullable id)object4
				       repeats: (bool)repeats;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Creates and schedules a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param repeats Whether the timer repeats after it has been executed
 * @param block The block to invoke when the timer fires
 * @return A new, autoreleased timer
 */
+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
				       repeats: (bool)repeats
					 block: (OFTimerBlock)block;
#endif

/**
 * @brief Creates a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			      repeats: (bool)repeats;

/**
 * @brief Creates a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object An object to pass when calling the selector on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (nullable id)object
			      repeats: (bool)repeats;

/**
 * @brief Creates a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object1 The first object to pass when calling the selector on the
 *		  target
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (nullable id)object1
			       object: (nullable id)object2
			      repeats: (bool)repeats;

/**
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (nullable id)object1
			       object: (nullable id)object2
			       object: (nullable id)object3
			      repeats: (bool)repeats;








|







262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (nullable id)object1
			       object: (nullable id)object2
			       object: (nullable id)object3
			      repeats: (bool)repeats;

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
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param object4 The fourth object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (nullable id)object1
			       object: (nullable id)object2
			       object: (nullable id)object3
			       object: (nullable id)object4
			      repeats: (bool)repeats;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Creates a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param repeats Whether the timer repeats after it has been executed
 * @param block The block to invoke when the timer fires
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			      repeats: (bool)repeats
				block: (of_timer_block_t)block;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated timer with the specified time
 *	  interval.
 *
 * @param fireDate The date at which the timer should fire
 * @param interval The time interval after which to repeat the timer, if it is
 *		   a repeating timer
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			 repeats: (bool)repeats;

/**
 * @brief Initializes an already allocated timer with the specified time
 *	  interval.
 *
 * @param fireDate The date at which the timer should fire
 * @param interval The time interval after which to repeat the timer, if it is
 *		   a repeating timer
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object An object to pass when calling the selector on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (nullable id)object
			 repeats: (bool)repeats;

/**
 * @brief Initializes an already allocated timer with the specified time







|

















|

|

















|


















|







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
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param object4 The fourth object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (nullable id)object1
			       object: (nullable id)object2
			       object: (nullable id)object3
			       object: (nullable id)object4
			      repeats: (bool)repeats;

#ifdef OF_HAVE_BLOCKS
/**
 * @brief Creates a new timer with the specified time interval.
 *
 * @param timeInterval The time interval after which the timer should be fired
 * @param repeats Whether the timer repeats after it has been executed
 * @param block The block to invoke when the timer fires
 * @return A new, autoreleased timer
 */
+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			      repeats: (bool)repeats
				block: (OFTimerBlock)block;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated timer with the specified time
 *	  interval.
 *
 * @param fireDate The date at which the timer should fire
 * @param interval The time interval after which to repeat the timer, if it is
 *		   a repeating timer
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			 repeats: (bool)repeats;

/**
 * @brief Initializes an already allocated timer with the specified time
 *	  interval.
 *
 * @param fireDate The date at which the timer should fire
 * @param interval The time interval after which to repeat the timer, if it is
 *		   a repeating timer
 * @param target The target on which to call the selector
 * @param selector The selector to call on the target
 * @param object An object to pass when calling the selector on the target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (nullable id)object
			 repeats: (bool)repeats;

/**
 * @brief Initializes an already allocated timer with the specified time
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
 *		  target
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (nullable id)object1
			  object: (nullable id)object2
			 repeats: (bool)repeats;

/**







|







367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
 *		  target
 * @param object2 The second object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (nullable id)object1
			  object: (nullable id)object2
			 repeats: (bool)repeats;

/**
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
 *		  target
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (nullable id)object1
			  object: (nullable id)object2
			  object: (nullable id)object3
			 repeats: (bool)repeats;








|







393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
 *		  target
 * @param object3 The third object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (nullable id)object1
			  object: (nullable id)object2
			  object: (nullable id)object3
			 repeats: (bool)repeats;

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
 *		  target
 * @param object4 The fourth object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (nullable id)object1
			  object: (nullable id)object2
			  object: (nullable id)object3
			  object: (nullable id)object4
			 repeats: (bool)repeats;







|







422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
 *		  target
 * @param object4 The fourth object to pass when calling the selector on the
 *		  target
 * @param repeats Whether the timer repeats after it has been executed
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (nullable id)object1
			  object: (nullable id)object2
			  object: (nullable id)object3
			  object: (nullable id)object4
			 repeats: (bool)repeats;
446
447
448
449
450
451
452
453
454
455
456
457








458
459
460
461
462
463
464
 * @param interval The time interval after which to repeat the timer, if it is
 *		   a repeating timer
 * @param repeats Whether the timer repeats after it has been executed
 * @param block The block to invoke when the timer fires
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			 repeats: (bool)repeats
			   block: (of_timer_block_t)block;
#endif









/**
 * @brief Fires the timer, meaning it will execute the specified selector on the
 *	  target.
 */
- (void)fire;

/**







|

|


>
>
>
>
>
>
>
>







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
 * @param interval The time interval after which to repeat the timer, if it is
 *		   a repeating timer
 * @param repeats Whether the timer repeats after it has been executed
 * @param block The block to invoke when the timer fires
 * @return An initialized timer
 */
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			 repeats: (bool)repeats
			   block: (OFTimerBlock)block;
#endif

/**
 * @brief Compares the timer to another timer.
 *
 * @param timer The timer to compare the string to
 * @return The result of the comparison
 */
- (OFComparisonResult)compare: (OFTimer *)timer;

/**
 * @brief Fires the timer, meaning it will execute the specified selector on the
 *	  target.
 */
- (void)fire;

/**

Modified src/OFTimer.m from [b1d045935e] to [a9abf62d66].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#ifdef OF_HAVE_THREADS
# import "OFCondition.h"
#endif

#import "OFInvalidArgumentException.h"

@implementation OFTimer
@synthesize timeInterval = _interval, repeating = _repeats, valid = _valid;

+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
				       repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					    target: target
					  selector: selector
					   repeats: repeats] autorelease];

	[[OFRunLoop currentRunLoop] addTimer: timer];

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (id)object
				       repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];







|

|




















|







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
#ifdef OF_HAVE_THREADS
# import "OFCondition.h"
#endif

#import "OFInvalidArgumentException.h"

@implementation OFTimer
@synthesize timeInterval = _interval, repeats = _repeats, valid = _valid;

+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
				       repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					    target: target
					  selector: selector
					   repeats: repeats] autorelease];

	[[OFRunLoop currentRunLoop] addTimer: timer];

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (id)object
				       repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (id)object1
					object: (id)object2
				       repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();







|







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

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (id)object1
					object: (id)object2
				       repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (id)object1
					object: (id)object2
					object: (id)object3
				       repeats: (bool)repeats
{







|







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (id)object1
					object: (id)object2
					object: (id)object3
				       repeats: (bool)repeats
{
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (id)object1
					object: (id)object2
					object: (id)object3
					object: (id)object4
				       repeats: (bool)repeats







|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
					target: (id)target
				      selector: (SEL)selector
					object: (id)object1
					object: (id)object2
					object: (id)object3
					object: (id)object4
				       repeats: (bool)repeats
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
	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

#ifdef OF_HAVE_BLOCKS
+ (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval
				       repeats: (bool)repeats
					 block: (of_timer_block_t)block
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					   repeats: repeats
					     block: block] autorelease];

	[[OFRunLoop currentRunLoop] addTimer: timer];

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}
#endif

+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			      repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					    target: target
					  selector: selector
					   repeats: repeats] autorelease];

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (id)object
			      repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					    target: target
					  selector: selector
					    object: object
					   repeats: repeats] autorelease];

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (id)object1
			       object: (id)object2
			      repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();







|

|

















|


















|




















|







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
	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

#ifdef OF_HAVE_BLOCKS
+ (instancetype)scheduledTimerWithTimeInterval: (OFTimeInterval)timeInterval
				       repeats: (bool)repeats
					 block: (OFTimerBlock)block
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					   repeats: repeats
					     block: block] autorelease];

	[[OFRunLoop currentRunLoop] addTimer: timer];

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}
#endif

+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			      repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					    target: target
					  selector: selector
					   repeats: repeats] autorelease];

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (id)object
			      repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					    target: target
					  selector: selector
					    object: object
					   repeats: repeats] autorelease];

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (id)object1
			       object: (id)object2
			      repeats: (bool)repeats
{
	void *pool = objc_autoreleasePoolPush();
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (id)object1
			       object: (id)object2
			       object: (id)object3
			      repeats: (bool)repeats
{







|







238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (id)object1
			       object: (id)object2
			       object: (id)object3
			      repeats: (bool)repeats
{
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (id)object1
			       object: (id)object2
			       object: (id)object3
			       object: (id)object4
			      repeats: (bool)repeats







|







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			       target: (id)target
			     selector: (SEL)selector
			       object: (id)object1
			       object: (id)object2
			       object: (id)object3
			       object: (id)object4
			      repeats: (bool)repeats
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

#ifdef OF_HAVE_BLOCKS
+ (instancetype)timerWithTimeInterval: (of_time_interval_t)timeInterval
			      repeats: (bool)repeats
				block: (of_timer_block_t)block
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					   repeats: repeats
					     block: block] autorelease];







|

|







291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
	[timer retain];
	objc_autoreleasePoolPop(pool);

	return [timer autorelease];
}

#ifdef OF_HAVE_BLOCKS
+ (instancetype)timerWithTimeInterval: (OFTimeInterval)timeInterval
			      repeats: (bool)repeats
				block: (OFTimerBlock)block
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: timeInterval];
	id timer = [[[self alloc] initWithFireDate: fireDate
					  interval: timeInterval
					   repeats: repeats
					     block: block] autorelease];
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithFireDate: (OFDate *)fireDate
			   interval: (of_time_interval_t)interval
			     target: (id)target
			   selector: (SEL)selector
			     object: (id)object1
			     object: (id)object2
			     object: (id)object3
			     object: (id)object4
			  arguments: (unsigned char)arguments







|







315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithFireDate: (OFDate *)fireDate
			   interval: (OFTimeInterval)interval
			     target: (id)target
			   selector: (SEL)selector
			     object: (id)object1
			     object: (id)object2
			     object: (id)object3
			     object: (id)object4
			  arguments: (unsigned char)arguments
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
		@throw e;
	}

	return self;
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			 repeats: (bool)repeats
{
	return [self of_initWithFireDate: fireDate
				interval: interval
				  target: target
				selector: selector
				  object: nil
				  object: nil
				  object: nil
				  object: nil
			       arguments: 0
				 repeats: repeats];
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (id)object
			 repeats: (bool)repeats
{
	return [self of_initWithFireDate: fireDate
				interval: interval
				  target: target
				selector: selector
				  object: object
				  object: nil
				  object: nil
				  object: nil
			       arguments: 1
				 repeats: repeats];
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (id)object1
			  object: (id)object2
			 repeats: (bool)repeats
{
	return [self of_initWithFireDate: fireDate
				interval: interval
				  target: target
				selector: selector
				  object: object1
				  object: object2
				  object: nil
				  object: nil
			       arguments: 2
				 repeats: repeats];
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (id)object1
			  object: (id)object2
			  object: (id)object3
			 repeats: (bool)repeats
{
	return [self of_initWithFireDate: fireDate
				interval: interval
				  target: target
				selector: selector
				  object: object1
				  object: object2
				  object: object3
				  object: nil
			       arguments: 3
				 repeats: repeats];
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (id)object1
			  object: (id)object2
			  object: (id)object3
			  object: (id)object4
			 repeats: (bool)repeats







|

















|


















|



















|




















|







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
		@throw e;
	}

	return self;
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			 repeats: (bool)repeats
{
	return [self of_initWithFireDate: fireDate
				interval: interval
				  target: target
				selector: selector
				  object: nil
				  object: nil
				  object: nil
				  object: nil
			       arguments: 0
				 repeats: repeats];
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (id)object
			 repeats: (bool)repeats
{
	return [self of_initWithFireDate: fireDate
				interval: interval
				  target: target
				selector: selector
				  object: object
				  object: nil
				  object: nil
				  object: nil
			       arguments: 1
				 repeats: repeats];
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (id)object1
			  object: (id)object2
			 repeats: (bool)repeats
{
	return [self of_initWithFireDate: fireDate
				interval: interval
				  target: target
				selector: selector
				  object: object1
				  object: object2
				  object: nil
				  object: nil
			       arguments: 2
				 repeats: repeats];
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (id)object1
			  object: (id)object2
			  object: (id)object3
			 repeats: (bool)repeats
{
	return [self of_initWithFireDate: fireDate
				interval: interval
				  target: target
				selector: selector
				  object: object1
				  object: object2
				  object: object3
				  object: nil
			       arguments: 3
				 repeats: repeats];
}

- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			  target: (id)target
			selector: (SEL)selector
			  object: (id)object1
			  object: (id)object2
			  object: (id)object3
			  object: (id)object4
			 repeats: (bool)repeats
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
				  object: object4
			       arguments: 4
				 repeats: repeats];
}

#ifdef OF_HAVE_BLOCKS
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (of_time_interval_t)interval
			 repeats: (bool)repeats
			   block: (of_timer_block_t)block
{
	self = [super init];

	@try {
		_fireDate = [fireDate retain];
		_interval = interval;
		_repeats = repeats;







|

|







453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
				  object: object4
			       arguments: 4
				 repeats: repeats];
}

#ifdef OF_HAVE_BLOCKS
- (instancetype)initWithFireDate: (OFDate *)fireDate
			interval: (OFTimeInterval)interval
			 repeats: (bool)repeats
			   block: (OFTimerBlock)block
{
	self = [super init];

	@try {
		_fireDate = [fireDate retain];
		_interval = interval;
		_repeats = repeats;
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
#ifdef OF_HAVE_THREADS
	[_condition release];
#endif

	[super dealloc];
}

- (of_comparison_result_t)compare: (id <OFComparing>)object
{
	OFTimer *timer;

	if (![(id)object isKindOfClass: [OFTimer class]])
		@throw [OFInvalidArgumentException exception];

	timer = (OFTimer *)object;

	return [_fireDate compare: timer->_fireDate];
}

- (void)of_setInRunLoop: (OFRunLoop *)runLoop
		   mode: (of_run_loop_mode_t)mode
{
	OFRunLoop *oldInRunLoop = _inRunLoop;
	of_run_loop_mode_t oldInRunLoopMode = _inRunLoopMode;

	_inRunLoop = [runLoop retain];
	[oldInRunLoop release];

	_inRunLoopMode = [mode copy];
	[oldInRunLoopMode release];
}

- (void)fire
{
	void *pool = objc_autoreleasePoolPush();
	id target = [[_target retain] autorelease];
	id object1 = [[_object1 retain] autorelease];
	id object2 = [[_object2 retain] autorelease];
	id object3 = [[_object3 retain] autorelease];
	id object4 = [[_object4 retain] autorelease];

	OF_ENSURE(_arguments <= 4);

	if (_repeats && _valid) {
		int64_t missedIntervals =
		    -_fireDate.timeIntervalSinceNow / _interval;
		of_time_interval_t newFireDate;
		OFRunLoop *runLoop;

		/* In case the clock was changed backwards */
		if (missedIntervals < 0)
			missedIntervals = 0;

		newFireDate = _fireDate.timeIntervalSince1970 +
		    (missedIntervals + 1) * _interval;

		[_fireDate release];
		_fireDate = [[OFDate alloc]
		    initWithTimeIntervalSince1970: newFireDate];

		runLoop = [OFRunLoop currentRunLoop];
		[runLoop addTimer: self
			  forMode: runLoop.currentMode];
	} else
		[self invalidate];

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(self);
	else {
#endif
		switch (_arguments) {
		case 0:
			[target performSelector: _selector];
			break;
		case 1:
			[target performSelector: _selector
				     withObject: object1];
			break;
		case 2:
			[target performSelector: _selector
				     withObject: object1
				     withObject: object2];
			break;
		case 3:







<
<
|
|
|

<
<




|
<


|

















|




|














|
<













|
<







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
#ifdef OF_HAVE_THREADS
	[_condition release];
#endif

	[super dealloc];
}



- (OFComparisonResult)compare: (OFTimer *)timer
{
	if (![timer isKindOfClass: [OFTimer class]])
		@throw [OFInvalidArgumentException exception];



	return [_fireDate compare: timer->_fireDate];
}

- (void)of_setInRunLoop: (OFRunLoop *)runLoop mode: (OFRunLoopMode)mode

{
	OFRunLoop *oldInRunLoop = _inRunLoop;
	OFRunLoopMode oldInRunLoopMode = _inRunLoopMode;

	_inRunLoop = [runLoop retain];
	[oldInRunLoop release];

	_inRunLoopMode = [mode copy];
	[oldInRunLoopMode release];
}

- (void)fire
{
	void *pool = objc_autoreleasePoolPush();
	id target = [[_target retain] autorelease];
	id object1 = [[_object1 retain] autorelease];
	id object2 = [[_object2 retain] autorelease];
	id object3 = [[_object3 retain] autorelease];
	id object4 = [[_object4 retain] autorelease];

	OFEnsure(_arguments <= 4);

	if (_repeats && _valid) {
		int64_t missedIntervals =
		    -_fireDate.timeIntervalSinceNow / _interval;
		OFTimeInterval newFireDate;
		OFRunLoop *runLoop;

		/* In case the clock was changed backwards */
		if (missedIntervals < 0)
			missedIntervals = 0;

		newFireDate = _fireDate.timeIntervalSince1970 +
		    (missedIntervals + 1) * _interval;

		[_fireDate release];
		_fireDate = [[OFDate alloc]
		    initWithTimeIntervalSince1970: newFireDate];

		runLoop = [OFRunLoop currentRunLoop];
		[runLoop addTimer: self forMode: runLoop.currentMode];

	} else
		[self invalidate];

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(self);
	else {
#endif
		switch (_arguments) {
		case 0:
			[target performSelector: _selector];
			break;
		case 1:
			[target performSelector: _selector withObject: object1];

			break;
		case 2:
			[target performSelector: _selector
				     withObject: object1
				     withObject: object2];
			break;
		case 3:
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
			[_inRunLoop of_removeTimer: self
					   forMode: _inRunLoopMode];

			old = _fireDate;
			_fireDate = [fireDate copy];
			[old release];

			[_inRunLoop addTimer: self
				     forMode: _inRunLoopMode];
		}
	} @finally {
		[self release];
	}
}

- (void)invalidate







|
<







622
623
624
625
626
627
628
629

630
631
632
633
634
635
636
			[_inRunLoop of_removeTimer: self
					   forMode: _inRunLoopMode];

			old = _fireDate;
			_fireDate = [fireDate copy];
			[old release];

			[_inRunLoop addTimer: self forMode: _inRunLoopMode];

		}
	} @finally {
		[self release];
	}
}

- (void)invalidate
672
673
674
675
676
677
678





























679

		[_condition wait];
	} @finally {
		[_condition unlock];
	}
}
#endif





























@end







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

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

		[_condition wait];
	} @finally {
		[_condition unlock];
	}
}
#endif

- (OFString *)description
{
#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return [OFString stringWithFormat:
		    @"<%@:\n"
		    @"\tFire date: %@\n"
		    @"\tInterval: %lf\n"
		    @"\tRepeats: %s\n"
		    @"\tBlock: %@\n"
		    @"\tValid: %s\n"
		    @">",
		    self.class, _fireDate, _interval, (_repeats ? "yes" : "no"),
		    _block, (_valid ? "yes" : "no")];
	else
#endif
		return [OFString stringWithFormat:
		    @"<%@:\n"
		    @"\tFire date: %@\n"
		    @"\tInterval: %lf\n"
		    @"\tRepeats: %s\n"
		    @"\tTarget: %@\n"
		    @"\tSelector: %s\n"
		    @"\tValid: %s\n"
		    @">",
		    self.class, _fireDate, _interval, (_repeats ? "yes" : "no"),
		    _target, sel_getName(_selector), (_valid ? "yes" : "no")];
}
@end

Modified src/OFTriple.h from [cbb4c88048] to [190f8c3dbc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFTriple.m from [9a522e8d33] to [51ba3c68c4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, [_firstObject hash]);
	OF_HASH_ADD_HASH(hash, [_secondObject hash]);
	OF_HASH_ADD_HASH(hash, [_thirdObject hash]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
	return [self retain];







|

|

|
|
|

|







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, [_firstObject hash]);
	OFHashAddHash(&hash, [_secondObject hash]);
	OFHashAddHash(&hash, [_thirdObject hash]);

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{
	return [self retain];

Modified src/OFUDPSocket+Private.h from [bc60d66b62] to [4a674f5731].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFUDPSocket.h"

OF_ASSUME_NONNULL_BEGIN

OF_DIRECT_MEMBERS
@interface OFUDPSocket ()
- (uint16_t)of_bindToAddress: (of_socket_address_t *)address
		   extraType: (int)extraType;
@end

OF_ASSUME_NONNULL_END

<
<
|



















|




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


 * 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 "OFUDPSocket.h"

OF_ASSUME_NONNULL_BEGIN

OF_DIRECT_MEMBERS
@interface OFUDPSocket ()
- (uint16_t)of_bindToAddress: (OFSocketAddress *)address
		   extraType: (int)extraType;
@end

OF_ASSUME_NONNULL_END

Modified src/OFUDPSocket.h from [58fc97b11e] to [3a21c440bc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
@end

/**
 * @class OFUDPSocket OFUDPSocket.h ObjFW/OFUDPSocket.h
 *
 * @brief A class which provides methods to create and use UDP sockets.
 *
 * Addresses are of type @ref of_socket_address_t. You can use the current
 * thread's @ref OFDNSResolver to create an address for a host / port pair and
 * @ref of_socket_address_ip_string to get the IP string / port pair for an

 * address. If you want to compare two addresses, you can use @ref
 * of_socket_address_equal and you can use @ref of_socket_address_hash to get a
 * hash to use in e.g. @ref OFMapTable.
 *
 * @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







|
|
|
>
|
|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@end

/**
 * @class OFUDPSocket OFUDPSocket.h ObjFW/OFUDPSocket.h
 *
 * @brief A class which provides methods to create and use UDP sockets.
 *
 * Addresses are of type @ref OFSocketAddress. You can use the current thread's
 * @ref OFDNSResolver to create an address for a host / port pair,
 * @ref OFSocketAddressString to get the IP address string for an address and
 * @ref OFSocketAddressPort to get the port for an address. If you want to
 * compare two addresses, you can use
 * @ref OFSocketAddressEqual and you can use @ref OFSocketAddressHash to get a
 * hash to use in e.g. @ref OFMapTable.
 *
 * @warning Even though the OFCopying protocol is implemented, it does *not*
 *	    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
70
71
72
73
74
75
76
77
78
79
80
81
 *
 * @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for
 *	       IPv6 to bind to all.
 * @param port The port to bind to. If the port is 0, an unused port will be
 *	       chosen, which can be obtained using the return value.
 * @return The port the socket was bound to
 */
- (uint16_t)bindToHost: (OFString *)host
		  port: (uint16_t)port;
@end

OF_ASSUME_NONNULL_END







|
<



69
70
71
72
73
74
75
76

77
78
79
 *
 * @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for
 *	       IPv6 to bind to all.
 * @param port The port to bind to. If the port is 0, an unused port will be
 *	       chosen, which can be obtained using the return value.
 * @return The port the socket was bound to
 */
- (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port;

@end

OF_ASSUME_NONNULL_END

Modified src/OFUDPSocket.m from [8b135fcca2] to [b89b6c4724].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFUDPSocket.h"
#import "OFUDPSocket+Private.h"
#import "OFDNSResolver.h"
#import "OFData.h"


#import "OFThread.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"

#import "socket.h"
#import "socket_helpers.h"

@implementation OFUDPSocket
@dynamic delegate;

- (uint16_t)of_bindToAddress: (of_socket_address_t *)address
		   extraType: (int)extraType OF_DIRECT
{
	void *pool = objc_autoreleasePoolPush();
	OFString *host;
	uint16_t port;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if ((_socket = socket(address->sockaddr.sockaddr.sa_family,
	    SOCK_DGRAM | SOCK_CLOEXEC | extraType, 0)) == INVALID_SOCKET) {
		host = of_socket_address_ip_string(address, &port);
		@throw [OFBindFailedException
		    exceptionWithHost: host
				 port: port
			       socket: self
				errNo: of_socket_errno()];
	}

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	/* {} needed to avoid warning with Clang 10 if next #if is false. */
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) {
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
	}
#endif

#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
	if (of_socket_address_get_port(address) != 0) {
#endif
		if (bind(_socket, &address->sockaddr.sockaddr,
		    address->length) != 0) {
			int errNo = of_socket_errno();

			closesocket(_socket);
			_socket = INVALID_SOCKET;

			host = of_socket_address_ip_string(address, &port);
			@throw [OFBindFailedException exceptionWithHost: host

								   port: port
								 socket: self
								  errNo: errNo];
		}
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
	} else {
		for (;;) {
			uint16_t rnd = 0;
			int ret;

			while (rnd < 1024)
				rnd = (uint16_t)rand();

			of_socket_address_set_port(address, rnd);

			if ((ret = bind(_socket, &address->sockaddr.sockaddr,
			    address->length)) == 0) {
				port = rnd;
				break;
			}

			if (of_socket_errno() != EADDRINUSE) {
				int errNo = of_socket_errno();



				closesocket(_socket);
				_socket = INVALID_SOCKET;

				host = of_socket_address_ip_string(
				    address, &port);
				@throw [OFBindFailedException
				    exceptionWithHost: host
						 port: port
					       socket: self
						errNo: errNo];
			}
		}
	}
#endif

	objc_autoreleasePoolPop(pool);

	if ((port = of_socket_address_get_port(address)) > 0)
		return port;

#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
	memset(address, 0, sizeof(*address));

	address->length = (socklen_t)sizeof(address->sockaddr);
	if (of_getsockname(_socket, &address->sockaddr.sockaddr,
	    &address->length) != 0) {
		int errNo = of_socket_errno();

		closesocket(_socket);
		_socket = INVALID_SOCKET;

		host = of_socket_address_ip_string(address, &port);
		@throw [OFBindFailedException exceptionWithHost: host

							   port: port
							 socket: self
							  errNo: errNo];
	}

	if (address->sockaddr.sockaddr.sa_family == AF_INET)
		return OF_BSWAP16_IF_LE(address->sockaddr.in.sin_port);
# ifdef OF_HAVE_IPV6
	else if (address->sockaddr.sockaddr.sa_family == AF_INET6)
		return OF_BSWAP16_IF_LE(address->sockaddr.in6.sin6_port);
# endif
	else {
		closesocket(_socket);
		_socket = INVALID_SOCKET;

		host = of_socket_address_ip_string(address, &port);
		@throw [OFBindFailedException exceptionWithHost: host

							   port: port
							 socket: self
							  errNo: EAFNOSUPPORT];
	}
#else
	closesocket(_socket);
	_socket = INVALID_SOCKET;

	host = of_socket_address_ip_string(address, &port);
	@throw [OFBindFailedException exceptionWithHost: host

						   port: port
						 socket: self
						  errNo: EADDRNOTAVAIL];
#endif
}

- (uint16_t)bindToHost: (OFString *)host
		  port: (uint16_t)port
{
	void *pool = objc_autoreleasePoolPush();
	OFData *socketAddresses;
	of_socket_address_t address;

	if (_socket != INVALID_SOCKET)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	socketAddresses = [[OFThread DNSResolver]
	    resolveAddressesForHost: host
		      addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY];

	address = *(of_socket_address_t *)[socketAddresses itemAtIndex: 0];
	of_socket_address_set_port(&address, port);

	port = [self of_bindToAddress: &address
			    extraType: 0];

	objc_autoreleasePoolPop(pool);

	return port;
}
@end

<
<
|















>
>
>
>
>










>
>





<
<
<



|



<






|
<

|
|

|
<










|
|



|


|

<
|
>
|
|
|

|








|


|
<

|
<
|
|
>
>


|

<
<












|


|



|

|


|

<
|
>
|
|
|



|


|



|

<
|
>
|
|
|



|

<
|
>
|
|
|



|
<



|

|




|

|
|

|
<






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


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

#ifndef _XOPEN_SOURCE_EXTENDED
# define _XOPEN_SOURCE_EXTENDED
#endif
#define _HPUX_ALT_XOPEN_SOCKET_API

#include <errno.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFUDPSocket.h"
#import "OFUDPSocket+Private.h"
#import "OFDNSResolver.h"
#import "OFData.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFThread.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"




@implementation OFUDPSocket
@dynamic delegate;

- (uint16_t)of_bindToAddress: (OFSocketAddress *)address
		   extraType: (int)extraType OF_DIRECT
{
	void *pool = objc_autoreleasePoolPush();

	uint16_t port;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if ((_socket = socket(address->sockaddr.sockaddr.sa_family,
	    SOCK_DGRAM | SOCK_CLOEXEC | extraType, 0)) == OFInvalidSocketHandle)

		@throw [OFBindFailedException
		    exceptionWithHost: OFSocketAddressString(address)
				 port: OFSocketAddressPort(address)
			       socket: self
				errNo: OFSocketErrNo()];


	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	/* {} needed to avoid warning with Clang 10 if next #if is false. */
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) {
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
	}
#endif

#if defined(OF_HPUX) || defined(OF_WII) || defined(OF_NINTENDO_3DS)
	if (OFSocketAddressPort(address) != 0) {
#endif
		if (bind(_socket, &address->sockaddr.sockaddr,
		    address->length) != 0) {
			int errNo = OFSocketErrNo();

			closesocket(_socket);
			_socket = OFInvalidSocketHandle;


			@throw [OFBindFailedException
			    exceptionWithHost: OFSocketAddressString(address)
					 port: OFSocketAddressPort(address)
				       socket: self
					errNo: errNo];
		}
#if defined(OF_HPUX) || defined(OF_WII) || defined(OF_NINTENDO_3DS)
	} else {
		for (;;) {
			uint16_t rnd = 0;
			int ret;

			while (rnd < 1024)
				rnd = (uint16_t)rand();

			OFSocketAddressSetPort(address, rnd);

			if ((ret = bind(_socket, &address->sockaddr.sockaddr,
			    address->length)) == 0)

				break;


			if (OFSocketErrNo() != EADDRINUSE) {
				int errNo = OFSocketErrNo();
				OFString *host = OFSocketAddressString(address);
				port = OFSocketAddressPort(address);

				closesocket(_socket);
				_socket = OFInvalidSocketHandle;



				@throw [OFBindFailedException
				    exceptionWithHost: host
						 port: port
					       socket: self
						errNo: errNo];
			}
		}
	}
#endif

	objc_autoreleasePoolPop(pool);

	if ((port = OFSocketAddressPort(address)) > 0)
		return port;

#if !defined(OF_HPUX) && !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
	memset(address, 0, sizeof(*address));

	address->length = (socklen_t)sizeof(address->sockaddr);
	if (OFGetSockName(_socket, &address->sockaddr.sockaddr,
	    &address->length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;


		@throw [OFBindFailedException
		    exceptionWithHost: OFSocketAddressString(address)
				 port: OFSocketAddressPort(address)
			       socket: self
				errNo: errNo];
	}

	if (address->sockaddr.sockaddr.sa_family == AF_INET)
		return OFFromBigEndian16(address->sockaddr.in.sin_port);
# ifdef OF_HAVE_IPV6
	else if (address->sockaddr.sockaddr.sa_family == AF_INET6)
		return OFFromBigEndian16(address->sockaddr.in6.sin6_port);
# endif
	else {
		closesocket(_socket);
		_socket = OFInvalidSocketHandle;


		@throw [OFBindFailedException
		    exceptionWithHost: OFSocketAddressString(address)
				 port: OFSocketAddressPort(address)
			       socket: self
				errNo: EAFNOSUPPORT];
	}
#else
	closesocket(_socket);
	_socket = OFInvalidSocketHandle;


	@throw [OFBindFailedException
	    exceptionWithHost: OFSocketAddressString(address)
			 port: OFSocketAddressPort(address)
		       socket: self
			errNo: EADDRNOTAVAIL];
#endif
}

- (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port

{
	void *pool = objc_autoreleasePoolPush();
	OFData *socketAddresses;
	OFSocketAddress address;

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	socketAddresses = [[OFThread DNSResolver]
	    resolveAddressesForHost: host
		      addressFamily: OFSocketAddressFamilyAny];

	address = *(OFSocketAddress *)[socketAddresses itemAtIndex: 0];
	OFSocketAddressSetPort(&address, port);

	port = [self of_bindToAddress: &address extraType: 0];


	objc_autoreleasePoolPop(pool);

	return port;
}
@end

Added src/OFUNIXDatagramSocket.h version [15597b10d9].

















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 OFUNIXDatagramSocketDelegate OFUNIXDatagramSocket.h \
 *	     ObjFW/OFUNIXDatagramSocket.h
 *
 * @brief A delegate for OFUNIXDatagramSocket.
 */
@protocol OFUNIXDatagramSocketDelegate <OFDatagramSocketDelegate>
@end

/**
 * @class OFUNIXDatagramSocket OFUNIXDatagramSocket.h \
 *	  ObjFW/OFUNIXDatagramSocket.h
 *
 * @brief A class which provides methods to create and use UNIX datagram
 *	  sockets.
 *
 * Addresses are of type @ref OFSocketAddress. You can use
 * @ref OFSocketAddressMakeUNIX to create an address or
 * @ref OFSocketAddressUNIXPath to get the socket path.
 *
 * @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 OFUNIXDatagramSocket: OFDatagramSocket
{
	OF_RESERVE_IVARS(OFUNIXDatagramSocket, 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 <OFUNIXDatagramSocketDelegate> delegate;

/**
 * @brief Bind the socket to the specified path.
 *
 * @param path The path to bind to
 * @return The address on which this socket can be reached
 */
- (OFSocketAddress)bindToPath: (OFString *)path;
@end

OF_ASSUME_NONNULL_END

Added src/OFUNIXDatagramSocket.m version [0b41257c02].

















































































































































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

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFUNIXDatagramSocket.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFString.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"

@implementation OFUNIXDatagramSocket
@dynamic delegate;

- (OFSocketAddress)bindToPath: (OFString *)path
{
	OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = OFSocketAddressMakeUNIX(path);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_DGRAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
		@throw [OFBindFailedException
		    exceptionWithPath: path
			       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, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPath: path
							 socket: self
							  errNo: errNo];
	}

	return address;
}
@end

Added src/OFUNIXStreamSocket.h version [34276ad0c9].









































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFStreamSocket.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;

/**
 * @protocol OFUNIXStreamSocketDelegate OFUNIXStreamSocket.h \
 *	     ObjFW/OFUNIXStreamSocket.h
 *
 * A delegate for OFUNIXStreamSocket.
 */
@protocol OFUNIXStreamSocketDelegate <OFStreamSocketDelegate>
@end

/**
 * @class OFUNIXStreamSocket OFUNIXStreamSocket.h ObjFW/OFUNIXStreamSocket.h
 *
 * @brief A class which provides methods to create and use UNIX stream sockets.
 *
 * To connect to a server, create a socket and connect it.
 * To create a server, create a socket, bind it and listen on it.
 */
@interface OFUNIXStreamSocket: OFStreamSocket
{
	OF_RESERVE_IVARS(OFUNIXStreamSocket, 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 <OFUNIXStreamSocketDelegate> delegate;

/**
 * @brief Connects the OFUNIXStreamSocket to the specified destination.
 *
 * @param path The path to connect to
 */
- (void)connectToPath: (OFString *)path;

/**
 * @brief Binds the socket to the specified host and port.
 *
 * @param path The path to bind to
 */
- (void)bindToPath: (OFString *)path;
@end

OF_ASSUME_NONNULL_END

Added src/OFUNIXStreamSocket.m version [72303f60dd].



























































































































































































































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

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFUNIXStreamSocket.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFString.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFConnectionFailedException.h"

@implementation OFUNIXStreamSocket
@dynamic delegate;

- (void)connectToPath: (OFString *)path
{
	OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = OFSocketAddressMakeUNIX(path);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
		@throw [OFConnectionFailedException
		    exceptionWithPath: path
			       socket: self
				errNo: OFSocketErrNo()];

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	if (connect(_socket, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFConnectionFailedException exceptionWithPath: path
							       socket: self
								errNo: errNo];
	}
}

- (void)bindToPath: (OFString *)path
{
	OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = OFSocketAddressMakeUNIX(path);

	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
	    SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
		@throw [OFBindFailedException
		    exceptionWithPath: path
			       socket: self
				errNo: OFSocketErrNo()];

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
#endif

	if (bind(_socket, &address.sockaddr.sockaddr, address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindFailedException exceptionWithPath: path
							 socket: self
							  errNo: errNo];
	}
}
@end

Modified src/OFURL.h from [125797e09d] to [40faae77ac].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
 * @brief The query part of the URL as a dictionary.
 *
 * For example, a query like `key1=value1&key2=value2` would correspond to the
 * following dictionary:
 *
 *     @{
 *         @"key1": "value1",
 *         @"key2": "value2"
 *     }
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
    OFDictionary OF_GENERIC(OFString *, OFString *) *queryDictionary;

/**
 * @brief The fragment part of the URL.







|
|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/**
 * @brief The query part of the URL as a dictionary.
 *
 * For example, a query like `key1=value1&key2=value2` would correspond to the
 * following dictionary:
 *
 *     @{
 *         @"key1": @"value1",
 *         @"key2": @"value2"
 *     }
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
    OFDictionary OF_GENERIC(OFString *, OFString *) *queryDictionary;

/**
 * @brief The fragment part of the URL.
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

/**
 * @brief The URL as a string.
 */
@property (readonly, nonatomic) OFString *string;

/**
 * @brief The URL with relative sub paths resolved.
 */
@property (readonly, nonatomic) OFURL *URLByStandardizingPath;

#ifdef OF_HAVE_FILES
/**
 * @brief The local file system representation for a file URL.
 *







|







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

/**
 * @brief The URL as a string.
 */
@property (readonly, nonatomic) OFString *string;

/**
 * @brief The URL with relative subpaths resolved.
 */
@property (readonly, nonatomic) OFURL *URLByStandardizingPath;

#ifdef OF_HAVE_FILES
/**
 * @brief The local file system representation for a file URL.
 *
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
 * @brief Creates a new URL with the specified string relative to the
 *	  specified URL.
 *
 * @param string A string describing a URL
 * @param URL An URL to which the string is relative
 * @return A new, autoreleased OFURL
 */
+ (instancetype)URLWithString: (OFString *)string
		relativeToURL: (OFURL *)URL;

#ifdef OF_HAVE_FILES
/**
 * @brief Creates a new URL with the specified local file path.
 *
 * If a directory exists at the specified path, a slash is appended if there is
 * no slash yet.







|
<







185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
 * @brief Creates a new URL with the specified string relative to the
 *	  specified URL.
 *
 * @param string A string describing a URL
 * @param URL An URL to which the string is relative
 * @return A new, autoreleased OFURL
 */
+ (instancetype)URLWithString: (OFString *)string relativeToURL: (OFURL *)URL;


#ifdef OF_HAVE_FILES
/**
 * @brief Creates a new URL with the specified local file path.
 *
 * If a directory exists at the specified path, a slash is appended if there is
 * no slash yet.
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
 * @brief Initializes an already allocated OFURL with the specified string and
 *	  relative URL.
 *
 * @param string A string describing a URL
 * @param URL A URL to which the string is relative
 * @return An initialized OFURL
 */
- (instancetype)initWithString: (OFString *)string
		 relativeToURL: (OFURL *)URL;

#ifdef OF_HAVE_FILES
/**
 * @brief Initializes an already allocated OFURL with the specified local file
 *	  path.
 *
 * If a directory exists at the specified path, a slash is appended if there is







|
<







227
228
229
230
231
232
233
234

235
236
237
238
239
240
241
 * @brief Initializes an already allocated OFURL with the specified string and
 *	  relative URL.
 *
 * @param string A string describing a URL
 * @param URL A URL to which the string is relative
 * @return An initialized OFURL
 */
- (instancetype)initWithString: (OFString *)string relativeToURL: (OFURL *)URL;


#ifdef OF_HAVE_FILES
/**
 * @brief Initializes an already allocated OFURL with the specified local file
 *	  path.
 *
 * If a directory exists at the specified path, a slash is appended if there is
367
368
369
370
371
372
373
374

375
376
377
378
379
380
381
 */
+ (OFCharacterSet *)URLFragmentAllowedCharacterSet;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern bool of_url_is_ipv6_host(OFString *host);

#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#import "OFMutableURL.h"







|
>







363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
 */
+ (OFCharacterSet *)URLFragmentAllowedCharacterSet;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern bool OFURLIsIPv6Host(OFString *host);
extern void OFURLVerifyIsEscaped(OFString *, OFCharacterSet *);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#import "OFMutableURL.h"

Modified src/OFURL.m from [a1fb3bc27c] to [8192f78e14].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#include <stdlib.h>
#include <string.h>

#import "OFURL.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFNumber.h"
#import "OFString.h"
#import "OFXMLElement.h"

#ifdef OF_HAVE_FILES
# import "OFFileManager.h"
# import "OFFileURLHandler.h"
#endif





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

#import "once.h"

@interface OFURLAllowedCharacterSetBase: OFCharacterSet
@end

@interface OFURLAllowedCharacterSet: OFURLAllowedCharacterSetBase
@end

@interface OFURLSchemeAllowedCharacterSet: OFURLAllowedCharacterSetBase







<
<
<
<




>
>
>
>





<
<







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

#include <stdlib.h>
#include <string.h>

#import "OFURL.h"
#import "OFArray.h"
#import "OFDictionary.h"




#ifdef OF_HAVE_FILES
# import "OFFileManager.h"
# import "OFFileURLHandler.h"
#endif
#import "OFNumber.h"
#import "OFOnce.h"
#import "OFString.h"
#import "OFXMLElement.h"

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



@interface OFURLAllowedCharacterSetBase: OFCharacterSet
@end

@interface OFURLAllowedCharacterSet: OFURLAllowedCharacterSetBase
@end

@interface OFURLSchemeAllowedCharacterSet: OFURLAllowedCharacterSetBase
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73

static OFCharacterSet *URLAllowedCharacterSet = nil;
static OFCharacterSet *URLSchemeAllowedCharacterSet = nil;
static OFCharacterSet *URLPathAllowedCharacterSet = nil;
static OFCharacterSet *URLQueryOrFragmentAllowedCharacterSet = nil;
static OFCharacterSet *URLQueryKeyValueAllowedCharacterSet = nil;

static of_once_t URLAllowedCharacterSetOnce = OF_ONCE_INIT;
static of_once_t URLQueryOrFragmentAllowedCharacterSetOnce = OF_ONCE_INIT;


static void
initURLAllowedCharacterSet(void)
{
	URLAllowedCharacterSet = [[OFURLAllowedCharacterSet alloc] init];
}








|
|
>







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

static OFCharacterSet *URLAllowedCharacterSet = nil;
static OFCharacterSet *URLSchemeAllowedCharacterSet = nil;
static OFCharacterSet *URLPathAllowedCharacterSet = nil;
static OFCharacterSet *URLQueryOrFragmentAllowedCharacterSet = nil;
static OFCharacterSet *URLQueryKeyValueAllowedCharacterSet = nil;

static OFOnceControl URLAllowedCharacterSetOnce = OFOnceControlInitValue;
static OFOnceControl URLQueryOrFragmentAllowedCharacterSetOnce =
    OFOnceControlInitValue;

static void
initURLAllowedCharacterSet(void)
{
	URLAllowedCharacterSet = [[OFURLAllowedCharacterSet alloc] init];
}

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
	    [[OFURLQueryKeyValueAllowedCharacterSet alloc] init];
}

OF_DIRECT_MEMBERS
@interface OFInvertedCharacterSetWithoutPercent: OFCharacterSet
{
	OFCharacterSet *_characterSet;
	bool (*_characterIsMember)(id, SEL, of_unichar_t);
}

- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet;
@end

bool
of_url_is_ipv6_host(OFString *host)
{
	const char *UTF8String = host.UTF8String;
	bool hasColon = false;

	while (*UTF8String != '\0') {
		if (!of_ascii_isdigit(*UTF8String) && *UTF8String != ':' &&
		    (*UTF8String < 'a' || *UTF8String > 'f') &&
		    (*UTF8String < 'A' || *UTF8String > 'F'))
			return false;

		if (*UTF8String == ':')
			hasColon = true;








|






|





|







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
	    [[OFURLQueryKeyValueAllowedCharacterSet alloc] init];
}

OF_DIRECT_MEMBERS
@interface OFInvertedCharacterSetWithoutPercent: OFCharacterSet
{
	OFCharacterSet *_characterSet;
	bool (*_characterIsMember)(id, SEL, OFUnichar);
}

- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet;
@end

bool
OFURLIsIPv6Host(OFString *host)
{
	const char *UTF8String = host.UTF8String;
	bool hasColon = false;

	while (*UTF8String != '\0') {
		if (!OFASCIIIsDigit(*UTF8String) && *UTF8String != ':' &&
		    (*UTF8String < 'a' || *UTF8String > 'f') &&
		    (*UTF8String < 'A' || *UTF8String > 'F'))
			return false;

		if (*UTF8String == ':')
			hasColon = true;

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

- (void)release
{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}
@end

@implementation OFURLAllowedCharacterSet
- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':







|




|

|







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

- (void)release
{
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}
@end

@implementation OFURLAllowedCharacterSet
- (bool)characterIsMember: (OFUnichar)character
{
	if (character < CHAR_MAX && OFASCIIIsAlnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':
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
	default:
		return false;
	}
}
@end

@implementation OFURLSchemeAllowedCharacterSet
- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '+':
	case '-':
	case '.':
		return true;
	default:
		return false;
	}
}
@end

@implementation OFURLPathAllowedCharacterSet
- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':







|

|














|

|







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
	default:
		return false;
	}
}
@end

@implementation OFURLSchemeAllowedCharacterSet
- (bool)characterIsMember: (OFUnichar)character
{
	if (character < CHAR_MAX && OFASCIIIsAlnum(character))
		return true;

	switch (character) {
	case '+':
	case '-':
	case '.':
		return true;
	default:
		return false;
	}
}
@end

@implementation OFURLPathAllowedCharacterSet
- (bool)characterIsMember: (OFUnichar)character
{
	if (character < CHAR_MAX && OFASCIIIsAlnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
	default:
		return false;
	}
}
@end

@implementation OFURLQueryOrFragmentAllowedCharacterSet
- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':







|

|







223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
	default:
		return false;
	}
}
@end

@implementation OFURLQueryOrFragmentAllowedCharacterSet
- (bool)characterIsMember: (OFUnichar)character
{
	if (character < CHAR_MAX && OFASCIIIsAlnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
	default:
		return false;
	}
}
@end

@implementation OFURLQueryKeyValueAllowedCharacterSet
- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':







|

|







256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
	default:
		return false;
	}
}
@end

@implementation OFURLQueryKeyValueAllowedCharacterSet
- (bool)characterIsMember: (OFUnichar)character
{
	if (character < CHAR_MAX && OFASCIIIsAlnum(character))
		return true;

	switch (character) {
	case '-':
	case '.':
	case '_':
	case '~':
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
@implementation OFInvertedCharacterSetWithoutPercent
- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet
{
	self = [super init];

	@try {
		_characterSet = [characterSet retain];
		_characterIsMember = (bool (*)(id, SEL, of_unichar_t))
		    [_characterSet methodForSelector:
		    @selector(characterIsMember:)];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_characterSet release];

	[super dealloc];
}

- (bool)characterIsMember: (of_unichar_t)character
{
	return (character != '%' && !_characterIsMember(_characterSet,
	    @selector(characterIsMember:), character));
}
@end

void
of_url_verify_escaped(OFString *string, OFCharacterSet *characterSet)
{
	void *pool = objc_autoreleasePoolPush();

	characterSet = [[[OFInvertedCharacterSetWithoutPercent alloc]
	    initWithCharacterSet: characterSet] autorelease];

	if ([string indexOfCharacterFromSet: characterSet] != OF_NOT_FOUND)
		@throw [OFInvalidFormatException exception];

	objc_autoreleasePoolPop(pool);
}

@implementation OFCharacterSet (URLCharacterSets)
+ (OFCharacterSet *)URLSchemeAllowedCharacterSet
{
	static of_once_t onceControl = OF_ONCE_INIT;
	of_once(&onceControl, initURLSchemeAllowedCharacterSet);

	return URLSchemeAllowedCharacterSet;
}

+ (OFCharacterSet *)URLHostAllowedCharacterSet
{
	of_once(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLUserAllowedCharacterSet
{
	of_once(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLPasswordAllowedCharacterSet
{
	of_once(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLPathAllowedCharacterSet
{
	static of_once_t onceControl = OF_ONCE_INIT;
	of_once(&onceControl, initURLPathAllowedCharacterSet);

	return URLPathAllowedCharacterSet;
}

+ (OFCharacterSet *)URLQueryAllowedCharacterSet
{
	of_once(&URLQueryOrFragmentAllowedCharacterSetOnce,
	    initURLQueryOrFragmentAllowedCharacterSet);

	return URLQueryOrFragmentAllowedCharacterSet;
}

+ (OFCharacterSet *)URLQueryKeyValueAllowedCharacterSet
{
	static of_once_t onceControl = OF_ONCE_INIT;
	of_once(&onceControl, initURLQueryKeyValueAllowedCharacterSet);

	return URLQueryKeyValueAllowedCharacterSet;
}

+ (OFCharacterSet *)URLFragmentAllowedCharacterSet
{
	of_once(&URLQueryOrFragmentAllowedCharacterSetOnce,
	    initURLQueryOrFragmentAllowedCharacterSet);

	return URLQueryOrFragmentAllowedCharacterSet;
}
@end

@implementation OFURL







|

















|







|






|








|
|






|






|






|






|
|






|







|
|






|







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
@implementation OFInvertedCharacterSetWithoutPercent
- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet
{
	self = [super init];

	@try {
		_characterSet = [characterSet retain];
		_characterIsMember = (bool (*)(id, SEL, OFUnichar))
		    [_characterSet methodForSelector:
		    @selector(characterIsMember:)];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_characterSet release];

	[super dealloc];
}

- (bool)characterIsMember: (OFUnichar)character
{
	return (character != '%' && !_characterIsMember(_characterSet,
	    @selector(characterIsMember:), character));
}
@end

void
OFURLVerifyIsEscaped(OFString *string, OFCharacterSet *characterSet)
{
	void *pool = objc_autoreleasePoolPush();

	characterSet = [[[OFInvertedCharacterSetWithoutPercent alloc]
	    initWithCharacterSet: characterSet] autorelease];

	if ([string indexOfCharacterFromSet: characterSet] != OFNotFound)
		@throw [OFInvalidFormatException exception];

	objc_autoreleasePoolPop(pool);
}

@implementation OFCharacterSet (URLCharacterSets)
+ (OFCharacterSet *)URLSchemeAllowedCharacterSet
{
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, initURLSchemeAllowedCharacterSet);

	return URLSchemeAllowedCharacterSet;
}

+ (OFCharacterSet *)URLHostAllowedCharacterSet
{
	OFOnce(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLUserAllowedCharacterSet
{
	OFOnce(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLPasswordAllowedCharacterSet
{
	OFOnce(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLPathAllowedCharacterSet
{
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, initURLPathAllowedCharacterSet);

	return URLPathAllowedCharacterSet;
}

+ (OFCharacterSet *)URLQueryAllowedCharacterSet
{
	OFOnce(&URLQueryOrFragmentAllowedCharacterSetOnce,
	    initURLQueryOrFragmentAllowedCharacterSet);

	return URLQueryOrFragmentAllowedCharacterSet;
}

+ (OFCharacterSet *)URLQueryKeyValueAllowedCharacterSet
{
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, initURLQueryKeyValueAllowedCharacterSet);

	return URLQueryKeyValueAllowedCharacterSet;
}

+ (OFCharacterSet *)URLFragmentAllowedCharacterSet
{
	OFOnce(&URLQueryOrFragmentAllowedCharacterSetOnce,
	    initURLQueryOrFragmentAllowedCharacterSet);

	return URLQueryOrFragmentAllowedCharacterSet;
}
@end

@implementation OFURL
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
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		char *tmp, *tmp2;
		bool isIPv6Host = false;

		if ((UTF8String2 = of_strdup(string.UTF8String)) == NULL)
			@throw [OFOutOfMemoryException
			     exceptionWithRequestedSize:
			     string.UTF8StringLength];

		UTF8String = UTF8String2;

		if ((tmp = strchr(UTF8String, ':')) == NULL)
			@throw [OFInvalidFormatException exception];

		if (strncmp(tmp, "://", 3) != 0)
			@throw [OFInvalidFormatException exception];

		for (tmp2 = UTF8String; tmp2 < tmp; tmp2++)
			*tmp2 = of_ascii_tolower(*tmp2);

		_URLEncodedScheme = [[OFString alloc]
		    initWithUTF8String: UTF8String
				length: tmp - UTF8String];

		of_url_verify_escaped(_URLEncodedScheme,
		    [OFCharacterSet URLSchemeAllowedCharacterSet]);

		UTF8String = tmp + 3;

		if ((tmp = strchr(UTF8String, '/')) != NULL) {
			*tmp = '\0';
			tmp++;







<
<
<
<
<
|








|





|







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
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		char *tmp, *tmp2;
		bool isIPv6Host = false;






		UTF8String = UTF8String2 = OFStrDup(string.UTF8String);

		if ((tmp = strchr(UTF8String, ':')) == NULL)
			@throw [OFInvalidFormatException exception];

		if (strncmp(tmp, "://", 3) != 0)
			@throw [OFInvalidFormatException exception];

		for (tmp2 = UTF8String; tmp2 < tmp; tmp2++)
			*tmp2 = OFASCIIToLower(*tmp2);

		_URLEncodedScheme = [[OFString alloc]
		    initWithUTF8String: UTF8String
				length: tmp - UTF8String];

		OFURLVerifyIsEscaped(_URLEncodedScheme,
		    [OFCharacterSet URLSchemeAllowedCharacterSet]);

		UTF8String = tmp + 3;

		if ((tmp = strchr(UTF8String, '/')) != NULL) {
			*tmp = '\0';
			tmp++;
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
				tmp3++;

				_URLEncodedUser = [[OFString alloc]
				    initWithUTF8String: UTF8String];
				_URLEncodedPassword = [[OFString alloc]
				    initWithUTF8String: tmp3];

				of_url_verify_escaped(_URLEncodedPassword,
				    [OFCharacterSet
				    URLPasswordAllowedCharacterSet]);
			} else
				_URLEncodedUser = [[OFString alloc]
				    initWithUTF8String: UTF8String];

			of_url_verify_escaped(_URLEncodedUser,
			    [OFCharacterSet URLUserAllowedCharacterSet]);

			UTF8String = tmp2;
		}

		if (UTF8String[0] == '[') {
			tmp2 = UTF8String++;

			while (of_ascii_isdigit(*UTF8String) ||
			    *UTF8String == ':' ||
			    (*UTF8String >= 'a' && *UTF8String <= 'f') ||
			    (*UTF8String >= 'A' && *UTF8String <= 'F'))
				UTF8String++;

			if (*UTF8String != ']')
				@throw [OFInvalidFormatException exception];

			UTF8String++;

			_URLEncodedHost = [[OFString alloc]
			    initWithUTF8String: tmp2
					length: UTF8String - tmp2];

			if (*UTF8String == ':') {
				OFString *portString;

				tmp2 = ++UTF8String;

				while (*UTF8String != '\0') {
					if (!of_ascii_isdigit(*UTF8String))
						@throw [OFInvalidFormatException
						    exception];

					UTF8String++;
				}

				portString = [OFString







|






|








|




















|







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
				tmp3++;

				_URLEncodedUser = [[OFString alloc]
				    initWithUTF8String: UTF8String];
				_URLEncodedPassword = [[OFString alloc]
				    initWithUTF8String: tmp3];

				OFURLVerifyIsEscaped(_URLEncodedPassword,
				    [OFCharacterSet
				    URLPasswordAllowedCharacterSet]);
			} else
				_URLEncodedUser = [[OFString alloc]
				    initWithUTF8String: UTF8String];

			OFURLVerifyIsEscaped(_URLEncodedUser,
			    [OFCharacterSet URLUserAllowedCharacterSet]);

			UTF8String = tmp2;
		}

		if (UTF8String[0] == '[') {
			tmp2 = UTF8String++;

			while (OFASCIIIsDigit(*UTF8String) ||
			    *UTF8String == ':' ||
			    (*UTF8String >= 'a' && *UTF8String <= 'f') ||
			    (*UTF8String >= 'A' && *UTF8String <= 'F'))
				UTF8String++;

			if (*UTF8String != ']')
				@throw [OFInvalidFormatException exception];

			UTF8String++;

			_URLEncodedHost = [[OFString alloc]
			    initWithUTF8String: tmp2
					length: UTF8String - tmp2];

			if (*UTF8String == ':') {
				OFString *portString;

				tmp2 = ++UTF8String;

				while (*UTF8String != '\0') {
					if (!OFASCIIIsDigit(*UTF8String))
						@throw [OFInvalidFormatException
						    exception];

					UTF8String++;
				}

				portString = [OFString
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
			_port = [[OFNumber alloc] initWithUnsignedShort:
			    portString.unsignedLongLongValue];
		} else
			_URLEncodedHost = [[OFString alloc]
			    initWithUTF8String: UTF8String];

		if (!isIPv6Host)
			of_url_verify_escaped(_URLEncodedHost,
			    [OFCharacterSet URLHostAllowedCharacterSet]);

		if ((UTF8String = tmp) != NULL) {
			if ((tmp = strchr(UTF8String, '#')) != NULL) {
				*tmp = '\0';

				_URLEncodedFragment = [[OFString alloc]
				    initWithUTF8String: tmp + 1];

				of_url_verify_escaped(_URLEncodedFragment,
				    [OFCharacterSet
				    URLFragmentAllowedCharacterSet]);
			}

			if ((tmp = strchr(UTF8String, '?')) != NULL) {
				*tmp = '\0';

				_URLEncodedQuery = [[OFString alloc]
				    initWithUTF8String: tmp + 1];

				of_url_verify_escaped(_URLEncodedQuery,
				    [OFCharacterSet
				    URLQueryAllowedCharacterSet]);
			}















			UTF8String--;
			*UTF8String = '/';




			_URLEncodedPath = [[OFString alloc]
			    initWithUTF8String: UTF8String];

			of_url_verify_escaped(_URLEncodedPath,
			    [OFCharacterSet URLPathAllowedCharacterSet]);
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	} @finally {
		free(UTF8String2);
	}

	return self;
}

- (instancetype)initWithString: (OFString *)string
		 relativeToURL: (OFURL *)URL
{
	char *UTF8String, *UTF8String2 = NULL;

	if ([string containsString: @"://"])
		return [self initWithString: string];

	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		char *tmp;

		_URLEncodedScheme = [URL->_URLEncodedScheme copy];
		_URLEncodedHost = [URL->_URLEncodedHost copy];
		_port = [URL->_port copy];
		_URLEncodedUser = [URL->_URLEncodedUser copy];
		_URLEncodedPassword = [URL->_URLEncodedPassword copy];

		if ((UTF8String2 = of_strdup(string.UTF8String)) == NULL)
			@throw [OFOutOfMemoryException
			     exceptionWithRequestedSize:
			     string.UTF8StringLength];

		UTF8String = UTF8String2;

		if ((tmp = strchr(UTF8String, '#')) != NULL) {
			*tmp = '\0';
			_URLEncodedFragment = [[OFString alloc]
			    initWithUTF8String: tmp + 1];

			of_url_verify_escaped(_URLEncodedFragment,
			    [OFCharacterSet URLFragmentAllowedCharacterSet]);
		}

		if ((tmp = strchr(UTF8String, '?')) != NULL) {
			*tmp = '\0';
			_URLEncodedQuery = [[OFString alloc]
			    initWithUTF8String: tmp + 1];

			of_url_verify_escaped(_URLEncodedQuery,
			    [OFCharacterSet URLQueryAllowedCharacterSet]);
		}

		if (*UTF8String == '/')
			_URLEncodedPath = [[OFString alloc]
			    initWithUTF8String: UTF8String];
		else {
			OFString *relativePath =
			    [OFString stringWithUTF8String: UTF8String];

			if ([URL->_URLEncodedPath hasSuffix: @"/"])
				_URLEncodedPath = [[URL->_URLEncodedPath
				    stringByAppendingString: relativePath]
				    copy];
			else {
				OFMutableString *path = [OFMutableString
				    stringWithString:
				    (URL->_URLEncodedPath != nil
				    ? URL->_URLEncodedPath
				    : @"/")];
				of_range_t range = [path
				    rangeOfString: @"/"
					  options: OF_STRING_SEARCH_BACKWARDS];

				if (range.location == OF_NOT_FOUND)
					@throw [OFInvalidFormatException
					    exception];

				range.location++;
				range.length = path.length - range.location;

				[path replaceCharactersInRange: range
						    withString: relativePath];
				[path makeImmutable];

				_URLEncodedPath = [path copy];
			}
		}

		of_url_verify_escaped(_URLEncodedPath,
		    [OFCharacterSet URLPathAllowedCharacterSet]);

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	} @finally {
		free(UTF8String2);
	}

	return self;
}

#ifdef OF_HAVE_FILES
- (instancetype)initFileURLWithPath: (OFString *)path
{
	bool isDirectory;

	@try {
		void *pool = objc_autoreleasePoolPush();

		isDirectory = [path of_isDirectoryPath];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initFileURLWithPath: path
			     isDirectory: isDirectory];

	return self;
}

- (instancetype)initFileURLWithPath: (OFString *)path
			isDirectory: (bool)isDirectory
{







|









|










|




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


>
>
>




|








|





|
<


















<
<
<
<
<
|






|








|




















|

|

|














|







|












<

<






|
<







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
			_port = [[OFNumber alloc] initWithUnsignedShort:
			    portString.unsignedLongLongValue];
		} else
			_URLEncodedHost = [[OFString alloc]
			    initWithUTF8String: UTF8String];

		if (!isIPv6Host)
			OFURLVerifyIsEscaped(_URLEncodedHost,
			    [OFCharacterSet URLHostAllowedCharacterSet]);

		if ((UTF8String = tmp) != NULL) {
			if ((tmp = strchr(UTF8String, '#')) != NULL) {
				*tmp = '\0';

				_URLEncodedFragment = [[OFString alloc]
				    initWithUTF8String: tmp + 1];

				OFURLVerifyIsEscaped(_URLEncodedFragment,
				    [OFCharacterSet
				    URLFragmentAllowedCharacterSet]);
			}

			if ((tmp = strchr(UTF8String, '?')) != NULL) {
				*tmp = '\0';

				_URLEncodedQuery = [[OFString alloc]
				    initWithUTF8String: tmp + 1];

				OFURLVerifyIsEscaped(_URLEncodedQuery,
				    [OFCharacterSet
				    URLQueryAllowedCharacterSet]);
			}

			/*
			 * Some versions of GCC issue a false-positive warning
			 * (turned error) about a string overflow. This is a
			 * false positive because UTF8String is set to tmp
			 * above and tmp is either NULL or points *after* the
			 * slash for the path. So all we do here is go back to
			 * that slash and restore it.
			 */
#if OF_GCC_VERSION >= 402
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpragmas"
# pragma GCC diagnostic ignored "-Wunknown-warning-option"
# pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
			UTF8String--;
			*UTF8String = '/';
#if OF_GCC_VERSION >= 402
# pragma GCC diagnostic pop
#endif

			_URLEncodedPath = [[OFString alloc]
			    initWithUTF8String: UTF8String];

			OFURLVerifyIsEscaped(_URLEncodedPath,
			    [OFCharacterSet URLPathAllowedCharacterSet]);
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	} @finally {
		OFFreeMemory(UTF8String2);
	}

	return self;
}

- (instancetype)initWithString: (OFString *)string relativeToURL: (OFURL *)URL

{
	char *UTF8String, *UTF8String2 = NULL;

	if ([string containsString: @"://"])
		return [self initWithString: string];

	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		char *tmp;

		_URLEncodedScheme = [URL->_URLEncodedScheme copy];
		_URLEncodedHost = [URL->_URLEncodedHost copy];
		_port = [URL->_port copy];
		_URLEncodedUser = [URL->_URLEncodedUser copy];
		_URLEncodedPassword = [URL->_URLEncodedPassword copy];






		UTF8String = UTF8String2 = OFStrDup(string.UTF8String);

		if ((tmp = strchr(UTF8String, '#')) != NULL) {
			*tmp = '\0';
			_URLEncodedFragment = [[OFString alloc]
			    initWithUTF8String: tmp + 1];

			OFURLVerifyIsEscaped(_URLEncodedFragment,
			    [OFCharacterSet URLFragmentAllowedCharacterSet]);
		}

		if ((tmp = strchr(UTF8String, '?')) != NULL) {
			*tmp = '\0';
			_URLEncodedQuery = [[OFString alloc]
			    initWithUTF8String: tmp + 1];

			OFURLVerifyIsEscaped(_URLEncodedQuery,
			    [OFCharacterSet URLQueryAllowedCharacterSet]);
		}

		if (*UTF8String == '/')
			_URLEncodedPath = [[OFString alloc]
			    initWithUTF8String: UTF8String];
		else {
			OFString *relativePath =
			    [OFString stringWithUTF8String: UTF8String];

			if ([URL->_URLEncodedPath hasSuffix: @"/"])
				_URLEncodedPath = [[URL->_URLEncodedPath
				    stringByAppendingString: relativePath]
				    copy];
			else {
				OFMutableString *path = [OFMutableString
				    stringWithString:
				    (URL->_URLEncodedPath != nil
				    ? URL->_URLEncodedPath
				    : @"/")];
				OFRange range = [path
				    rangeOfString: @"/"
					  options: OFStringSearchBackwards];

				if (range.location == OFNotFound)
					@throw [OFInvalidFormatException
					    exception];

				range.location++;
				range.length = path.length - range.location;

				[path replaceCharactersInRange: range
						    withString: relativePath];
				[path makeImmutable];

				_URLEncodedPath = [path copy];
			}
		}

		OFURLVerifyIsEscaped(_URLEncodedPath,
		    [OFCharacterSet URLPathAllowedCharacterSet]);

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	} @finally {
		OFFreeMemory(UTF8String2);
	}

	return self;
}

#ifdef OF_HAVE_FILES
- (instancetype)initFileURLWithPath: (OFString *)path
{
	bool isDirectory;

	@try {
		void *pool = objc_autoreleasePoolPush();

		isDirectory = [path of_isDirectoryPath];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initFileURLWithPath: path isDirectory: isDirectory];


	return self;
}

- (instancetype)initFileURLWithPath: (OFString *)path
			isDirectory: (bool)isDirectory
{
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stringValue;

	@try {
		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		stringValue = element.stringValue;
	} @catch (id e) {
		[self release];
		@throw e;
	}







|







777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stringValue;

	@try {
		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		stringValue = element.stringValue;
	} @catch (id e) {
		[self release];
		@throw e;
	}
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _URLEncodedScheme.hash);
	OF_HASH_ADD_HASH(hash, _URLEncodedHost.hash);
	OF_HASH_ADD_HASH(hash, _port.hash);
	OF_HASH_ADD_HASH(hash, _URLEncodedUser.hash);
	OF_HASH_ADD_HASH(hash, _URLEncodedPassword.hash);
	OF_HASH_ADD_HASH(hash, _URLEncodedPath.hash);
	OF_HASH_ADD_HASH(hash, _URLEncodedQuery.hash);
	OF_HASH_ADD_HASH(hash, _URLEncodedFragment.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)scheme
{
	return _URLEncodedScheme.stringByURLDecoding;
}

- (OFString *)URLEncodedScheme
{
	return _URLEncodedScheme;
}

- (OFString *)host
{
	if ([_URLEncodedHost hasPrefix: @"["] &&
	    [_URLEncodedHost hasSuffix: @"]"]) {
		OFString *host = [_URLEncodedHost substringWithRange:
		    of_range(1, _URLEncodedHost.length - 2)];

		if (!of_url_is_ipv6_host(host))
			@throw [OFInvalidArgumentException exception];

		return host;
	}

	return _URLEncodedHost.stringByURLDecoding;
}







|

|

|
|
|
|
|
|
|
|

|



















|

|







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
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _URLEncodedScheme.hash);
	OFHashAddHash(&hash, _URLEncodedHost.hash);
	OFHashAddHash(&hash, _port.hash);
	OFHashAddHash(&hash, _URLEncodedUser.hash);
	OFHashAddHash(&hash, _URLEncodedPassword.hash);
	OFHashAddHash(&hash, _URLEncodedPath.hash);
	OFHashAddHash(&hash, _URLEncodedQuery.hash);
	OFHashAddHash(&hash, _URLEncodedFragment.hash);

	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)scheme
{
	return _URLEncodedScheme.stringByURLDecoding;
}

- (OFString *)URLEncodedScheme
{
	return _URLEncodedScheme;
}

- (OFString *)host
{
	if ([_URLEncodedHost hasPrefix: @"["] &&
	    [_URLEncodedHost hasSuffix: @"]"]) {
		OFString *host = [_URLEncodedHost substringWithRange:
		    OFRangeMake(1, _URLEncodedHost.length - 2)];

		if (!OFURLIsIPv6Host(host))
			@throw [OFInvalidArgumentException exception];

		return host;
	}

	return _URLEncodedHost.stringByURLDecoding;
}
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
#ifdef OF_HAVE_FILES
	if (isFile) {
		OFString *path = [_URLEncodedPath
		    of_URLPathToPathWithURLEncodedHost: nil];
		ret = [[path.pathComponents mutableCopy] autorelease];

		if (![ret.firstObject isEqual: @"/"])
			    [ret insertObject: @"/"
				      atIndex: 0];
	} else
#endif
		ret = [[[_URLEncodedPath componentsSeparatedByString: @"/"]
		    mutableCopy] autorelease];

	count = ret.count;

	if (count > 0 && [ret.firstObject length] == 0)
		[ret replaceObjectAtIndex: 0
			       withObject: @"/"];

	for (size_t i = 0; i < count; i++) {
		OFString *component = [ret objectAtIndex: i];

#ifdef OF_HAVE_FILES
		if (isFile)
			component =







|
<








|
<







948
949
950
951
952
953
954
955

956
957
958
959
960
961
962
963
964

965
966
967
968
969
970
971
#ifdef OF_HAVE_FILES
	if (isFile) {
		OFString *path = [_URLEncodedPath
		    of_URLPathToPathWithURLEncodedHost: nil];
		ret = [[path.pathComponents mutableCopy] autorelease];

		if (![ret.firstObject isEqual: @"/"])
			[ret insertObject: @"/" atIndex: 0];

	} else
#endif
		ret = [[[_URLEncodedPath componentsSeparatedByString: @"/"]
		    mutableCopy] autorelease];

	count = ret.count;

	if (count > 0 && [ret.firstObject length] == 0)
		[ret replaceObjectAtIndex: 0 withObject: @"/"];


	for (size_t i = 0; i < count; i++) {
		OFString *component = [ret objectAtIndex: i];

#ifdef OF_HAVE_FILES
		if (isFile)
			component =
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
	return [path autorelease];
}
#endif

- (OFURL *)URLByAppendingPathComponent: (OFString *)component
{
	OFMutableURL *URL = [[self mutableCopy] autorelease];

	[URL appendPathComponent: component];
	[URL makeImmutable];

	return URL;
}

- (OFURL *)URLByAppendingPathComponent: (OFString *)component
			   isDirectory: (bool)isDirectory
{
	OFMutableURL *URL = [[self mutableCopy] autorelease];

	[URL appendPathComponent: component
		     isDirectory: isDirectory];
	[URL makeImmutable];

	return URL;
}

- (OFURL *)URLByStandardizingPath
{
	OFMutableURL *URL = [[self mutableCopy] autorelease];

	[URL standardizePath];
	[URL makeImmutable];

	return URL;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@: %@>",
					   self.class, self.string];
}

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

	element = [OFXMLElement elementWithName: self.className
				      namespace: OF_SERIALIZATION_NS
				    stringValue: self.string];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}
@end







<


<







<
|
<

<






<


<















|









1158
1159
1160
1161
1162
1163
1164

1165
1166

1167
1168
1169
1170
1171
1172
1173

1174

1175

1176
1177
1178
1179
1180
1181

1182
1183

1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
	return [path autorelease];
}
#endif

- (OFURL *)URLByAppendingPathComponent: (OFString *)component
{
	OFMutableURL *URL = [[self mutableCopy] autorelease];

	[URL appendPathComponent: component];
	[URL makeImmutable];

	return URL;
}

- (OFURL *)URLByAppendingPathComponent: (OFString *)component
			   isDirectory: (bool)isDirectory
{
	OFMutableURL *URL = [[self mutableCopy] autorelease];

	[URL appendPathComponent: component isDirectory: isDirectory];

	[URL makeImmutable];

	return URL;
}

- (OFURL *)URLByStandardizingPath
{
	OFMutableURL *URL = [[self mutableCopy] autorelease];

	[URL standardizePath];
	[URL makeImmutable];

	return URL;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@: %@>",
					   self.class, self.string];
}

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

	element = [OFXMLElement elementWithName: self.className
				      namespace: OFSerializationNS
				    stringValue: self.string];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}
@end

Modified src/OFURLHandler.h from [95a1235a75] to [0e58f1d36b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 * created per scheme.
 *
 * @param class_ The class to register as the handler for the specified scheme
 * @param scheme The scheme for which to register the handler
 * @return Whether the class was successfully registered. If a handler for the
 *	   same scheme is already registered, registration fails.
 */
+ (bool)registerClass: (Class)class_
	    forScheme: (OFString *)scheme;

/**
 * @brief Returns the handler for the specified URL.
 *
 * @return The handler for the specified URL.
 */
+ (nullable OF_KINDOF(OFURLHandler *))handlerForURL: (OFURL *)URL;







|
<







47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
 * created per scheme.
 *
 * @param class_ The class to register as the handler for the specified scheme
 * @param scheme The scheme for which to register the handler
 * @return Whether the class was successfully registered. If a handler for the
 *	   same scheme is already registered, registration fails.
 */
+ (bool)registerClass: (Class)class_ forScheme: (OFString *)scheme;


/**
 * @brief Returns the handler for the specified URL.
 *
 * @return The handler for the specified URL.
 */
+ (nullable OF_KINDOF(OFURLHandler *))handlerForURL: (OFURL *)URL;
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
 *	       `w+x`          | Read-write, create or fail, exclusive
 *	       `a`            | Write-only, create or append
 *	       `a+`           | Read-write, create or append
 *	       @n
 *	       The handler is allowed to not implement all modes and is also
 *	       allowed to implement additional, scheme-specific modes.
 */
- (OFStream *)openItemAtURL: (OFURL *)URL
		       mode: (OFString *)mode;

/**
 * @brief Returns the attributes for the item at the specified URL.
 *
 * @param URL The URL to return the attributes for
 * @return A dictionary of attributes for the specified URL, with the keys of
 *	   type @ref of_file_attribute_key_t
 */
- (of_file_attributes_t)attributesOfItemAtURL: (OFURL *)URL;

/**
 * @brief Sets the attributes for the item at the specified URL.
 *
 * All attributes not part of the dictionary are left unchanged.
 *
 * @param attributes The attributes to set for the specified URL
 * @param URL The URL of the item to set the attributes for
 */
- (void)setAttributes: (of_file_attributes_t)attributes
	  ofItemAtURL: (OFURL *)URL;

/**
 * @brief Checks whether a file exists at the specified URL.
 *
 * @param URL The URL to check
 * @return A boolean whether there is a file at the specified URL
 */







|
<






|

|









<
|







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
 *	       `w+x`          | Read-write, create or fail, exclusive
 *	       `a`            | Write-only, create or append
 *	       `a+`           | Read-write, create or append
 *	       @n
 *	       The handler is allowed to not implement all modes and is also
 *	       allowed to implement additional, scheme-specific modes.
 */
- (OFStream *)openItemAtURL: (OFURL *)URL mode: (OFString *)mode;


/**
 * @brief Returns the attributes for the item at the specified URL.
 *
 * @param URL The URL to return the attributes for
 * @return A dictionary of attributes for the specified URL, with the keys of
 *	   type @ref OFFileAttributeKey
 */
- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL;

/**
 * @brief Sets the attributes for the item at the specified URL.
 *
 * All attributes not part of the dictionary are left unchanged.
 *
 * @param attributes The attributes to set for the specified URL
 * @param URL The URL of the item to set the attributes for
 */

- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURL: (OFURL *)URL;

/**
 * @brief Checks whether a file exists at the specified URL.
 *
 * @param URL The URL to check
 * @return A boolean whether there is a file at the specified URL
 */
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
 * @brief Creates a directory at the specified URL.
 *
 * @param URL The URL of the directory to create
 */
- (void)createDirectoryAtURL: (OFURL *)URL;

/**
 * @brief Returns an array with the 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 of OFString with the items in the specified directory
 */
- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtURL: (OFURL *)URL;

/**
 * @brief Removes the item at the specified URL.
 *
 * If the item at the specified URL is a directory, it is removed recursively.
 *
 * @param URL The URL to the item which should be removed







|
>




|

|







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
 * @brief Creates a directory at the specified URL.
 *
 * @param URL The URL of the directory to create
 */
- (void)createDirectoryAtURL: (OFURL *)URL;

/**
 * @brief Returns an array with the URLs of the items in the specified
 *	  directory.
 *
 * @note `.` and `..` are not part of the returned array.
 *
 * @param URL The URL to the directory whose items should be returned
 * @return An array with the URLs of the items in the specified directory
 */
- (OFArray OF_GENERIC(OFURL *) *)contentsOfDirectoryAtURL: (OFURL *)URL;

/**
 * @brief Removes the item at the specified URL.
 *
 * If the item at the specified URL is a directory, it is removed recursively.
 *
 * @param URL The URL to the item which should be removed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
 * name of the item.
 *
 * This method is not available for all URLs.
 *
 * @param source The URL to the item for which a link should be created
 * @param destination The URL to the item which should link to the source
 */
- (void)linkItemAtURL: (OFURL *)source
		toURL: (OFURL *)destination;

/**
 * @brief Creates a symbolic link for an item.
 *
 * The destination uRL must have a full path, which means it must include the
 * name of the item.
 *







|
<







162
163
164
165
166
167
168
169

170
171
172
173
174
175
176
 * name of the item.
 *
 * This method is not available for all URLs.
 *
 * @param source The URL to the item for which a link should be created
 * @param destination The URL to the item which should link to the source
 */
- (void)linkItemAtURL: (OFURL *)source toURL: (OFURL *)destination;


/**
 * @brief Creates a symbolic link for an item.
 *
 * The destination uRL must have a full path, which means it must include the
 * name of the item.
 *
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
 *
 * @param source The file, directory or symbolic link to copy
 * @param destination The destination URL
 * @return True if an efficient copy was performed, false if an efficient copy
 *	   was not possible. Note that errors while performing a copy are
 *	   reported via exceptions and not by returning false!
 */
- (bool)copyItemAtURL: (OFURL *)source
		toURL: (OFURL *)destination;

/**
 * @brief Tries to efficiently move an item. If a move would only be possible
 *	  by copying the source and deleting it, it returns false.
 *
 * The destination URL must have a full path, which means it must include the
 * name of the item.
 *
 * If the destination is on a different logical device or uses a different
 * scheme, an efficient move is not possible and false is returned.
 *
 * @param source The item to rename
 * @param destination The new name for the item
 * @return True if an efficient move was performed, false if an efficient move
 *	   was not possible. Note that errors while performing a move are
 *	   reported via exceptions and not by returning false!
 */
- (bool)moveItemAtURL: (OFURL *)source
		toURL: (OFURL *)destination;
@end

OF_ASSUME_NONNULL_END







|
<

















|
<



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
 *
 * @param source The file, directory or symbolic link to copy
 * @param destination The destination URL
 * @return True if an efficient copy was performed, false if an efficient copy
 *	   was not possible. Note that errors while performing a copy are
 *	   reported via exceptions and not by returning false!
 */
- (bool)copyItemAtURL: (OFURL *)source toURL: (OFURL *)destination;


/**
 * @brief Tries to efficiently move an item. If a move would only be possible
 *	  by copying the source and deleting it, it returns false.
 *
 * The destination URL must have a full path, which means it must include the
 * name of the item.
 *
 * If the destination is on a different logical device or uses a different
 * scheme, an efficient move is not possible and false is returned.
 *
 * @param source The item to rename
 * @param destination The new name for the item
 * @return True if an efficient move was performed, false if an efficient move
 *	   was not possible. Note that errors while performing a move are
 *	   reported via exceptions and not by returning false!
 */
- (bool)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination;

@end

OF_ASSUME_NONNULL_END

Modified src/OFURLHandler.m from [2d065f473d] to [0e5a95e00a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
# import "OFHTTPURLHandler.h"
#endif

static OFMutableDictionary OF_GENERIC(OFString *, OFURLHandler *) *handlers;
#ifdef OF_HAVE_THREADS
static OFMutex *mutex;






#endif

@implementation OFURLHandler
@synthesize scheme = _scheme;

+ (void)initialize
{
	if (self != [OFURLHandler class])
		return;

	handlers = [[OFMutableDictionary alloc] init];
#ifdef OF_HAVE_THREADS
	mutex = [[OFMutex alloc] init];

#endif

#ifdef OF_HAVE_FILES
	[self registerClass: [OFFileURLHandler class]
		  forScheme: @"file"];
#endif
#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
	[self registerClass: [OFHTTPURLHandler class]
		  forScheme: @"http"];
	[self registerClass: [OFHTTPURLHandler class]
		  forScheme: @"https"];
#endif
}

+ (bool)registerClass: (Class)class
	    forScheme: (OFString *)scheme
{
#ifdef OF_HAVE_THREADS
	[mutex lock];
	@try {
#endif
		OFURLHandler *handler;

		if ([handlers objectForKey: scheme] != nil)
			return false;

		handler = [[class alloc] initWithScheme: scheme];
		@try {
			[handlers setObject: handler
				     forKey: scheme];
		} @finally {
			[handler release];
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		[mutex unlock];
	}







>
>
>
>
>
>













>



|
<


|
<
|
<



|
<












|
<







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
#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
# import "OFHTTPURLHandler.h"
#endif

static OFMutableDictionary OF_GENERIC(OFString *, OFURLHandler *) *handlers;
#ifdef OF_HAVE_THREADS
static OFMutex *mutex;

static void
releaseMutex(void)
{
	[mutex release];
}
#endif

@implementation OFURLHandler
@synthesize scheme = _scheme;

+ (void)initialize
{
	if (self != [OFURLHandler class])
		return;

	handlers = [[OFMutableDictionary alloc] init];
#ifdef OF_HAVE_THREADS
	mutex = [[OFMutex alloc] init];
	atexit(releaseMutex);
#endif

#ifdef OF_HAVE_FILES
	[self registerClass: [OFFileURLHandler class] forScheme: @"file"];

#endif
#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
	[self registerClass: [OFHTTPURLHandler class] forScheme: @"http"];

	[self registerClass: [OFHTTPURLHandler class] forScheme: @"https"];

#endif
}

+ (bool)registerClass: (Class)class forScheme: (OFString *)scheme

{
#ifdef OF_HAVE_THREADS
	[mutex lock];
	@try {
#endif
		OFURLHandler *handler;

		if ([handlers objectForKey: scheme] != nil)
			return false;

		handler = [[class alloc] initWithScheme: scheme];
		@try {
			[handlers setObject: handler forKey: scheme];

		} @finally {
			[handler release];
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		[mutex unlock];
	}
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
- (void)dealloc
{
	[_scheme release];

	[super dealloc];
}

- (OFStream *)openItemAtURL: (OFURL *)URL
		       mode: (OFString *)mode
{
	OF_UNRECOGNIZED_SELECTOR
}

- (of_file_attributes_t)attributesOfItemAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)setAttributes: (of_file_attributes_t)attributes
	  ofItemAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)fileExistsAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)directoryExistsAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)createDirectoryAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)removeItemAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)linkItemAtURL: (OFURL *)source
		toURL: (OFURL *)destination
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)createSymbolicLinkAtURL: (OFURL *)destination
	    withDestinationPath: (OFString *)source
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)copyItemAtURL: (OFURL *)source
		toURL: (OFURL *)destination
{
	return false;
}

- (bool)moveItemAtURL: (OFURL *)source
		toURL: (OFURL *)destination
{
	return false;
}
@end







|
<




|




<
|



















|









|
<










|
<




|
<




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
- (void)dealloc
{
	[_scheme release];

	[super dealloc];
}

- (OFStream *)openItemAtURL: (OFURL *)URL mode: (OFString *)mode

{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFFileAttributes)attributesOfItemAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}


- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)fileExistsAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)directoryExistsAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)createDirectoryAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFArray OF_GENERIC(OFURL *) *)contentsOfDirectoryAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)removeItemAtURL: (OFURL *)URL
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)linkItemAtURL: (OFURL *)source toURL: (OFURL *)destination

{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)createSymbolicLinkAtURL: (OFURL *)destination
	    withDestinationPath: (OFString *)source
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)copyItemAtURL: (OFURL *)source toURL: (OFURL *)destination

{
	return false;
}

- (bool)moveItemAtURL: (OFURL *)source toURL: (OFURL *)destination

{
	return false;
}
@end

Modified src/OFUTF8String+Private.h from [03f2449d2e] to [a7a276ddae].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFUTF8String.h from [e49ea36f6c] to [d381d1f2a1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	/*
	 * A pointer to the actual data.
	 *
	 * Since constant strings don't have `_storage`, they have to allocate
	 * it on the first access. Strings created at runtime just set the
	 * pointer to `&_storage`.
	 */
	struct of_string_utf8_ivars {
		char          *cString;
		size_t        cStringLength;
		bool          isUTF8;
		size_t        length;
		bool          hashed;
		unsigned long hash;
		char          *_Nullable freeWhenDone;
	} *restrict _s;
	struct of_string_utf8_ivars _storage;
}
@end

#ifdef __cplusplus
extern "C" {
#endif
extern int of_string_utf8_check(const char *, size_t, size_t *);
extern size_t of_string_utf8_get_index(const char *, size_t);
extern size_t of_string_utf8_get_position(const char *, size_t, size_t);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|




|

|

|






|
<
|





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
	/*
	 * A pointer to the actual data.
	 *
	 * Since constant strings don't have `_storage`, they have to allocate
	 * it on the first access. Strings created at runtime just set the
	 * pointer to `&_storage`.
	 */
	struct OFUTF8StringIvars {
		char          *cString;
		size_t        cStringLength;
		bool          isUTF8;
		size_t        length;
		bool          hasHash;
		unsigned long hash;
		bool          freeWhenDone;
	} *restrict _s;
	struct OFUTF8StringIvars _storage;
}
@end

#ifdef __cplusplus
extern "C" {
#endif
extern int OFUTF8StringCheck(const char *, size_t, size_t *);

extern size_t OFUTF8StringIndexToPosition(const char *, size_t, size_t);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFUTF8String.m from [d8b3a6972a] to [0c9d61efea].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#ifdef OF_HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#import "OFUTF8String.h"
#import "OFUTF8String+Private.h"

#import "OFArray.h"

#import "OFMutableUTF8String.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#import "of_asprintf.h"
#import "unicode.h"

extern const of_char16_t of_iso_8859_2_table[];
extern const size_t of_iso_8859_2_table_offset;
extern const of_char16_t of_iso_8859_3_table[];
extern const size_t of_iso_8859_3_table_offset;
extern const of_char16_t of_iso_8859_15_table[];
extern const size_t of_iso_8859_15_table_offset;
extern const of_char16_t of_windows_1251_table[];
extern const size_t of_windows_1251_table_offset;
extern const of_char16_t of_windows_1252_table[];
extern const size_t of_windows_1252_table_offset;
extern const of_char16_t of_codepage_437_table[];
extern const size_t of_codepage_437_table_offset;
extern const of_char16_t of_codepage_850_table[];
extern const size_t of_codepage_850_table_offset;
extern const of_char16_t of_codepage_858_table[];
extern const size_t of_codepage_858_table_offset;
extern const of_char16_t of_mac_roman_table[];
extern const size_t of_mac_roman_table_offset;
extern const of_char16_t of_koi8_r_table[];
extern const size_t of_koi8_r_table_offset;
extern const of_char16_t of_koi8_u_table[];
extern const size_t of_koi8_u_table_offset;

static inline int
memcasecmp(const char *first, const char *second, size_t length)
{
	for (size_t i = 0; i < length; i++) {
		unsigned char f = first[i];
		unsigned char s = second[i];

		f = of_ascii_toupper(f);
		s = of_ascii_toupper(s);

		if (f > s)
			return OF_ORDERED_DESCENDING;
		if (f < s)
			return OF_ORDERED_ASCENDING;
	}

	return OF_ORDERED_SAME;
}

int
of_string_utf8_check(const char *UTF8String, size_t UTF8Length, size_t *length)
{
	size_t tmpLength = UTF8Length;
	int isUTF8 = 0;

	for (size_t i = 0; i < UTF8Length; i++) {
		/* No sign of UTF-8 here */
		if OF_LIKELY (!(UTF8String[i] & 0x80))







>

>









<


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








|
|


|

|


|



|







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

#ifdef OF_HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#import "OFUTF8String.h"
#import "OFUTF8String+Private.h"
#import "OFASPrintF.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFMutableUTF8String.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"


#import "unicode.h"

extern const OFChar16 OFISO8859_2Table[];
extern const size_t OFISO8859_2TableOffset;
extern const OFChar16 OFISO8859_3Table[];
extern const size_t OFISO8859_3TableOffset;
extern const OFChar16 OFISO8859_15Table[];
extern const size_t OFISO8859_15TableOffset;
extern const OFChar16 OFWindows1251Table[];
extern const size_t OFWindows1251TableOffset;
extern const OFChar16 OFWindows1252Table[];
extern const size_t OFWindows1252TableOffset;
extern const OFChar16 OFCodepage437Table[];
extern const size_t OFCodepage437TableOffset;
extern const OFChar16 OFCodepage850Table[];
extern const size_t OFCodepage850TableOffset;
extern const OFChar16 OFCodepage858Table[];
extern const size_t OFCodepage858TableOffset;
extern const OFChar16 OFMacRomanTable[];
extern const size_t OFMacRomanTableOffset;
extern const OFChar16 OFKOI8RTable[];
extern const size_t OFKOI8RTableOffset;
extern const OFChar16 OFKOI8UTable[];
extern const size_t OFKOI8UTableOffset;

static inline int
memcasecmp(const char *first, const char *second, size_t length)
{
	for (size_t i = 0; i < length; i++) {
		unsigned char f = first[i];
		unsigned char s = second[i];

		f = OFASCIIToUpper(f);
		s = OFASCIIToUpper(s);

		if (f > s)
			return OFOrderedDescending;
		if (f < s)
			return OFOrderedAscending;
	}

	return OFOrderedSame;
}

int
OFUTF8StringCheck(const char *UTF8String, size_t UTF8Length, size_t *length)
{
	size_t tmpLength = UTF8Length;
	int isUTF8 = 0;

	for (size_t i = 0; i < UTF8Length; i++) {
		/* No sign of UTF-8 here */
		if OF_LIKELY (!(UTF8String[i] & 0x80))
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
	if (length != NULL)
		*length = tmpLength;

	return isUTF8;
}

size_t
of_string_utf8_get_index(const char *string, size_t position)
{
	size_t idx = position;

	for (size_t i = 0; i < position; i++)
		if OF_UNLIKELY ((string[i] & 0xC0) == 0x80)
			idx--;

	return idx;
}

size_t
of_string_utf8_get_position(const char *string, size_t idx, size_t length)
{
	for (size_t i = 0; i <= idx; i++)
		if OF_UNLIKELY ((string[i] & 0xC0) == 0x80)
			if (++idx > length)
				@throw [OFInvalidFormatException exception];

	return idx;
}

@implementation OFUTF8String
- (instancetype)init
{
	self = [super init];

	@try {
		_s = &_storage;

		_s->cString = [self allocMemoryWithSize: 1];
		_s->cString[0] = '\0';
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







|











|

















|
|







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
	if (length != NULL)
		*length = tmpLength;

	return isUTF8;
}

size_t
positionToIndex(const char *string, size_t position)
{
	size_t idx = position;

	for (size_t i = 0; i < position; i++)
		if OF_UNLIKELY ((string[i] & 0xC0) == 0x80)
			idx--;

	return idx;
}

size_t
OFUTF8StringIndexToPosition(const char *string, size_t idx, size_t length)
{
	for (size_t i = 0; i <= idx; i++)
		if OF_UNLIKELY ((string[i] & 0xC0) == 0x80)
			if (++idx > length)
				@throw [OFInvalidFormatException exception];

	return idx;
}

@implementation OFUTF8String
- (instancetype)init
{
	self = [super init];

	@try {
		_s = &_storage;

		_s->cString = OFAllocZeroedMemory(1, 1);
		_s->freeWhenDone = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
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
		}

		_s = &_storage;

		_s->cString = storage;
		_s->cStringLength = UTF8StringLength;

		switch (of_string_utf8_check(UTF8String, UTF8StringLength,
		    &_s->length)) {
		case 1:
			_s->isUTF8 = true;
			break;
		case -1:
			@throw [OFInvalidEncodingException exception];
		}

		memcpy(_s->cString, UTF8String, UTF8StringLength);
		_s->cString[UTF8StringLength] = 0;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
			 length: (size_t)cStringLength
{
	self = [super init];

	@try {
		const of_char16_t *table;
		size_t tableOffset, j;

		if (encoding == OF_STRING_ENCODING_UTF_8 &&
		    cStringLength >= 3 &&
		    memcmp(cString, "\xEF\xBB\xBF", 3) == 0) {
			cString += 3;
			cStringLength -= 3;
		}

		_s = &_storage;

		_s->cString = [self allocMemoryWithSize: cStringLength + 1];
		_s->cStringLength = cStringLength;


		if (encoding == OF_STRING_ENCODING_UTF_8 ||
		    encoding == OF_STRING_ENCODING_ASCII) {
			switch (of_string_utf8_check(cString, cStringLength,
			    &_s->length)) {
			case 1:
				if (encoding == OF_STRING_ENCODING_ASCII)
					@throw [OFInvalidEncodingException
					    exception];

				_s->isUTF8 = true;
				break;
			case -1:
				@throw [OFInvalidEncodingException exception];
			}

			memcpy(_s->cString, cString, cStringLength);
			_s->cString[cStringLength] = 0;

			return self;
		}

		/* All other encodings we support are single byte encodings */
		_s->length = cStringLength;

		if (encoding == OF_STRING_ENCODING_ISO_8859_1) {
			j = 0;
			for (size_t i = 0; i < cStringLength; i++) {
				char buffer[4];
				size_t bytes;

				if (!(cString[i] & 0x80)) {
					_s->cString[j++] = cString[i];
					continue;
				}

				_s->isUTF8 = true;
				bytes = of_string_utf8_encode(
				    (uint8_t)cString[i], buffer);

				if (bytes == 0)
					@throw [OFInvalidEncodingException
					    exception];

				_s->cStringLength += bytes - 1;
				_s->cString = [self
				    resizeMemory: _s->cString
					    size: _s->cStringLength + 1];

				memcpy(_s->cString + j, buffer, bytes);
				j += bytes;
			}

			_s->cString[_s->cStringLength] = 0;

			return self;
		}

		switch (encoding) {
#define CASE(encoding, var)			\
	case encoding:				\
		table = var;			\
		tableOffset = var##_offset;	\
		break;
#ifdef HAVE_ISO_8859_2
		CASE(OF_STRING_ENCODING_ISO_8859_2, of_iso_8859_2_table)
#endif
#ifdef HAVE_ISO_8859_3
		CASE(OF_STRING_ENCODING_ISO_8859_3, of_iso_8859_3_table)
#endif
#ifdef HAVE_ISO_8859_15
		CASE(OF_STRING_ENCODING_ISO_8859_15, of_iso_8859_15_table)
#endif
#ifdef HAVE_WINDOWS_1251
		CASE(OF_STRING_ENCODING_WINDOWS_1251, of_windows_1251_table)
#endif
#ifdef HAVE_WINDOWS_1252
		CASE(OF_STRING_ENCODING_WINDOWS_1252, of_windows_1252_table)
#endif
#ifdef HAVE_CODEPAGE_437
		CASE(OF_STRING_ENCODING_CODEPAGE_437, of_codepage_437_table)
#endif
#ifdef HAVE_CODEPAGE_850
		CASE(OF_STRING_ENCODING_CODEPAGE_850, of_codepage_850_table)
#endif
#ifdef HAVE_CODEPAGE_858
		CASE(OF_STRING_ENCODING_CODEPAGE_858, of_codepage_858_table)
#endif
#ifdef HAVE_MAC_ROMAN
		CASE(OF_STRING_ENCODING_MAC_ROMAN, of_mac_roman_table)
#endif
#ifdef HAVE_KOI8_R
		CASE(OF_STRING_ENCODING_KOI8_R, of_koi8_r_table)
#endif
#ifdef HAVE_KOI8_U
		CASE(OF_STRING_ENCODING_KOI8_U, of_koi8_u_table)
#endif
#undef CASE
		default:
			@throw [OFInvalidEncodingException exception];
		}

		j = 0;
		for (size_t i = 0; i < cStringLength; i++) {
			unsigned char character = (unsigned char)cString[i];
			of_unichar_t unichar;
			char buffer[4];
			size_t byteLength;

			if (character < tableOffset) {
				_s->cString[j++] = cString[i];
				continue;
			}

			unichar = table[character - tableOffset];

			if (unichar == 0xFFFF)
				@throw [OFInvalidEncodingException exception];

			_s->isUTF8 = true;
			byteLength = of_string_utf8_encode(unichar, buffer);

			if (byteLength == 0)
				@throw [OFInvalidEncodingException exception];

			_s->cStringLength += byteLength - 1;
			_s->cString = [self
			    resizeMemory: _s->cString
				    size: _s->cStringLength + 1];

			memcpy(_s->cString + j, buffer, byteLength);
			j += byteLength;
		}

		_s->cString[_s->cStringLength] = 0;
	} @catch (id e) {







|



















|





|


|








|

>

|
|
|


|


















|











|







|
<
|











|
|
|
|
|

|


|


|


|


|


|


|


|


|


|


|









|














|





|
<
|







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
		}

		_s = &_storage;

		_s->cString = storage;
		_s->cStringLength = UTF8StringLength;

		switch (OFUTF8StringCheck(UTF8String, UTF8StringLength,
		    &_s->length)) {
		case 1:
			_s->isUTF8 = true;
			break;
		case -1:
			@throw [OFInvalidEncodingException exception];
		}

		memcpy(_s->cString, UTF8String, UTF8StringLength);
		_s->cString[UTF8StringLength] = 0;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
			 length: (size_t)cStringLength
{
	self = [super init];

	@try {
		const OFChar16 *table;
		size_t tableOffset, j;

		if (encoding == OFStringEncodingUTF8 &&
		    cStringLength >= 3 &&
		    memcmp(cString, "\xEF\xBB\xBF", 3) == 0) {
			cString += 3;
			cStringLength -= 3;
		}

		_s = &_storage;

		_s->cString = OFAllocMemory(cStringLength + 1, 1);
		_s->cStringLength = cStringLength;
		_s->freeWhenDone = true;

		if (encoding == OFStringEncodingUTF8 ||
		    encoding == OFStringEncodingASCII) {
			switch (OFUTF8StringCheck(cString, cStringLength,
			    &_s->length)) {
			case 1:
				if (encoding == OFStringEncodingASCII)
					@throw [OFInvalidEncodingException
					    exception];

				_s->isUTF8 = true;
				break;
			case -1:
				@throw [OFInvalidEncodingException exception];
			}

			memcpy(_s->cString, cString, cStringLength);
			_s->cString[cStringLength] = 0;

			return self;
		}

		/* All other encodings we support are single byte encodings */
		_s->length = cStringLength;

		if (encoding == OFStringEncodingISO8859_1) {
			j = 0;
			for (size_t i = 0; i < cStringLength; i++) {
				char buffer[4];
				size_t bytes;

				if (!(cString[i] & 0x80)) {
					_s->cString[j++] = cString[i];
					continue;
				}

				_s->isUTF8 = true;
				bytes = OFUTF8StringEncode(
				    (uint8_t)cString[i], buffer);

				if (bytes == 0)
					@throw [OFInvalidEncodingException
					    exception];

				_s->cStringLength += bytes - 1;
				_s->cString = OFResizeMemory(_s->cString,

				    _s->cStringLength + 1, 1);

				memcpy(_s->cString + j, buffer, bytes);
				j += bytes;
			}

			_s->cString[_s->cStringLength] = 0;

			return self;
		}

		switch (encoding) {
#define CASE(encoding, var)				\
		case encoding:				\
			table = var;			\
			tableOffset = var##Offset;	\
			break;
#ifdef HAVE_ISO_8859_2
		CASE(OFStringEncodingISO8859_2, OFISO8859_2Table)
#endif
#ifdef HAVE_ISO_8859_3
		CASE(OFStringEncodingISO8859_3, OFISO8859_3Table)
#endif
#ifdef HAVE_ISO_8859_15
		CASE(OFStringEncodingISO8859_15, OFISO8859_15Table)
#endif
#ifdef HAVE_WINDOWS_1251
		CASE(OFStringEncodingWindows1251, OFWindows1251Table)
#endif
#ifdef HAVE_WINDOWS_1252
		CASE(OFStringEncodingWindows1252, OFWindows1252Table)
#endif
#ifdef HAVE_CODEPAGE_437
		CASE(OFStringEncodingCodepage437, OFCodepage437Table)
#endif
#ifdef HAVE_CODEPAGE_850
		CASE(OFStringEncodingCodepage850, OFCodepage850Table)
#endif
#ifdef HAVE_CODEPAGE_858
		CASE(OFStringEncodingCodepage858, OFCodepage858Table)
#endif
#ifdef HAVE_MAC_ROMAN
		CASE(OFStringEncodingMacRoman, OFMacRomanTable)
#endif
#ifdef HAVE_KOI8_R
		CASE(OFStringEncodingKOI8R, OFKOI8RTable)
#endif
#ifdef HAVE_KOI8_U
		CASE(OFStringEncodingKOI8U, OFKOI8UTable)
#endif
#undef CASE
		default:
			@throw [OFInvalidEncodingException exception];
		}

		j = 0;
		for (size_t i = 0; i < cStringLength; i++) {
			unsigned char character = (unsigned char)cString[i];
			OFUnichar unichar;
			char buffer[4];
			size_t byteLength;

			if (character < tableOffset) {
				_s->cString[j++] = cString[i];
				continue;
			}

			unichar = table[character - tableOffset];

			if (unichar == 0xFFFF)
				@throw [OFInvalidEncodingException exception];

			_s->isUTF8 = true;
			byteLength = OFUTF8StringEncode(unichar, buffer);

			if (byteLength == 0)
				@throw [OFInvalidEncodingException exception];

			_s->cStringLength += byteLength - 1;
			_s->cString = OFResizeMemory(_s->cString,

			    _s->cStringLength + 1, 1);

			memcpy(_s->cString + j, buffer, byteLength);
			j += byteLength;
		}

		_s->cString[_s->cStringLength] = 0;
	} @catch (id e) {
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

}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
				  length: (size_t)UTF8StringLength
			    freeWhenDone: (bool)freeWhenDone
{
	@try {
		self = [super init];
	} @catch (id e) {
		if (freeWhenDone)
			free(UTF8String);
		@throw e;
	}

	@try {
		_s = &_storage;

		if (freeWhenDone)
			_s->freeWhenDone = UTF8String;

		if (UTF8StringLength >= 3 &&
		    memcmp(UTF8String, "\xEF\xBB\xBF", 3) == 0) {
			UTF8String += 3;
			UTF8StringLength -= 3;
		}

		_s->cString = (char *)UTF8String;
		_s->cStringLength = UTF8StringLength;

		switch (of_string_utf8_check(UTF8String, UTF8StringLength,
		    &_s->length)) {
		case 1:
			_s->isUTF8 = true;
			break;
		case -1:
			@throw [OFInvalidEncodingException exception];
		}




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

	return self;
}







<
|
<
<
<
<
<




<
<
<






<
<
<
|







>
>
>
>







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

}

- (instancetype)initWithUTF8StringNoCopy: (char *)UTF8String
				  length: (size_t)UTF8StringLength
			    freeWhenDone: (bool)freeWhenDone
{

	self = [super init];






	@try {
		_s = &_storage;




		if (UTF8StringLength >= 3 &&
		    memcmp(UTF8String, "\xEF\xBB\xBF", 3) == 0) {
			UTF8String += 3;
			UTF8StringLength -= 3;
		}




		switch (OFUTF8StringCheck(UTF8String, UTF8StringLength,
		    &_s->length)) {
		case 1:
			_s->isUTF8 = true;
			break;
		case -1:
			@throw [OFInvalidEncodingException exception];
		}

		_s->cString = (char *)UTF8String;
		_s->cStringLength = UTF8StringLength;
		_s->freeWhenDone = freeWhenDone;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
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
		    [string isKindOfClass: [OFMutableUTF8String class]])
			_s->isUTF8 = ((OFUTF8String *)string)->_s->isUTF8;
		else
			_s->isUTF8 = true;

		_s->length = string.length;

		_s->cString = [self allocMemoryWithSize: _s->cStringLength + 1];
		memcpy(_s->cString, string.UTF8String, _s->cStringLength + 1);

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

	return self;
}

- (instancetype)initWithCharacters: (const of_unichar_t *)characters
			    length: (size_t)length
{
	self = [super init];

	@try {
		size_t j;

		_s = &_storage;

		_s->cString = [self allocMemoryWithSize: (length * 4) + 1];
		_s->length = length;


		j = 0;
		for (size_t i = 0; i < length; i++) {
			size_t len = of_string_utf8_encode(characters[i],
			    _s->cString + j);

			if (len == 0)
				@throw [OFInvalidEncodingException exception];

			if (len > 1)
				_s->isUTF8 = true;

			j += len;
		}

		_s->cString[j] = '\0';
		_s->cStringLength = j;

		@try {
			_s->cString = [self resizeMemory: _s->cString
						    size: j + 1];
		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only tried to make it smaller */
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithUTF16String: (const of_char16_t *)string
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	self = [super init];

	@try {
		size_t j;
		bool swap = false;

		if (length > 0 && *string == 0xFEFF) {
			string++;
			length--;
		} else if (length > 0 && *string == 0xFFFE) {
			swap = true;
			string++;
			length--;
		} else if (byteOrder != OF_BYTE_ORDER_NATIVE)
			swap = true;

		_s = &_storage;

		_s->cString = [self allocMemoryWithSize: (length * 4) + 1];
		_s->length = length;


		j = 0;
		for (size_t i = 0; i < length; i++) {
			of_unichar_t character =
			    (swap ? OF_BSWAP16(string[i]) : string[i]);
			size_t len;

			/* Missing high surrogate */
			if ((character & 0xFC00) == 0xDC00)
				@throw [OFInvalidEncodingException exception];

			if ((character & 0xFC00) == 0xD800) {
				of_char16_t nextCharacter;

				if (length <= i + 1)
					@throw [OFInvalidEncodingException
					    exception];

				nextCharacter = (swap
				    ? OF_BSWAP16(string[i + 1])
				    : string[i + 1]);

				if ((nextCharacter & 0xFC00) != 0xDC00)
					@throw [OFInvalidEncodingException
					    exception];

				character = (((character & 0x3FF) << 10) |
				    (nextCharacter & 0x3FF)) + 0x10000;

				i++;
				_s->length--;
			}

			len = of_string_utf8_encode(character, _s->cString + j);

			if (len == 0)
				@throw [OFInvalidEncodingException exception];

			if (len > 1)
				_s->isUTF8 = true;

			j += len;
		}

		_s->cString[j] = '\0';
		_s->cStringLength = j;

		@try {
			_s->cString = [self resizeMemory: _s->cString
						    size: j + 1];
		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only tried to make it smaller */
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithUTF32String: (const of_char32_t *)characters
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	self = [super init];

	@try {
		size_t j;
		bool swap = false;

		if (length > 0 && *characters == 0xFEFF) {
			characters++;
			length--;
		} else if (length > 0 && *characters == 0xFFFE0000) {
			swap = true;
			characters++;
			length--;
		} else if (byteOrder != OF_BYTE_ORDER_NATIVE)
			swap = true;

		_s = &_storage;

		_s->cString = [self allocMemoryWithSize: (length * 4) + 1];
		_s->length = length;


		j = 0;
		for (size_t i = 0; i < length; i++) {
			char buffer[4];
			size_t len = of_string_utf8_encode(

			    (swap ? OF_BSWAP32(characters[i]) : characters[i]),
			    buffer);

			switch (len) {
			case 1:
				_s->cString[j++] = buffer[0];
				break;
			case 2:







|

>








|









|

>



|















|
<











|

|














|




|

>



|
|







|






|













|














|
<











|

|














|




|

>




|
>
|







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
		    [string isKindOfClass: [OFMutableUTF8String class]])
			_s->isUTF8 = ((OFUTF8String *)string)->_s->isUTF8;
		else
			_s->isUTF8 = true;

		_s->length = string.length;

		_s->cString = OFAllocMemory(_s->cStringLength + 1, 1);
		memcpy(_s->cString, string.UTF8String, _s->cStringLength + 1);
		_s->freeWhenDone = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithCharacters: (const OFUnichar *)characters
			    length: (size_t)length
{
	self = [super init];

	@try {
		size_t j;

		_s = &_storage;

		_s->cString = OFAllocMemory((length * 4) + 1, 1);
		_s->length = length;
		_s->freeWhenDone = true;

		j = 0;
		for (size_t i = 0; i < length; i++) {
			size_t len = OFUTF8StringEncode(characters[i],
			    _s->cString + j);

			if (len == 0)
				@throw [OFInvalidEncodingException exception];

			if (len > 1)
				_s->isUTF8 = true;

			j += len;
		}

		_s->cString[j] = '\0';
		_s->cStringLength = j;

		@try {
			_s->cString = OFResizeMemory(_s->cString, j + 1, 1);

		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only tried to make it smaller */
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithUTF16String: (const OFChar16 *)string
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	self = [super init];

	@try {
		size_t j;
		bool swap = false;

		if (length > 0 && *string == 0xFEFF) {
			string++;
			length--;
		} else if (length > 0 && *string == 0xFFFE) {
			swap = true;
			string++;
			length--;
		} else if (byteOrder != OFByteOrderNative)
			swap = true;

		_s = &_storage;

		_s->cString = OFAllocMemory((length * 4) + 1, 1);
		_s->length = length;
		_s->freeWhenDone = true;

		j = 0;
		for (size_t i = 0; i < length; i++) {
			OFUnichar character =
			    (swap ? OFByteSwap16(string[i]) : string[i]);
			size_t len;

			/* Missing high surrogate */
			if ((character & 0xFC00) == 0xDC00)
				@throw [OFInvalidEncodingException exception];

			if ((character & 0xFC00) == 0xD800) {
				OFChar16 nextCharacter;

				if (length <= i + 1)
					@throw [OFInvalidEncodingException
					    exception];

				nextCharacter = (swap
				    ? OFByteSwap16(string[i + 1])
				    : string[i + 1]);

				if ((nextCharacter & 0xFC00) != 0xDC00)
					@throw [OFInvalidEncodingException
					    exception];

				character = (((character & 0x3FF) << 10) |
				    (nextCharacter & 0x3FF)) + 0x10000;

				i++;
				_s->length--;
			}

			len = OFUTF8StringEncode(character, _s->cString + j);

			if (len == 0)
				@throw [OFInvalidEncodingException exception];

			if (len > 1)
				_s->isUTF8 = true;

			j += len;
		}

		_s->cString[j] = '\0';
		_s->cStringLength = j;

		@try {
			_s->cString = OFResizeMemory(_s->cString, j + 1, 1);

		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only tried to make it smaller */
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithUTF32String: (const OFChar32 *)characters
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	self = [super init];

	@try {
		size_t j;
		bool swap = false;

		if (length > 0 && *characters == 0xFEFF) {
			characters++;
			length--;
		} else if (length > 0 && *characters == 0xFFFE0000) {
			swap = true;
			characters++;
			length--;
		} else if (byteOrder != OFByteOrderNative)
			swap = true;

		_s = &_storage;

		_s->cString = OFAllocMemory((length * 4) + 1, 1);
		_s->length = length;
		_s->freeWhenDone = true;

		j = 0;
		for (size_t i = 0; i < length; i++) {
			char buffer[4];
			size_t len = OFUTF8StringEncode((swap
			    ? OFByteSwap32(characters[i])
			    : characters[i]),
			    buffer);

			switch (len) {
			case 1:
				_s->cString[j++] = buffer[0];
				break;
			case 2:
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
			}
		}

		_s->cString[j] = '\0';
		_s->cStringLength = j;

		@try {
			_s->cString = [self resizeMemory: _s->cString
						    size: j + 1];
		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only tried to make it smaller */
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}







|
<







642
643
644
645
646
647
648
649

650
651
652
653
654
655
656
			}
		}

		_s->cString[j] = '\0';
		_s->cStringLength = j;

		@try {
			_s->cString = OFResizeMemory(_s->cString, j + 1, 1);

		} @catch (OFOutOfMemoryException *e) {
			/* We don't care, as we only tried to make it smaller */
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}
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
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
		int cStringLength;

		if (format == nil)
			@throw [OFInvalidArgumentException exception];

		_s = &_storage;

		if ((cStringLength = of_vasprintf(&tmp, format.UTF8String,
		    arguments)) == -1)
			@throw [OFInvalidFormatException exception];

		_s->cStringLength = cStringLength;

		@try {
			switch (of_string_utf8_check(tmp, cStringLength,
			    &_s->length)) {
			case 1:
				_s->isUTF8 = true;
				break;
			case -1:
				@throw [OFInvalidEncodingException exception];
			}

			_s->cString = [self
			    allocMemoryWithSize: cStringLength + 1];
			memcpy(_s->cString, tmp, cStringLength + 1);

		} @finally {
			free(tmp);
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_s != NULL && _s->freeWhenDone != NULL)
		free(_s->freeWhenDone);

	[super dealloc];
}

- (size_t)getCString: (char *)cString
	   maxLength: (size_t)maxLength
	    encoding: (of_string_encoding_t)encoding
{
	switch (encoding) {
	case OF_STRING_ENCODING_ASCII:
		if (_s->isUTF8)
			@throw [OFInvalidEncodingException exception];
		/* intentional fall-through */
	case OF_STRING_ENCODING_UTF_8:
		if (_s->cStringLength + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		memcpy(cString, _s->cString, _s->cStringLength + 1);

		return _s->cStringLength;
	default:
		return [super getCString: cString
			       maxLength: maxLength
				encoding: encoding];
	}
}

- (const char *)cStringWithEncoding: (of_string_encoding_t)encoding
{
	switch (encoding) {
	case OF_STRING_ENCODING_ASCII:
		if (_s->isUTF8)
			@throw [OFInvalidEncodingException exception];
		/* intentional fall-through */
	case OF_STRING_ENCODING_UTF_8:
		return _s->cString;
	default:
		return [super cStringWithEncoding: encoding];
	}
}

- (const char *)UTF8String
{
	return _s->cString;
}

- (size_t)length
{
	return _s->length;
}

- (size_t)cStringLengthWithEncoding: (of_string_encoding_t)encoding
{
	switch (encoding) {
	case OF_STRING_ENCODING_UTF_8:
	case OF_STRING_ENCODING_ASCII:
		return _s->cStringLength;
	default:
		return [super cStringLengthWithEncoding: encoding];
	}
}

- (size_t)UTF8StringLength
{
	return _s->cStringLength;
}

- (bool)isEqual: (id)object
{
	OFUTF8String *otherString;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFString class]])
		return false;

	otherString = object;

	if (otherString.UTF8StringLength != _s->cStringLength ||
	    otherString.length != _s->length)
		return false;

	if (([otherString isKindOfClass: [OFUTF8String class]] ||
	    [otherString isKindOfClass: [OFMutableUTF8String class]]) &&
	    _s->hashed && otherString->_s->hashed &&
	    _s->hash != otherString->_s->hash)
		return false;

	if (strcmp(_s->cString, otherString.UTF8String) != 0)
		return false;

	return true;
}

- (of_comparison_result_t)compare: (id <OFComparing>)object
{
	OFString *otherString;
	size_t otherCStringLength, minimumCStringLength;
	int compare;

	if (object == self)
		return OF_ORDERED_SAME;

	if (![(id)object isKindOfClass: [OFString class]])
		@throw [OFInvalidArgumentException exception];

	otherString = (OFString *)object;
	otherCStringLength = otherString.UTF8StringLength;
	minimumCStringLength = (_s->cStringLength > otherCStringLength
	    ? otherCStringLength : _s->cStringLength);

	if ((compare = memcmp(_s->cString, otherString.UTF8String,
	    minimumCStringLength)) == 0) {
		if (_s->cStringLength > otherCStringLength)
			return OF_ORDERED_DESCENDING;
		if (_s->cStringLength < otherCStringLength)
			return OF_ORDERED_ASCENDING;
		return OF_ORDERED_SAME;
	}

	if (compare > 0)
		return OF_ORDERED_DESCENDING;
	else
		return OF_ORDERED_ASCENDING;
}

- (of_comparison_result_t)caseInsensitiveCompare: (OFString *)otherString
{
	const char *otherCString;
	size_t otherCStringLength, minimumCStringLength;
#ifdef OF_HAVE_UNICODE_TABLES
	size_t i, j;
#endif
	int compare;

	if (otherString == self)
		return OF_ORDERED_SAME;

	if (![otherString isKindOfClass: [OFString class]])
		@throw [OFInvalidArgumentException exception];

	otherCString = otherString.UTF8String;
	otherCStringLength = otherString.UTF8StringLength;

#ifdef OF_HAVE_UNICODE_TABLES
	if (!_s->isUTF8) {
#endif
		minimumCStringLength = (_s->cStringLength > otherCStringLength
		    ? otherCStringLength : _s->cStringLength);

		if ((compare = memcasecmp(_s->cString, otherCString,
		    minimumCStringLength)) == 0) {
			if (_s->cStringLength > otherCStringLength)
				return OF_ORDERED_DESCENDING;
			if (_s->cStringLength < otherCStringLength)
				return OF_ORDERED_ASCENDING;
			return OF_ORDERED_SAME;
		}

		if (compare > 0)
			return OF_ORDERED_DESCENDING;
		else
			return OF_ORDERED_ASCENDING;
#ifdef OF_HAVE_UNICODE_TABLES
	}

	i = j = 0;

	while (i < _s->cStringLength && j < otherCStringLength) {
		of_unichar_t c1, c2;
		ssize_t l1, l2;

		l1 = of_string_utf8_decode(_s->cString + i,
		    _s->cStringLength - i, &c1);
		l2 = of_string_utf8_decode(otherCString + j,
		    otherCStringLength - j, &c2);

		if (l1 <= 0 || l2 <= 0 || c1 > 0x10FFFF || c2 > 0x10FFFF)
			@throw [OFInvalidEncodingException exception];

		if (c1 >> 8 < OF_UNICODE_CASEFOLDING_TABLE_SIZE) {
			of_unichar_t tc =
			    of_unicode_casefolding_table[c1 >> 8][c1 & 0xFF];

			if (tc)
				c1 = tc;
		}

		if (c2 >> 8 < OF_UNICODE_CASEFOLDING_TABLE_SIZE) {
			of_unichar_t tc =
			    of_unicode_casefolding_table[c2 >> 8][c2 & 0xFF];

			if (tc)
				c2 = tc;
		}

		if (c1 > c2)
			return OF_ORDERED_DESCENDING;
		if (c1 < c2)
			return OF_ORDERED_ASCENDING;

		i += l1;
		j += l2;
	}

	if (_s->cStringLength - i > otherCStringLength - j)
		return OF_ORDERED_DESCENDING;
	else if (_s->cStringLength - i < otherCStringLength - j)
		return OF_ORDERED_ASCENDING;
#endif

	return OF_ORDERED_SAME;
}

- (unsigned long)hash
{
	uint32_t hash;

	if (_s->hashed)
		return _s->hash;

	OF_HASH_INIT(hash);

	for (size_t i = 0; i < _s->cStringLength; i++) {
		of_unichar_t c;
		ssize_t length;

		if ((length = of_string_utf8_decode(_s->cString + i,
		    _s->cStringLength - i, &c)) <= 0)
			@throw [OFInvalidEncodingException exception];

		OF_HASH_ADD(hash, (c & 0xFF0000) >> 16);
		OF_HASH_ADD(hash, (c & 0x00FF00) >> 8);
		OF_HASH_ADD(hash, c & 0x0000FF);

		i += length - 1;
	}

	OF_HASH_FINALIZE(hash);

	_s->hash = hash;
	_s->hashed = true;

	return hash;
}

- (of_unichar_t)characterAtIndex: (size_t)idx
{
	of_unichar_t character;

	if (idx >= _s->length)
		@throw [OFOutOfRangeException exception];

	if (!_s->isUTF8)
		return _s->cString[idx];

	idx = of_string_utf8_get_position(_s->cString, idx, _s->cStringLength);

	if (of_string_utf8_decode(_s->cString + idx,
	    _s->cStringLength - idx, &character) <= 0)
		@throw [OFInvalidEncodingException exception];

	return character;
}

- (void)getCharacters: (of_unichar_t *)buffer
	      inRange: (of_range_t)range
{
	/* TODO: Could be slightly optimized */
	void *pool = objc_autoreleasePoolPush();
	const of_unichar_t *characters = self.characters;

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

	memcpy(buffer, characters + range.location,
	    range.length * sizeof(of_unichar_t));

	objc_autoreleasePoolPop(pool);
}

- (of_range_t)rangeOfString: (OFString *)string
		    options: (int)options
		      range: (of_range_t)range
{
	const char *cString = string.UTF8String;
	size_t cStringLength = string.UTF8StringLength;
	size_t rangeLocation, rangeLength;

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

	if (_s->isUTF8) {
		rangeLocation = of_string_utf8_get_position(
		    _s->cString, range.location, _s->cStringLength);
		rangeLength = of_string_utf8_get_position(
		    _s->cString + rangeLocation, range.length,
		    _s->cStringLength - rangeLocation);
	} else {
		rangeLocation = range.location;
		rangeLength = range.length;
	}

	if (cStringLength == 0)
		return of_range(0, 0);

	if (cStringLength > rangeLength)
		return of_range(OF_NOT_FOUND, 0);

	if (options & OF_STRING_SEARCH_BACKWARDS) {
		for (size_t i = rangeLength - cStringLength;; i--) {
			if (memcmp(_s->cString + rangeLocation + i, cString,
			    cStringLength) == 0) {
				range.location += of_string_utf8_get_index(
				    _s->cString + rangeLocation, i);
				range.length = string.length;

				return range;
			}

			/* Did not match and we're at the last char */
			if (i == 0)
				return of_range(OF_NOT_FOUND, 0);
		}
	} else {
		for (size_t i = 0; i <= rangeLength - cStringLength; i++) {
			if (memcmp(_s->cString + rangeLocation + i, cString,
			    cStringLength) == 0) {
				range.location += of_string_utf8_get_index(
				    _s->cString + rangeLocation, i);
				range.length = string.length;

				return range;
			}
		}
	}

	return of_range(OF_NOT_FOUND, 0);
}

- (bool)containsString: (OFString *)string
{
	const char *cString = string.UTF8String;
	size_t cStringLength = string.UTF8StringLength;

	if (cStringLength == 0)
		return true;

	if (cStringLength > _s->cStringLength)
		return false;

	for (size_t i = 0; i <= _s->cStringLength - cStringLength; i++)
		if (memcmp(_s->cString + i, cString, cStringLength) == 0)
			return true;

	return false;
}

- (OFString *)substringWithRange: (of_range_t)range
{
	size_t start = range.location;
	size_t end = range.location + range.length;

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

	if (_s->isUTF8) {
		start = of_string_utf8_get_position(_s->cString, start,
		    _s->cStringLength);
		end = of_string_utf8_get_position(_s->cString, end,
		    _s->cStringLength);
	}

	return [OFString stringWithUTF8String: _s->cString + start
				       length: end - start];
}








|






|








|
<

>

|











|
|






|


|



|













|


|



|
















|


|
|













|







|

|
|


|
|
<
|


|





|

<



|
|

|


<
|



|


|

|
|



|

|


|








|
|

<
<
<
|
|










|

|
|



|

|






|


|

|





|
|
|





|
|
|






|

|






|

|


|




|

|


|


|


|



|
|
|




|


|




|

|







|

|
|





|
<



|






|




|
|
|










|

|








|


|

|



|








|





|








|




















|








|

|







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
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
		int cStringLength;

		if (format == nil)
			@throw [OFInvalidArgumentException exception];

		_s = &_storage;

		if ((cStringLength = OFVASPrintF(&tmp, format.UTF8String,
		    arguments)) == -1)
			@throw [OFInvalidFormatException exception];

		_s->cStringLength = cStringLength;

		@try {
			switch (OFUTF8StringCheck(tmp, cStringLength,
			    &_s->length)) {
			case 1:
				_s->isUTF8 = true;
				break;
			case -1:
				@throw [OFInvalidEncodingException exception];
			}

			_s->cString = OFAllocMemory(cStringLength + 1, 1);

			memcpy(_s->cString, tmp, cStringLength + 1);
			_s->freeWhenDone = true;
		} @finally {
			OFFreeMemory(tmp);
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_s != NULL && _s->freeWhenDone)
		OFFreeMemory(_s->cString);

	[super dealloc];
}

- (size_t)getCString: (char *)cString
	   maxLength: (size_t)maxLength
	    encoding: (OFStringEncoding)encoding
{
	switch (encoding) {
	case OFStringEncodingASCII:
		if (_s->isUTF8)
			@throw [OFInvalidEncodingException exception];
		/* intentional fall-through */
	case OFStringEncodingUTF8:
		if (_s->cStringLength + 1 > maxLength)
			@throw [OFOutOfRangeException exception];

		memcpy(cString, _s->cString, _s->cStringLength + 1);

		return _s->cStringLength;
	default:
		return [super getCString: cString
			       maxLength: maxLength
				encoding: encoding];
	}
}

- (const char *)cStringWithEncoding: (OFStringEncoding)encoding
{
	switch (encoding) {
	case OFStringEncodingASCII:
		if (_s->isUTF8)
			@throw [OFInvalidEncodingException exception];
		/* intentional fall-through */
	case OFStringEncodingUTF8:
		return _s->cString;
	default:
		return [super cStringWithEncoding: encoding];
	}
}

- (const char *)UTF8String
{
	return _s->cString;
}

- (size_t)length
{
	return _s->length;
}

- (size_t)cStringLengthWithEncoding: (OFStringEncoding)encoding
{
	switch (encoding) {
	case OFStringEncodingUTF8:
	case OFStringEncodingASCII:
		return _s->cStringLength;
	default:
		return [super cStringLengthWithEncoding: encoding];
	}
}

- (size_t)UTF8StringLength
{
	return _s->cStringLength;
}

- (bool)isEqual: (id)object
{
	OFUTF8String *string;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFString class]])
		return false;

	string = object;

	if (string.UTF8StringLength != _s->cStringLength ||
	    string.length != _s->length)
		return false;

	if (([string isKindOfClass: [OFUTF8String class]] ||
	    [string isKindOfClass: [OFMutableUTF8String class]]) &&

	    _s->hasHash && string->_s->hasHash && _s->hash != string->_s->hash)
		return false;

	if (strcmp(_s->cString, string.UTF8String) != 0)
		return false;

	return true;
}

- (OFComparisonResult)compare: (OFString *)string
{

	size_t otherCStringLength, minimumCStringLength;
	int compare;

	if (string == self)
		return OFOrderedSame;

	if (![string isKindOfClass: [OFString class]])
		@throw [OFInvalidArgumentException exception];


	otherCStringLength = string.UTF8StringLength;
	minimumCStringLength = (_s->cStringLength > otherCStringLength
	    ? otherCStringLength : _s->cStringLength);

	if ((compare = memcmp(_s->cString, string.UTF8String,
	    minimumCStringLength)) == 0) {
		if (_s->cStringLength > otherCStringLength)
			return OFOrderedDescending;
		if (_s->cStringLength < otherCStringLength)
			return OFOrderedAscending;
		return OFOrderedSame;
	}

	if (compare > 0)
		return OFOrderedDescending;
	else
		return OFOrderedAscending;
}

- (OFComparisonResult)caseInsensitiveCompare: (OFString *)string
{
	const char *otherCString;
	size_t otherCStringLength, minimumCStringLength;
#ifdef OF_HAVE_UNICODE_TABLES
	size_t i, j;
#endif
	int compare;

	if (string == self)
		return OFOrderedSame;




	otherCString = string.UTF8String;
	otherCStringLength = string.UTF8StringLength;

#ifdef OF_HAVE_UNICODE_TABLES
	if (!_s->isUTF8) {
#endif
		minimumCStringLength = (_s->cStringLength > otherCStringLength
		    ? otherCStringLength : _s->cStringLength);

		if ((compare = memcasecmp(_s->cString, otherCString,
		    minimumCStringLength)) == 0) {
			if (_s->cStringLength > otherCStringLength)
				return OFOrderedDescending;
			if (_s->cStringLength < otherCStringLength)
				return OFOrderedAscending;
			return OFOrderedSame;
		}

		if (compare > 0)
			return OFOrderedDescending;
		else
			return OFOrderedAscending;
#ifdef OF_HAVE_UNICODE_TABLES
	}

	i = j = 0;

	while (i < _s->cStringLength && j < otherCStringLength) {
		OFUnichar c1, c2;
		ssize_t l1, l2;

		l1 = OFUTF8StringDecode(_s->cString + i,
		    _s->cStringLength - i, &c1);
		l2 = OFUTF8StringDecode(otherCString + j,
		    otherCStringLength - j, &c2);

		if (l1 <= 0 || l2 <= 0 || c1 > 0x10FFFF || c2 > 0x10FFFF)
			@throw [OFInvalidEncodingException exception];

		if (c1 >> 8 < OFUnicodeCaseFoldingTableSize) {
			OFUnichar tc =
			    OFUnicodeCaseFoldingTable[c1 >> 8][c1 & 0xFF];

			if (tc)
				c1 = tc;
		}

		if (c2 >> 8 < OFUnicodeCaseFoldingTableSize) {
			OFUnichar tc =
			    OFUnicodeCaseFoldingTable[c2 >> 8][c2 & 0xFF];

			if (tc)
				c2 = tc;
		}

		if (c1 > c2)
			return OFOrderedDescending;
		if (c1 < c2)
			return OFOrderedAscending;

		i += l1;
		j += l2;
	}

	if (_s->cStringLength - i > otherCStringLength - j)
		return OFOrderedDescending;
	else if (_s->cStringLength - i < otherCStringLength - j)
		return OFOrderedAscending;
#endif

	return OFOrderedSame;
}

- (unsigned long)hash
{
	unsigned long hash;

	if (_s->hasHash)
		return _s->hash;

	OFHashInit(&hash);

	for (size_t i = 0; i < _s->cStringLength; i++) {
		OFUnichar c;
		ssize_t length;

		if ((length = OFUTF8StringDecode(_s->cString + i,
		    _s->cStringLength - i, &c)) <= 0)
			@throw [OFInvalidEncodingException exception];

		OFHashAdd(&hash, (c & 0xFF0000) >> 16);
		OFHashAdd(&hash, (c & 0x00FF00) >> 8);
		OFHashAdd(&hash, c & 0x0000FF);

		i += length - 1;
	}

	OFHashFinalize(&hash);

	_s->hash = hash;
	_s->hasHash = true;

	return hash;
}

- (OFUnichar)characterAtIndex: (size_t)idx
{
	OFUnichar character;

	if (idx >= _s->length)
		@throw [OFOutOfRangeException exception];

	if (!_s->isUTF8)
		return _s->cString[idx];

	idx = OFUTF8StringIndexToPosition(_s->cString, idx, _s->cStringLength);

	if (OFUTF8StringDecode(_s->cString + idx, _s->cStringLength - idx,
	    &character) <= 0)
		@throw [OFInvalidEncodingException exception];

	return character;
}

- (void)getCharacters: (OFUnichar *)buffer inRange: (OFRange)range

{
	/* TODO: Could be slightly optimized */
	void *pool = objc_autoreleasePoolPush();
	const OFUnichar *characters = self.characters;

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

	memcpy(buffer, characters + range.location,
	    range.length * sizeof(OFUnichar));

	objc_autoreleasePoolPop(pool);
}

- (OFRange)rangeOfString: (OFString *)string
		 options: (OFStringSearchOptions)options
		   range: (OFRange)range
{
	const char *cString = string.UTF8String;
	size_t cStringLength = string.UTF8StringLength;
	size_t rangeLocation, rangeLength;

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

	if (_s->isUTF8) {
		rangeLocation = OFUTF8StringIndexToPosition(
		    _s->cString, range.location, _s->cStringLength);
		rangeLength = OFUTF8StringIndexToPosition(
		    _s->cString + rangeLocation, range.length,
		    _s->cStringLength - rangeLocation);
	} else {
		rangeLocation = range.location;
		rangeLength = range.length;
	}

	if (cStringLength == 0)
		return OFRangeMake(0, 0);

	if (cStringLength > rangeLength)
		return OFRangeMake(OFNotFound, 0);

	if (options & OFStringSearchBackwards) {
		for (size_t i = rangeLength - cStringLength;; i--) {
			if (memcmp(_s->cString + rangeLocation + i, cString,
			    cStringLength) == 0) {
				range.location += positionToIndex(
				    _s->cString + rangeLocation, i);
				range.length = string.length;

				return range;
			}

			/* Did not match and we're at the last char */
			if (i == 0)
				return OFRangeMake(OFNotFound, 0);
		}
	} else {
		for (size_t i = 0; i <= rangeLength - cStringLength; i++) {
			if (memcmp(_s->cString + rangeLocation + i, cString,
			    cStringLength) == 0) {
				range.location += positionToIndex(
				    _s->cString + rangeLocation, i);
				range.length = string.length;

				return range;
			}
		}
	}

	return OFRangeMake(OFNotFound, 0);
}

- (bool)containsString: (OFString *)string
{
	const char *cString = string.UTF8String;
	size_t cStringLength = string.UTF8StringLength;

	if (cStringLength == 0)
		return true;

	if (cStringLength > _s->cStringLength)
		return false;

	for (size_t i = 0; i <= _s->cStringLength - cStringLength; i++)
		if (memcmp(_s->cString + i, cString, cStringLength) == 0)
			return true;

	return false;
}

- (OFString *)substringWithRange: (OFRange)range
{
	size_t start = range.location;
	size_t end = range.location + range.length;

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

	if (_s->isUTF8) {
		start = OFUTF8StringIndexToPosition(_s->cString, start,
		    _s->cStringLength);
		end = OFUTF8StringIndexToPosition(_s->cString, end,
		    _s->cStringLength);
	}

	return [OFString stringWithUTF8String: _s->cString + start
				       length: end - start];
}

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
		return false;

	return (memcmp(_s->cString + (_s->cStringLength - cStringLength),
	    suffix.UTF8String, cStringLength) == 0);
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
				 options: (int)options
{
	void *pool;
	OFMutableArray *array;
	const char *cString = delimiter.UTF8String;
	size_t cStringLength = delimiter.UTF8StringLength;
	bool skipEmpty = (options & OF_STRING_SKIP_EMPTY);
	size_t last;
	OFString *component;







	array = [OFMutableArray array];
	pool = objc_autoreleasePoolPush();



	if (cStringLength > _s->cStringLength) {
		[array addObject: [[self copy] autorelease]];
		objc_autoreleasePoolPop(pool);

		return array;
	}







|



|
|
|



>
>
>
>
>
>


>
>







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
		return false;

	return (memcmp(_s->cString + (_s->cStringLength - cStringLength),
	    suffix.UTF8String, cStringLength) == 0);
}

- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
				 options: (OFStringSeparationOptions)options
{
	void *pool;
	OFMutableArray *array;
	const char *cString;
	size_t cStringLength;
	bool skipEmpty = (options & OFStringSkipEmptyComponents);
	size_t last;
	OFString *component;

	if (delimiter == nil)
		@throw [OFInvalidArgumentException exception];

	if (delimiter.length == 0)
		return [OFArray arrayWithObject: self];

	array = [OFMutableArray array];
	pool = objc_autoreleasePoolPush();
	cString = delimiter.UTF8String;
	cStringLength = delimiter.UTF8StringLength;

	if (cStringLength > _s->cStringLength) {
		[array addObject: [[self copy] autorelease]];
		objc_autoreleasePoolPop(pool);

		return array;
	}
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

1182
1183

1184
1185
1186










1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
	[array makeImmutable];

	objc_autoreleasePoolPop(pool);

	return array;
}

- (const of_unichar_t *)characters
{
	OFObject *object = [[[OFObject alloc] init] autorelease];
	of_unichar_t *ret;
	size_t i, j;

	ret = [object allocMemoryWithSize: sizeof(of_unichar_t)
				    count: _s->length];

	i = j = 0;

	while (i < _s->cStringLength) {
		of_unichar_t c;
		ssize_t cLen;

		cLen = of_string_utf8_decode(_s->cString + i,
		    _s->cStringLength - i, &c);

		if (cLen <= 0 || c > 0x10FFFF)

			@throw [OFInvalidEncodingException exception];


		ret[j++] = c;
		i += cLen;
	}











	return ret;
}

- (const of_char32_t *)UTF32StringWithByteOrder: (of_byte_order_t)byteOrder
{
	OFObject *object = [[[OFObject alloc] init] autorelease];
	of_char32_t *ret;
	size_t i, j;

	ret = [object allocMemoryWithSize: sizeof(of_unichar_t)
				    count: _s->length + 1];

	i = j = 0;

	while (i < _s->cStringLength) {
		of_unichar_t c;
		ssize_t cLen;

		cLen = of_string_utf8_decode(_s->cString + i,
		    _s->cStringLength - i, &c);

		if (cLen <= 0 || c > 0x10FFFF)

			@throw [OFInvalidEncodingException exception];


		if (byteOrder != OF_BYTE_ORDER_NATIVE)
			ret[j++] = OF_BSWAP32(c);
		else
			ret[j++] = c;

		i += cLen;
	}
	ret[j] = 0;











	return ret;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (of_string_line_enumeration_block_t)block
{
	void *pool;
	const char *cString = _s->cString;
	const char *last = cString;
	bool stop = false, lastCarriageReturn = false;

	while (!stop && *cString != 0) {
		if (lastCarriageReturn && *cString == '\n') {
			lastCarriageReturn = false;

			cString++;
			last++;

			continue;
		}

		if (*cString == '\n' || *cString == '\r') {
			pool = objc_autoreleasePoolPush();

			block([OFString
			    stringWithUTF8String: last
					  length: cString - last], &stop);

			last = cString + 1;

			objc_autoreleasePoolPop(pool);
		}

		lastCarriageReturn = (*cString == '\r');
		cString++;







|

<
|
|
|
<
<

<
<

|


|


|
>

|
>
|


>
>
>
>
>
>
>
>
>
>




|

<
|
|
|
<
<

<
<

|


|


|
>

|
>
|
|

|



|
>
>
>
>
>
>
>
>
>
>





|



















|
<
|
>







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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192

1193
1194
1195


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
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
	[array makeImmutable];

	objc_autoreleasePoolPop(pool);

	return array;
}

- (const OFUnichar *)characters
{

	OFUnichar *buffer = OFAllocMemory(_s->length, sizeof(OFUnichar));
	size_t i = 0, j = 0;
	const OFUnichar *ret;





	while (i < _s->cStringLength) {
		OFUnichar c;
		ssize_t cLen;

		cLen = OFUTF8StringDecode(_s->cString + i,
		    _s->cStringLength - i, &c);

		if (cLen <= 0 || c > 0x10FFFF) {
			OFFreeMemory(buffer);
			@throw [OFInvalidEncodingException exception];
		}

		buffer[j++] = c;
		i += cLen;
	}

	@try {
		ret = [[OFData dataWithItemsNoCopy: buffer
					     count: _s->length
					  itemSize: sizeof(OFUnichar)
				      freeWhenDone: true] items];
	} @catch (id e) {
		OFFreeMemory(buffer);
		@throw e;
	}

	return ret;
}

- (const OFChar32 *)UTF32StringWithByteOrder: (OFByteOrder)byteOrder
{

	OFChar32 *buffer = OFAllocMemory(_s->length + 1, sizeof(OFChar32));
	size_t i = 0, j = 0;
	const OFChar32 *ret;





	while (i < _s->cStringLength) {
		OFChar32 c;
		ssize_t cLen;

		cLen = OFUTF8StringDecode(_s->cString + i,
		    _s->cStringLength - i, &c);

		if (cLen <= 0 || c > 0x10FFFF) {
			OFFreeMemory(buffer);
			@throw [OFInvalidEncodingException exception];
		}

		if (byteOrder != OFByteOrderNative)
			buffer[j++] = OFByteSwap32(c);
		else
			buffer[j++] = c;

		i += cLen;
	}
	buffer[j] = 0;

	@try {
		ret = [[OFData dataWithItemsNoCopy: buffer
					     count: _s->length + 1
					  itemSize: sizeof(OFChar32)
				      freeWhenDone: true] items];
	} @catch (id e) {
		OFFreeMemory(buffer);
		@throw e;
	}

	return ret;
}

#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (OFStringLineEnumerationBlock)block
{
	void *pool;
	const char *cString = _s->cString;
	const char *last = cString;
	bool stop = false, lastCarriageReturn = false;

	while (!stop && *cString != 0) {
		if (lastCarriageReturn && *cString == '\n') {
			lastCarriageReturn = false;

			cString++;
			last++;

			continue;
		}

		if (*cString == '\n' || *cString == '\r') {
			pool = objc_autoreleasePoolPush();

			block([OFString stringWithUTF8String: last

						      length: cString - last],
			    &stop);
			last = cString + 1;

			objc_autoreleasePoolPop(pool);
		}

		lastCarriageReturn = (*cString == '\r');
		cString++;

Added src/OFUUID.h version [e7cc13e47d].















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFSerialization.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;

/**
 * @class OFUUID OFUUID.h ObjFW/OFUUID.h
 *
 * @brief A UUID conforming to RFC 4122.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFUUID: OFObject <OFCopying, OFComparing, OFSerialization>
{
	unsigned char _bytes[16];
}

/**
 * @brief The UUID as a string.
 */
@property (readonly, nonatomic) OFString *UUIDString;

/**
 * @brief Creates a new random UUID as per RFC 4122 version 4.
 *
 * @return A new, autoreleased OFUUID
 */
+ (instancetype)UUID;

/**
 * @brief Creates a new UUID with the specified bytes.
 *
 * @param bytes The bytes for the UUID
 * @return A new, autoreleased OFUUID
 */
+ (instancetype)UUIDWithUUIDBytes: (const unsigned char [_Nonnull 16])bytes;

/**
 * @brief Creates a new UUID with the specified UUID string.
 *
 * @param string The UUID string for the UUID
 * @return A new, autoreleased OFUUID
 */
+ (instancetype)UUIDWithUUIDString: (OFString *)string;

/**
 * @brief Initializes an already allocated OFUUID as a new random UUID as per
 *	  RFC 4122 version 4.
 *
 * @return An initialized OFUUID
 */
- (instancetype)init;

/**
 * @brief Initializes an already allocated OFUUID with the specified bytes.
 *
 * @param bytes The bytes to initialize the OFUUID with
 * @return An initialized OFUUID
 */
- (instancetype)initWithUUIDBytes: (const unsigned char [_Nonnull 16])bytes;

/**
 * @brief Initializes an already allocated OFUUID with the specified UUID
 *	  string.
 *
 * @param string The UUID string to initialize the OFUUID with
 * @return An initialized OFUUID
 */
- (instancetype)initWithUUIDString: (OFString *)string;

/**
 * @brief Compares the UUID to another UUID.
 *
 * @param UUID The UUID to compare to
 * @return The result of the comparison
 */
- (OFComparisonResult)compare: (OFUUID *)UUID;

/**
 * @brief Gets the bytes of the UUID.
 *
 * @param bytes An array of 16 bytes into which to write the UUID
 */
- (void)getUUIDBytes: (unsigned char [_Nonnull 16])bytes;
@end

OF_ASSUME_NONNULL_END

Added src/OFUUID.m version [2113b35dea].

























































































































































































































































































































































































































































































































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

#import "OFUUID.h"
#import "OFArray.h"
#import "OFString.h"
#import "OFXMLElement.h"

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

#define bytesSize 16

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

+ (instancetype)UUIDWithUUIDBytes: (const unsigned char [16])bytes
{
	return [[[self alloc] initWithUUIDBytes: bytes] autorelease];
}

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

- (instancetype)init
{
	uint64_t r;

	self = [super init];

	r = OFRandom64();
	memcpy(_bytes, &r, 8);
	r = OFRandom64();
	memcpy(_bytes + 8, &r, 8);

	_bytes[6] &= ~((1 << 7) | (1 << 5) | (1 << 4));
	_bytes[6] |= (1 << 6);
	_bytes[8] &= ~(1 << 6);
	_bytes[8] |= (1 << 7);

	return self;
}

- (instancetype)initWithUUIDBytes: (const unsigned char [16])bytes
{
	self = [super init];

	memcpy(_bytes, bytes, sizeof(_bytes));

	return self;
}

static void
decode(OFArray OF_GENERIC(OFString *) *components, size_t componentIndex,
    size_t componentLength, unsigned char *bytes, size_t *i)
{
	void *pool = objc_autoreleasePoolPush();
	OFString *component = [components objectAtIndex: componentIndex];
	const char *cString;

	if (component.UTF8StringLength != componentLength)
		@throw [OFInvalidFormatException exception];

	if (*i + componentLength / 2 > bytesSize)
		@throw [OFOutOfRangeException exception];

	cString = component.UTF8String;

	for (size_t j = 0; j < componentLength; j += 2) {
		uint8_t value;

		if (cString[j] >= '0' && cString[j] <= '9')
			value = cString[j] - '0';
		else if (cString[j] >= 'a' && cString[j] <= 'f')
			value = cString[j] - 'a' + 10;
		else if (cString[j] >= 'A' && cString[j] <= 'F')
			value = cString[j] - 'A' + 10;
		else
			@throw [OFInvalidFormatException exception];

		value <<= 4;

		if (cString[j + 1] >= '0' && cString[j + 1] <= '9')
			value |= cString[j + 1] - '0';
		else if (cString[j + 1] >= 'a' && cString[j + 1] <= 'f')
			value |= cString[j + 1] - 'a' + 10;
		else if (cString[j + 1] >= 'A' && cString[j + 1] <= 'F')
			value |= cString[j + 1] - 'A' + 10;
		else
			@throw [OFInvalidFormatException exception];

		bytes[(*i)++] = value;
	}

	objc_autoreleasePoolPop(pool);
}

- (instancetype)initWithUUIDString: (OFString *)string
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		size_t i = 0;
		OFArray OF_GENERIC(OFString *) *components =
		    [string componentsSeparatedByString: @"-"];

		if (components.count != 5)
			@throw [OFInvalidFormatException exception];

		decode(components, 0, 8, _bytes, &i);
		decode(components, 1, 4, _bytes, &i);
		decode(components, 2, 4, _bytes, &i);
		decode(components, 3, 4, _bytes, &i);
		decode(components, 4, 12, _bytes, &i);

		OFEnsure(i == 16);

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	void *pool = objc_autoreleasePoolPush();
	OFString *UUIDString;

	@try {
		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		UUIDString = element.stringValue;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	self = [self initWithUUIDString: UUIDString];

	objc_autoreleasePoolPop(pool);

	return self;
}

- (bool)isEqual: (id)object
{
	OFUUID *UUID;

	if (![object isKindOfClass: [OFUUID class]])
		return false;

	UUID = object;

	return (memcmp(_bytes, UUID->_bytes, sizeof(_bytes)) == 0);
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	for (size_t i = 0; i < sizeof(_bytes); i++)
		OFHashAdd(&hash, _bytes[i]);

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{
	return [self retain];
}

- (OFComparisonResult)compare: (OFUUID *)UUID
{
	int comparison;

	if (![UUID isKindOfClass: [OFUUID class]])
		@throw [OFInvalidArgumentException exception];

	if ((comparison = memcmp(_bytes, UUID->_bytes, sizeof(_bytes))) == 0)
		return OFOrderedSame;

	if (comparison > 0)
		return OFOrderedDescending;
	else
		return OFOrderedAscending;
}

- (void)getUUIDBytes: (unsigned char [16])bytes
{
	memcpy(bytes, _bytes, sizeof(_bytes));
}

- (OFString *)UUIDString
{
	return [OFString stringWithFormat:
	    @"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
	    @"%02x%02x%02x%02x%02x%02x",
	    _bytes[0], _bytes[1], _bytes[2], _bytes[3],
	    _bytes[4], _bytes[5], _bytes[6], _bytes[7],
	    _bytes[8], _bytes[9], _bytes[10], _bytes[11],
	    _bytes[12], _bytes[13], _bytes[14], _bytes[15]];
}

- (OFString *)description
{
	return self.UUIDString;
}

- (OFXMLElement *)XMLElementBySerializing
{
	void *pool = objc_autoreleasePoolPush();
	OFXMLElement *element = [OFXMLElement elementWithName: self.className
						    namespace: OFSerializationNS
						  stringValue: self.UUIDString];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}
@end

Modified src/OFValue.h from [e9a82ba4b1] to [823407a4d9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 * @brief The value as a non-retained object.
 *
 * If the value is not pointer-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) id nonretainedObjectValue;

/**
 * @brief The value as a range.
 *
 * If the value is not range-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) of_range_t rangeValue;

/**
 * @brief The value as a point.
 *
 * If the value is not point-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) of_point_t pointValue;

/**
 * @brief The value as a dimension.
 *
 * If the value is not dimension-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) of_dimension_t dimensionValue;

/**
 * @brief The value as a rectangle.
 *
 * If the value is not rectangle-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) of_rectangle_t rectangleValue;

/**
 * @brief Creates a new, autorelease OFValue with the specified bytes of the
 *	  specified type.
 *
 * @param bytes The bytes containing the value
 * @param objCType The ObjC type encoding for the value







|

|

|


|

|

|


|

|

|


|

|

|







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
 * @brief The value as a non-retained object.
 *
 * If the value is not pointer-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) id nonretainedObjectValue;

/**
 * @brief The value as an OFRange.
 *
 * If the value is not OFRange-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) OFRange rangeValue;

/**
 * @brief The value as an OFPoint.
 *
 * If the value is not OFPoint-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) OFPoint pointValue;

/**
 * @brief The value as an OFSize.
 *
 * If the value is not OFSize-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) OFSize sizeValue;

/**
 * @brief The value as a OFRect.
 *
 * If the value is not OFRect-sized, @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) OFRect rectValue;

/**
 * @brief Creates a new, autorelease OFValue with the specified bytes of the
 *	  specified type.
 *
 * @param bytes The bytes containing the value
 * @param objCType The ObjC type encoding for the value
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

/**
 * @brief Creates a new, autoreleased OFValue containing the specified range.
 *
 * @param range The range the OFValue should contain
 * @return A new, autoreleased OFValue
 */
+ (instancetype)valueWithRange: (of_range_t)range;

/**
 * @brief Creates a new, autoreleased OFValue containing the specified point.
 *
 * @param point The point the OFValue should contain
 * @return A new, autoreleased OFValue
 */
+ (instancetype)valueWithPoint: (of_point_t)point;

/**
 * @brief Creates a new, autoreleased OFValue containing the specified
 *	  dimension.
 *
 * @param dimension The dimension the OFValue should contain
 * @return A new, autoreleased OFValue
 */
+ (instancetype)valueWithDimension: (of_dimension_t)dimension;

/**
 * @brief Creates a new, autoreleased OFValue containing the specified
 *	  rectangle.
 *
 * @param rectangle The rectangle the OFValue should contain
 * @return A new, autoreleased OFValue
 */
+ (instancetype)valueWithRectangle: (of_rectangle_t)rectangle;

/**
 * @brief Initializes an already allocated OFValue with the specified bytes of
 *	  the specified type.
 *
 * @param bytes The bytes containing the value
 * @param objCType The ObjC type encoding for the value
 * @return An initialized OFValue
 */
- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType;

/**
 * @brief Initializes an already allocated OFValue containing the specified
 *	  pointer.
 *
 * Only the raw value of the pointer is stored and no data will be copied.
 *
 * @param pointer The pointer the OFValue should contain
 * @return An initialized OFValue
 */
- (instancetype)initWithPointer: (const void *)pointer;

/**
 * @brief Initializes an already allocated OFValue containing the specified
 *	  non-retained object.
 *
 * The object is not retained, which makes this useful for storing objects in
 * collections without retaining them.
 *
 * @param object The object the OFValue should contain without retaining it
 * @return An initialized OFValue
 */
- (instancetype)initWithNonretainedObject: (id)object;

/**
 * @brief Initializes an already allocated OFValue containing the specified
 *	  range.
 *
 * @param range The range the OFValue should contain
 * @return An initialized OFValue
 */
- (instancetype)initWithRange: (of_range_t)range;

/**
 * @brief Initializes an already allocated OFValue containing the specified
 *	  point.
 *
 * @param point The point the OFValue should contain
 * @return An initialized OFValue
 */
- (instancetype)initWithPoint: (of_point_t)point;

/**
 * @brief Initializes an already allocated OFValue containing the specified
 *	  dimension.
 *
 * @param dimension The dimension the OFValue should contain
 * @return An initialized OFValue
 */
- (instancetype)initWithDimension: (of_dimension_t)dimension;

/**
 * @brief Initializes an already allocated OFValue containing the specified
 *	  rectangle.
 *
 * @param rectangle The rectangle the OFValue should contain
 * @return An initialized OFValue
 */
- (instancetype)initWithRectangle: (of_rectangle_t)rectangle;

/**
 * @brief Gets the value.
 *
 * If the specified size does not match, this raises an
 * @ref OFOutOfRangeException.
 *
 * @param value The buffer to copy the value into
 * @param size The size of the value
 */
- (void)getValue: (void *)value
	    size: (size_t)size;
@end

OF_ASSUME_NONNULL_END

#if !defined(NSINTEGER_DEFINED) && !__has_feature(modules)
/* Required for array literals to work */
@compatibility_alias NSValue OFValue;
#endif







|







|


|
<

|


|





|


|












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









|
<








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

/**
 * @brief Creates a new, autoreleased OFValue containing the specified range.
 *
 * @param range The range the OFValue should contain
 * @return A new, autoreleased OFValue
 */
+ (instancetype)valueWithRange: (OFRange)range;

/**
 * @brief Creates a new, autoreleased OFValue containing the specified point.
 *
 * @param point The point the OFValue should contain
 * @return A new, autoreleased OFValue
 */
+ (instancetype)valueWithPoint: (OFPoint)point;

/**
 * @brief Creates a new, autoreleased OFValue containing the specified size.

 *
 * @param size The size the OFValue should contain
 * @return A new, autoreleased OFValue
 */
+ (instancetype)valueWithSize: (OFSize)size;

/**
 * @brief Creates a new, autoreleased OFValue containing the specified
 *	  rectangle.
 *
 * @param rect The rectangle the OFValue should contain
 * @return A new, autoreleased OFValue
 */
+ (instancetype)valueWithRect: (OFRect)rect;

/**
 * @brief Initializes an already allocated OFValue with the specified bytes of
 *	  the specified type.
 *
 * @param bytes The bytes containing the value
 * @param objCType The ObjC type encoding for the value
 * @return An initialized OFValue
 */
- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType;




























































/**
 * @brief Gets the value.
 *
 * If the specified size does not match, this raises an
 * @ref OFOutOfRangeException.
 *
 * @param value The buffer to copy the value into
 * @param size The size of the value
 */
- (void)getValue: (void *)value size: (size_t)size;

@end

OF_ASSUME_NONNULL_END

#if !defined(NSINTEGER_DEFINED) && !__has_feature(modules)
/* Required for array literals to work */
@compatibility_alias NSValue OFValue;
#endif

Modified src/OFValue.m from [1029a1bdaf] to [4253491c94].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFValue.h"
#import "OFBytesValue.h"
#import "OFDimensionValue.h"
#import "OFMethodSignature.h"
#import "OFNonretainedObjectValue.h"
#import "OFPointValue.h"
#import "OFPointerValue.h"
#import "OFRangeValue.h"
#import "OFRectangleValue.h"

#import "OFString.h"

#import "OFOutOfMemoryException.h"

static struct {
	Class isa;
} placeholder;

@interface OFValuePlaceholder: OFValue
@end

@implementation OFValuePlaceholder
- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType
{
	return (id)[[OFBytesValue alloc] initWithBytes: bytes
					      objCType: objCType];
}

- (instancetype)initWithPointer: (const void *)pointer
{
	return (id)[[OFPointerValue alloc] initWithPointer: pointer];
}

- (instancetype)initWithNonretainedObject: (id)object
{
	return (id)[[OFNonretainedObjectValue alloc]
	    initWithNonretainedObject: object];
}

- (instancetype)initWithRange: (of_range_t)range
{
	return (id)[[OFRangeValue alloc] initWithRange: range];
}

- (instancetype)initWithPoint: (of_point_t)point
{
	return (id)[[OFPointValue alloc] initWithPoint: point];
}

- (instancetype)initWithDimension: (of_dimension_t)dimension
{
	return (id)[[OFDimensionValue alloc] initWithDimension: dimension];
}

- (instancetype)initWithRectangle: (of_rectangle_t)rectangle
{
	return (id)[[OFRectangleValue alloc] initWithRectangle: rectangle];
}
@end

@implementation OFValue
+ (void)initialize
{
	if (self == [OFValue class])
		placeholder.isa = [OFValuePlaceholder class];
}

+ (instancetype)alloc
{
	if (self == [OFValue class])
		return (id)&placeholder;

	return [super alloc];
}

+ (instancetype)valueWithBytes: (const void *)bytes
		      objCType: (const char *)objCType
{
	return [[[self alloc] initWithBytes: bytes
				   objCType: objCType] autorelease];
}

+ (instancetype)valueWithPointer: (const void *)pointer
{
	return [[[self alloc] initWithPointer: pointer] autorelease];
}

+ (instancetype)valueWithNonretainedObject: (id)object
{

	return [[[self alloc] initWithNonretainedObject: object] autorelease];
}

+ (instancetype)valueWithRange: (of_range_t)range
{
	return [[[self alloc] initWithRange: range] autorelease];
}

+ (instancetype)valueWithPoint: (of_point_t)point
{
	return [[[self alloc] initWithPoint: point] autorelease];
}

+ (instancetype)valueWithDimension: (of_dimension_t)dimension
{
	return [[[self alloc] initWithDimension: dimension] autorelease];
}

+ (instancetype)valueWithRectangle: (of_rectangle_t)rectangle
{
	return [[[self alloc] initWithRectangle: rectangle] autorelease];
}

- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPointer: (const void *)pointer
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithNonretainedObject: (id)object
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRange: (of_range_t)range
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPoint: (of_point_t)point
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithDimension: (of_dimension_t)dimension
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRectangle: (of_rectangle_t)rectangle
{
	OF_INVALID_INIT_METHOD
}

- (bool)isEqual: (id)object
{
	const char *objCType;
	size_t size;

<
<
|















<





|
>




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

<
<
<
<
<
<



|







|
|




|




>
|


|

|


|

|


|

|


|

|





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







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


 * 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 "OFValue.h"
#import "OFBytesValue.h"

#import "OFMethodSignature.h"
#import "OFNonretainedObjectValue.h"
#import "OFPointValue.h"
#import "OFPointerValue.h"
#import "OFRangeValue.h"
#import "OFRectValue.h"
#import "OFSizeValue.h"
#import "OFString.h"

#import "OFOutOfMemoryException.h"
















































@implementation OFValue






+ (instancetype)alloc
{
	if (self == [OFValue class])
		return [OFBytesValue alloc];

	return [super alloc];
}

+ (instancetype)valueWithBytes: (const void *)bytes
		      objCType: (const char *)objCType
{
	return [[[OFBytesValue alloc] initWithBytes: bytes
					   objCType: objCType] autorelease];
}

+ (instancetype)valueWithPointer: (const void *)pointer
{
	return [[[OFPointerValue alloc] initWithPointer: pointer] autorelease];
}

+ (instancetype)valueWithNonretainedObject: (id)object
{
	return [[[OFNonretainedObjectValue alloc]
	    initWithNonretainedObject: object] autorelease];
}

+ (instancetype)valueWithRange: (OFRange)range
{
	return [[[OFRangeValue alloc] initWithRange: range] autorelease];
}

+ (instancetype)valueWithPoint: (OFPoint)point
{
	return [[[OFPointValue alloc] initWithPoint: point] autorelease];
}

+ (instancetype)valueWithSize: (OFSize)size
{
	return [[[OFSizeValue alloc] initWithSize: size] autorelease];
}

+ (instancetype)valueWithRect: (OFRect)rect
{
	return [[[OFRectValue alloc] initWithRect: rect] autorelease];
}

- (instancetype)initWithBytes: (const void *)bytes
		     objCType: (const char *)objCType
{






























	OF_INVALID_INIT_METHOD
}

- (bool)isEqual: (id)object
{
	const char *objCType;
	size_t size;
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
		return false;

	objCType = self.objCType;

	if (strcmp([object objCType], objCType) != 0)
		return false;

	size = of_sizeof_type_encoding(objCType);

	value = of_malloc(1, size);
	@try {
		otherValue = of_malloc(1, size);
	} @catch (id e) {
		of_free(value);
		@throw e;
	}

	@try {
		[self getValue: value
			  size: size];
		[object getValue: otherValue
			    size: size];

		ret = (memcmp(value, otherValue, size) == 0);
	} @finally {
		of_free(value);
		of_free(otherValue);
	}

	return ret;
}

- (unsigned long)hash
{
	size_t size = of_sizeof_type_encoding(self.objCType);
	unsigned char *value;
	uint32_t hash;

	value = of_malloc(1, size);
	@try {
		[self getValue: value
			  size: size];

		OF_HASH_INIT(hash);

		for (size_t i = 0; i < size; i++)
			OF_HASH_ADD(hash, value[i]);

		OF_HASH_FINALIZE(hash);
	} @finally {
		of_free(value);
	}

	return hash;
}

- (id)copy
{
	return [self retain];
}

- (const char *)objCType
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)getValue: (void *)value
	    size: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)pointerValue
{
	void *ret;

	[self getValue: &ret
		  size: sizeof(ret)];

	return ret;
}

- (id)nonretainedObjectValue
{
	id ret;

	[self getValue: &ret
		  size: sizeof(ret)];

	return ret;
}

- (of_range_t)rangeValue
{
	of_range_t ret;

	[self getValue: &ret
		  size: sizeof(ret)];

	return ret;
}

- (of_point_t)pointValue
{
	of_point_t ret;

	[self getValue: &ret
		  size: sizeof(ret)];

	return ret;
}

- (of_dimension_t)dimensionValue
{
	of_dimension_t ret;

	[self getValue: &ret
		  size: sizeof(ret)];

	return ret;
}

- (of_rectangle_t)rectangleValue
{
	of_rectangle_t ret;

	[self getValue: &ret
		  size: sizeof(ret)];

	return ret;
}

- (OFString *)description
{
	OFMutableString *ret =
	    [OFMutableString stringWithString: @"<OFValue: "];
	size_t size = of_sizeof_type_encoding(self.objCType);
	unsigned char *value;

	value = of_malloc(1, size);
	@try {
		[self getValue: value
			  size: size];

		for (size_t i = 0; i < size; i++) {
			if (i > 0)
				[ret appendString: @" "];

			[ret appendFormat: @"%02x", value[i]];
		}
	} @finally {
		of_free(value);
	}

	[ret appendString: @">"];

	[ret makeImmutable];
	return ret;
}
@end







|

|

|

|




|
<
|
<
<


|
|







|

|

|

|
<

|


|

|

|















|
<







<
|
<
<






<
|
<
<



|

<
|
|
<
<



|

|
<
<
|
<



|

|
<
|
<
<



|

|
<
|
<
<







|


|

|
<








|








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
		return false;

	objCType = self.objCType;

	if (strcmp([object objCType], objCType) != 0)
		return false;

	size = OFSizeOfTypeEncoding(objCType);

	value = OFAllocMemory(1, size);
	@try {
		otherValue = OFAllocMemory(1, size);
	} @catch (id e) {
		OFFreeMemory(value);
		@throw e;
	}

	@try {
		[self getValue: value size: size];

		[object getValue: otherValue size: size];


		ret = (memcmp(value, otherValue, size) == 0);
	} @finally {
		OFFreeMemory(value);
		OFFreeMemory(otherValue);
	}

	return ret;
}

- (unsigned long)hash
{
	size_t size = OFSizeOfTypeEncoding(self.objCType);
	unsigned char *value;
	unsigned long hash;

	value = OFAllocMemory(1, size);
	@try {
		[self getValue: value size: size];


		OFHashInit(&hash);

		for (size_t i = 0; i < size; i++)
			OFHashAdd(&hash, value[i]);

		OFHashFinalize(&hash);
	} @finally {
		OFFreeMemory(value);
	}

	return hash;
}

- (id)copy
{
	return [self retain];
}

- (const char *)objCType
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)getValue: (void *)value size: (size_t)size

{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)pointerValue
{
	void *ret;

	[self getValue: &ret size: sizeof(ret)];


	return ret;
}

- (id)nonretainedObjectValue
{
	id ret;

	[self getValue: &ret size: sizeof(ret)];


	return ret;
}

- (OFRange)rangeValue
{

	OFRange ret;
	[self getValue: &ret size: sizeof(ret)];


	return ret;
}

- (OFPoint)pointValue
{
	OFPoint ret;


	[self getValue: &ret size: sizeof(ret)];

	return ret;
}

- (OFSize)sizeValue
{
	OFSize ret;

	[self getValue: &ret size: sizeof(ret)];


	return ret;
}

- (OFRect)rectValue
{
	OFRect ret;

	[self getValue: &ret size: sizeof(ret)];


	return ret;
}

- (OFString *)description
{
	OFMutableString *ret =
	    [OFMutableString stringWithString: @"<OFValue: "];
	size_t size = OFSizeOfTypeEncoding(self.objCType);
	unsigned char *value;

	value = OFAllocMemory(1, size);
	@try {
		[self getValue: value size: size];


		for (size_t i = 0; i < size; i++) {
			if (i > 0)
				[ret appendString: @" "];

			[ret appendFormat: @"%02x", value[i]];
		}
	} @finally {
		OFFreeMemory(value);
	}

	[ret appendString: @">"];

	[ret makeImmutable];
	return ret;
}
@end

Modified src/OFWin32ConsoleStdIOStream.h from [77f92f2723] to [d9940ccf28].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

#define OF_STDIO_STREAM_WIN32CONSOLE_H

#import "OFStdIOStream.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFWin32ConsoleStdIOStream: OFStdIOStream
{
	HANDLE _handle;
	WORD _attributes;
	of_char16_t _incompleteUTF16Surrogate;
	char _incompleteUTF8Surrogate[4];
	size_t _incompleteUTF8SurrogateLen;
}
@end

OF_ASSUME_NONNULL_END

<
<
|













<
<








|






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


 * 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 "OFStdIOStream.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFWin32ConsoleStdIOStream: OFStdIOStream
{
	HANDLE _handle;
	WORD _attributes;
	OFChar16 _incompleteUTF16Surrogate;
	char _incompleteUTF8Surrogate[4];
	size_t _incompleteUTF8SurrogateLen;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFWin32ConsoleStdIOStream.m from [959c780c29] to [8c3f9ce8af].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 * For example, on Windows XP, when using Windows XP's console, changing the
 * codepage to UTF-8 mostly breaks write() and completely breaks read():
 * write() suddenly returns the number of characters - instead of bytes -
 * written and read() just returns 0 as soon as a Unicode character is being
 * read.
 *
 * Therefore, instead of just using the UTF-8 codepage, this captures all reads
 * and writes to of_std{in,out,err} on the low level, interprets the buffer as
 * UTF-8 and converts to / from UTF-16 to use ReadConsoleW() / WriteConsoleW().
 * Doing so is safe, as the console only supports text anyway and thus it does
 * not matter if binary gets garbled by the conversion (e.g. because invalid
 * UTF-8 gets converted to U+FFFD).
 *
 * In order to not do this when redirecting input / output to a file (as the
 * file would then be read / written in the wrong encoding and break reading /
 * writing binary), it checks that the handle is indeed a console.
 */

#define OF_STDIO_STREAM_WIN32_CONSOLE_M

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <io.h>

#import "OFWin32ConsoleStdIOStream.h"







|










<
<







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
 * For example, on Windows XP, when using Windows XP's console, changing the
 * codepage to UTF-8 mostly breaks write() and completely breaks read():
 * write() suddenly returns the number of characters - instead of bytes -
 * written and read() just returns 0 as soon as a Unicode character is being
 * read.
 *
 * Therefore, instead of just using the UTF-8 codepage, this captures all reads
 * and writes to OFStd{In,Out,Err} on the low level, interprets the buffer as
 * UTF-8 and converts to / from UTF-16 to use ReadConsoleW() / WriteConsoleW().
 * Doing so is safe, as the console only supports text anyway and thus it does
 * not matter if binary gets garbled by the conversion (e.g. because invalid
 * UTF-8 gets converted to U+FFFD).
 *
 * In order to not do this when redirecting input / output to a file (as the
 * file would then be read / written in the wrong encoding and break reading /
 * writing binary), it checks that the handle is indeed a console.
 */



#include "config.h"

#include <assert.h>
#include <errno.h>
#include <io.h>

#import "OFWin32ConsoleStdIOStream.h"
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
#import "OFInvalidEncodingException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#include <windows.h>

static of_string_encoding_t
codepageToEncoding(UINT codepage)
{
	switch (codepage) {
	case 437:
		return OF_STRING_ENCODING_CODEPAGE_437;
	case 850:
		return OF_STRING_ENCODING_CODEPAGE_850;
	case 858:
		return OF_STRING_ENCODING_CODEPAGE_858;
	case 1251:
		return OF_STRING_ENCODING_WINDOWS_1251;
	case 1252:
		return OF_STRING_ENCODING_WINDOWS_1252;
	default:
		@throw [OFInvalidEncodingException exception];
	}
}

@implementation OFWin32ConsoleStdIOStream
+ (void)load
{
	int fd;

	if (self != [OFWin32ConsoleStdIOStream class])
		return;

	if ((fd = _fileno(stdin)) >= 0)
		of_stdin = [[OFWin32ConsoleStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
	if ((fd = _fileno(stdout)) >= 0)
		of_stdout = [[OFWin32ConsoleStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
	if ((fd = _fileno(stderr)) >= 0)
		of_stderr = [[OFWin32ConsoleStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
}

- (instancetype)of_initWithFileDescriptor: (int)fd
{
	self = [super of_initWithFileDescriptor: fd];








|




|

|

|

|

|














|


|


|







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
#import "OFInvalidEncodingException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#include <windows.h>

static OFStringEncoding
codepageToEncoding(UINT codepage)
{
	switch (codepage) {
	case 437:
		return OFStringEncodingCodepage437;
	case 850:
		return OFStringEncodingCodepage850;
	case 858:
		return OFStringEncodingCodepage858;
	case 1251:
		return OFStringEncodingWindows1251;
	case 1252:
		return OFStringEncodingWindows1252;
	default:
		@throw [OFInvalidEncodingException exception];
	}
}

@implementation OFWin32ConsoleStdIOStream
+ (void)load
{
	int fd;

	if (self != [OFWin32ConsoleStdIOStream class])
		return;

	if ((fd = _fileno(stdin)) >= 0)
		OFStdIn = [[OFWin32ConsoleStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
	if ((fd = _fileno(stdout)) >= 0)
		OFStdOut = [[OFWin32ConsoleStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
	if ((fd = _fileno(stderr)) >= 0)
		OFStdErr = [[OFWin32ConsoleStdIOStream alloc]
		    of_initWithFileDescriptor: fd];
}

- (instancetype)of_initWithFileDescriptor: (int)fd
{
	self = [super of_initWithFileDescriptor: fd];

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
		[self release];
		@throw e;
	}

	return self;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer_
			  length: (size_t)length
{
	void *pool = objc_autoreleasePoolPush();
	char *buffer = buffer_;
	of_char16_t *UTF16;
	size_t j = 0;

	if (length > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	UTF16 = [self allocMemoryWithSize: sizeof(of_char16_t)
				    count: length];
	@try {
		DWORD UTF16Len;
		OFMutableData *rest = nil;
		size_t i = 0;

		if ([OFSystemInfo isWindowsNT]) {
			if (!ReadConsoleW(_handle, UTF16, (DWORD)length,
			    &UTF16Len, NULL))
				@throw [OFReadFailedException
				    exceptionWithObject: self
					requestedLength: length * 2
						  errNo: EIO];
		} else {
			of_string_encoding_t encoding;
			OFString *string;
			size_t stringLen;

			if (!ReadConsoleA(_handle, (char *)UTF16, (DWORD)length,
			    &UTF16Len, NULL))
				@throw [OFReadFailedException
				    exceptionWithObject: self







|
<



|





|
<













|







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
		[self release];
		@throw e;
	}

	return self;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer_ length: (size_t)length

{
	void *pool = objc_autoreleasePoolPush();
	char *buffer = buffer_;
	OFChar16 *UTF16;
	size_t j = 0;

	if (length > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	UTF16 = OFAllocMemory(length, sizeof(OFChar16));

	@try {
		DWORD UTF16Len;
		OFMutableData *rest = nil;
		size_t i = 0;

		if ([OFSystemInfo isWindowsNT]) {
			if (!ReadConsoleW(_handle, UTF16, (DWORD)length,
			    &UTF16Len, NULL))
				@throw [OFReadFailedException
				    exceptionWithObject: self
					requestedLength: length * 2
						  errNo: EIO];
		} else {
			OFStringEncoding encoding;
			OFString *string;
			size_t stringLen;

			if (!ReadConsoleA(_handle, (char *)UTF16, (DWORD)length,
			    &UTF16Len, NULL))
				@throw [OFReadFailedException
				    exceptionWithObject: self
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
				@throw [OFOutOfRangeException exception];

			UTF16Len = (DWORD)stringLen;
			memcpy(UTF16, string.UTF16String, stringLen);
		}

		if (UTF16Len > 0 && _incompleteUTF16Surrogate != 0) {
			of_unichar_t c =
			    (((_incompleteUTF16Surrogate & 0x3FF) << 10) |
			    (UTF16[0] & 0x3FF)) + 0x10000;
			char UTF8[4];
			size_t UTF8Len;

			if ((UTF8Len = of_string_utf8_encode(c, UTF8)) == 0)
				@throw [OFInvalidEncodingException exception];

			if (UTF8Len <= length) {
				memcpy(buffer, UTF8, UTF8Len);
				j += UTF8Len;
			} else {
				if (rest == nil)
					rest = [OFMutableData data];

				[rest addItems: UTF8
					 count: UTF8Len];
			}

			_incompleteUTF16Surrogate = 0;
			i++;
		}

		for (; i < UTF16Len; i++) {
			of_unichar_t c = UTF16[i];
			char UTF8[4];
			size_t UTF8Len;

			/* Missing high surrogate */
			if ((c & 0xFC00) == 0xDC00)
				@throw [OFInvalidEncodingException exception];

			if ((c & 0xFC00) == 0xD800) {
				of_char16_t next;

				if (UTF16Len <= i + 1) {
					_incompleteUTF16Surrogate = c;

					if (rest != nil) {
						const char *items = rest.items;
						size_t count = rest.count;







|





|









|
<







|








|







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
				@throw [OFOutOfRangeException exception];

			UTF16Len = (DWORD)stringLen;
			memcpy(UTF16, string.UTF16String, stringLen);
		}

		if (UTF16Len > 0 && _incompleteUTF16Surrogate != 0) {
			OFUnichar c =
			    (((_incompleteUTF16Surrogate & 0x3FF) << 10) |
			    (UTF16[0] & 0x3FF)) + 0x10000;
			char UTF8[4];
			size_t UTF8Len;

			if ((UTF8Len = OFUTF8StringEncode(c, UTF8)) == 0)
				@throw [OFInvalidEncodingException exception];

			if (UTF8Len <= length) {
				memcpy(buffer, UTF8, UTF8Len);
				j += UTF8Len;
			} else {
				if (rest == nil)
					rest = [OFMutableData data];

				[rest addItems: UTF8 count: UTF8Len];

			}

			_incompleteUTF16Surrogate = 0;
			i++;
		}

		for (; i < UTF16Len; i++) {
			OFUnichar c = UTF16[i];
			char UTF8[4];
			size_t UTF8Len;

			/* Missing high surrogate */
			if ((c & 0xFC00) == 0xDC00)
				@throw [OFInvalidEncodingException exception];

			if ((c & 0xFC00) == 0xD800) {
				OFChar16 next;

				if (UTF16Len <= i + 1) {
					_incompleteUTF16Surrogate = c;

					if (rest != nil) {
						const char *items = rest.items;
						size_t count = rest.count;
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

				c = (((c & 0x3FF) << 10) | (next & 0x3FF)) +
				    0x10000;

				i++;
			}

			if ((UTF8Len = of_string_utf8_encode(c, UTF8)) == 0)
				@throw [OFInvalidEncodingException exception];

			if (j + UTF8Len <= length) {
				memcpy(buffer + j, UTF8, UTF8Len);
				j += UTF8Len;
			} else {
				if (rest == nil)
					rest = [OFMutableData data];

				[rest addItems: UTF8
					 count: UTF8Len];
			}
		}

		if (rest != nil)
			[self unreadFromBuffer: rest.items
					length: rest.count];
	} @finally {
		[self freeMemory: UTF16];
	}

	objc_autoreleasePoolPop(pool);

	return j;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer_
		       length: (size_t)length
{
	const char *buffer = buffer_;
	of_char16_t *tmp;
	size_t i = 0, j = 0;

	if (length > SIZE_MAX / 2)
		@throw [OFOutOfRangeException exception];

	if (_incompleteUTF8SurrogateLen > 0) {
		of_unichar_t c;
		of_char16_t UTF16[2];
		ssize_t UTF8Len;
		size_t toCopy;
		DWORD UTF16Len, bytesWritten;

		UTF8Len = -of_string_utf8_decode(
		    _incompleteUTF8Surrogate, _incompleteUTF8SurrogateLen, &c);

		OF_ENSURE(UTF8Len > 0);

		toCopy = UTF8Len - _incompleteUTF8SurrogateLen;
		if (toCopy > length)
			toCopy = length;

		memcpy(_incompleteUTF8Surrogate + _incompleteUTF8SurrogateLen,
		    buffer, toCopy);
		_incompleteUTF8SurrogateLen += toCopy;

		if (_incompleteUTF8SurrogateLen < (size_t)UTF8Len)
			return 0;

		UTF8Len = of_string_utf8_decode(
		    _incompleteUTF8Surrogate, _incompleteUTF8SurrogateLen, &c);

		if (UTF8Len <= 0 || c > 0x10FFFF) {
			assert(UTF8Len == 0 || UTF8Len < -4);

			UTF16[0] = 0xFFFD;
			UTF16Len = 1;







|









|
<




|
<

|







|
<


|






|
|




|


|












|







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

				c = (((c & 0x3FF) << 10) | (next & 0x3FF)) +
				    0x10000;

				i++;
			}

			if ((UTF8Len = OFUTF8StringEncode(c, UTF8)) == 0)
				@throw [OFInvalidEncodingException exception];

			if (j + UTF8Len <= length) {
				memcpy(buffer + j, UTF8, UTF8Len);
				j += UTF8Len;
			} else {
				if (rest == nil)
					rest = [OFMutableData data];

				[rest addItems: UTF8 count: UTF8Len];

			}
		}

		if (rest != nil)
			[self unreadFromBuffer: rest.items length: rest.count];

	} @finally {
		OFFreeMemory(UTF16);
	}

	objc_autoreleasePoolPop(pool);

	return j;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer_ length: (size_t)length

{
	const char *buffer = buffer_;
	OFChar16 *tmp;
	size_t i = 0, j = 0;

	if (length > SIZE_MAX / 2)
		@throw [OFOutOfRangeException exception];

	if (_incompleteUTF8SurrogateLen > 0) {
		OFUnichar c;
		OFChar16 UTF16[2];
		ssize_t UTF8Len;
		size_t toCopy;
		DWORD UTF16Len, bytesWritten;

		UTF8Len = -OFUTF8StringDecode(
		    _incompleteUTF8Surrogate, _incompleteUTF8SurrogateLen, &c);

		OFEnsure(UTF8Len > 0);

		toCopy = UTF8Len - _incompleteUTF8SurrogateLen;
		if (toCopy > length)
			toCopy = length;

		memcpy(_incompleteUTF8Surrogate + _incompleteUTF8SurrogateLen,
		    buffer, toCopy);
		_incompleteUTF8SurrogateLen += toCopy;

		if (_incompleteUTF8SurrogateLen < (size_t)UTF8Len)
			return 0;

		UTF8Len = OFUTF8StringDecode(
		    _incompleteUTF8Surrogate, _incompleteUTF8SurrogateLen, &c);

		if (UTF8Len <= 0 || c > 0x10FFFF) {
			assert(UTF8Len == 0 || UTF8Len < -4);

			UTF16[0] = 0xFFFD;
			UTF16Len = 1;
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
					   bytesWritten: bytesWritten * 2
						  errNo: EIO];
		} else {
			void *pool = objc_autoreleasePoolPush();
			OFString *string = [OFString
			    stringWithUTF16String: UTF16
					   length: UTF16Len];
			of_string_encoding_t encoding =
			    codepageToEncoding(GetConsoleOutputCP());
			size_t nativeLen = [string
			    cStringLengthWithEncoding: encoding];

			if (nativeLen > UINT32_MAX)
				@throw [OFOutOfRangeException exception];








|







320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
					   bytesWritten: bytesWritten * 2
						  errNo: EIO];
		} else {
			void *pool = objc_autoreleasePoolPush();
			OFString *string = [OFString
			    stringWithUTF16String: UTF16
					   length: UTF16Len];
			OFStringEncoding encoding =
			    codepageToEncoding(GetConsoleOutputCP());
			size_t nativeLen = [string
			    cStringLengthWithEncoding: encoding];

			if (nativeLen > UINT32_MAX)
				@throw [OFOutOfRangeException exception];

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
				   bytesWritten: bytesWritten * 2
					  errNo: 0];

		_incompleteUTF8SurrogateLen = 0;
		i += toCopy;
	}

	tmp = [self allocMemoryWithSize: sizeof(of_char16_t)
				  count: length * 2];
	@try {
		DWORD bytesWritten;

		while (i < length) {
			of_unichar_t c;
			ssize_t UTF8Len;

			UTF8Len = of_string_utf8_decode(buffer + i, length - i,
			    &c);

			if (UTF8Len < 0 && UTF8Len >= -4) {
				OF_ENSURE(length - i < 4);

				memcpy(_incompleteUTF8Surrogate, buffer + i,
				    length - i);
				_incompleteUTF8SurrogateLen = length - i;

				break;
			}







<
|




|


|



|







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
				   bytesWritten: bytesWritten * 2
					  errNo: 0];

		_incompleteUTF8SurrogateLen = 0;
		i += toCopy;
	}


	tmp = OFAllocMemory(length * 2, sizeof(OFChar16));
	@try {
		DWORD bytesWritten;

		while (i < length) {
			OFUnichar c;
			ssize_t UTF8Len;

			UTF8Len = OFUTF8StringDecode(buffer + i, length - i,
			    &c);

			if (UTF8Len < 0 && UTF8Len >= -4) {
				OFEnsure(length - i < 4);

				memcpy(_incompleteUTF8Surrogate, buffer + i,
				    length - i);
				_incompleteUTF8SurrogateLen = length - i;

				break;
			}
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
					requestedLength: j * 2
					   bytesWritten: bytesWritten * 2
						  errNo: EIO];
		} else {
			void *pool = objc_autoreleasePoolPush();
			OFString *string = [OFString stringWithUTF16String: tmp
								    length: j];
			of_string_encoding_t encoding =
			    codepageToEncoding(GetConsoleOutputCP());
			size_t nativeLen = [string
			    cStringLengthWithEncoding: encoding];

			if (nativeLen > UINT32_MAX)
				@throw [OFOutOfRangeException exception];








|







403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
					requestedLength: j * 2
					   bytesWritten: bytesWritten * 2
						  errNo: EIO];
		} else {
			void *pool = objc_autoreleasePoolPush();
			OFString *string = [OFString stringWithUTF16String: tmp
								    length: j];
			OFStringEncoding encoding =
			    codepageToEncoding(GetConsoleOutputCP());
			size_t nativeLen = [string
			    cStringLengthWithEncoding: encoding];

			if (nativeLen > UINT32_MAX)
				@throw [OFOutOfRangeException exception];

441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
		if (bytesWritten != j)
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: j * 2
				   bytesWritten: bytesWritten * 2
					  errNo: 0];
	} @finally {
		[self freeMemory: tmp];
	}

	/*
	 * We do not count in bytes when writing to the Win32 console. But
	 * since any incomplete write is an exception here anyway, we can just
	 * return length.
	 */







|







430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
		if (bytesWritten != j)
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: j * 2
				   bytesWritten: bytesWritten * 2
					  errNo: 0];
	} @finally {
		OFFreeMemory(tmp);
	}

	/*
	 * We do not count in bytes when writing to the Win32 console. But
	 * since any incomplete write is an exception here anyway, we can just
	 * return length.
	 */
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN |
	    FOREGROUND_BLUE | FOREGROUND_INTENSITY);

	[color getRed: &red
		green: &green
		 blue: &blue
		alpha: NULL];

	if (red >= 0.25)
		csbi.wAttributes |= FOREGROUND_RED;
	if (green >= 0.25)
		csbi.wAttributes |= FOREGROUND_GREEN;
	if (blue >= 0.25)
		csbi.wAttributes |= FOREGROUND_BLUE;







|
<
<
<







480
481
482
483
484
485
486
487



488
489
490
491
492
493
494

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN |
	    FOREGROUND_BLUE | FOREGROUND_INTENSITY);

	[color getRed: &red green: &green blue: &blue alpha: NULL];




	if (red >= 0.25)
		csbi.wAttributes |= FOREGROUND_RED;
	if (green >= 0.25)
		csbi.wAttributes |= FOREGROUND_GREEN;
	if (blue >= 0.25)
		csbi.wAttributes |= FOREGROUND_BLUE;
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN |
	    BACKGROUND_BLUE | BACKGROUND_INTENSITY);

	[color getRed: &red
		green: &green
		 blue: &blue
		alpha: NULL];

	if (red >= 0.25)
		csbi.wAttributes |= BACKGROUND_RED;
	if (green >= 0.25)
		csbi.wAttributes |= BACKGROUND_GREEN;
	if (blue >= 0.25)
		csbi.wAttributes |= BACKGROUND_BLUE;







|
<
<
<







506
507
508
509
510
511
512
513



514
515
516
517
518
519
520

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN |
	    BACKGROUND_BLUE | BACKGROUND_INTENSITY);

	[color getRed: &red green: &green blue: &blue alpha: NULL];




	if (red >= 0.25)
		csbi.wAttributes |= BACKGROUND_RED;
	if (green >= 0.25)
		csbi.wAttributes |= BACKGROUND_GREEN;
	if (blue >= 0.25)
		csbi.wAttributes |= BACKGROUND_BLUE;
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
		return;

	csbi.dwCursorPosition.X = column;

	SetConsoleCursorPosition(_handle, csbi.dwCursorPosition);
}

- (void)setCursorPosition: (of_point_t)position
{
	if (position.x < 0 || position.y < 0)
		@throw [OFInvalidArgumentException exception];

	SetConsoleCursorPosition(_handle, (COORD){ position.x, position.y });
}

- (void)setRelativeCursorPosition: (of_point_t)position
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.dwCursorPosition.X += position.x;
	csbi.dwCursorPosition.Y += position.y;

	SetConsoleCursorPosition(_handle, csbi.dwCursorPosition);
}
@end







|







|












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
		return;

	csbi.dwCursorPosition.X = column;

	SetConsoleCursorPosition(_handle, csbi.dwCursorPosition);
}

- (void)setCursorPosition: (OFPoint)position
{
	if (position.x < 0 || position.y < 0)
		@throw [OFInvalidArgumentException exception];

	SetConsoleCursorPosition(_handle, (COORD){ position.x, position.y });
}

- (void)setRelativeCursorPosition: (OFPoint)position
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.dwCursorPosition.X += position.x;
	csbi.dwCursorPosition.Y += position.y;

	SetConsoleCursorPosition(_handle, csbi.dwCursorPosition);
}
@end

Modified src/OFWindowsRegistryKey.h from [fcd22050ce] to [c114c8ff33].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

/**
 * @brief Opens the subkey at the specified path.
 *
 * @param path The path of the subkey to open
 * @param securityAndAccessRights Please refer to the `RegOpenKeyEx()`
 *				  documentation for `samDesired`
 * @return The subkey with the specified path, or nil if it does not exist
 */
- (nullable OFWindowsRegistryKey *)
	   openSubkeyAtPath: (OFString *)path
    securityAndAccessRights: (REGSAM)securityAndAccessRights;

/**
 * @brief Opens the subkey at the specified path.
 *
 * @param path The path of the subkey to open
 * @param options Please refer to the `RegOpenKeyEx()` documentation for
 *		  `ulOptions`. Usually 0.
 * @param securityAndAccessRights Please refer to the `RegOpenKeyEx()`
 *				  documentation for `samDesired`
 * @return The subkey with the specified path, or nil if it does not exist
 */
- (nullable OFWindowsRegistryKey *)
	   openSubkeyAtPath: (OFString *)path
		    options: (DWORD)options
    securityAndAccessRights: (REGSAM)securityAndAccessRights;

/**
 * @brief Creates a subkey at the specified path or opens it if it already
 *	  exists.
 *
 * @param path The path of the subkey to create
 * @param securityAndAccessRights Please refer to the `RegCreateKeyEx()`







|

<
|
|









|

<
|
|
|







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

/**
 * @brief Opens the subkey at the specified path.
 *
 * @param path The path of the subkey to open
 * @param securityAndAccessRights Please refer to the `RegOpenKeyEx()`
 *				  documentation for `samDesired`
 * @return The subkey with the specified path
 */

- (OFWindowsRegistryKey *)openSubkeyAtPath: (OFString *)path
		   securityAndAccessRights: (REGSAM)securityAndAccessRights;

/**
 * @brief Opens the subkey at the specified path.
 *
 * @param path The path of the subkey to open
 * @param options Please refer to the `RegOpenKeyEx()` documentation for
 *		  `ulOptions`. Usually 0.
 * @param securityAndAccessRights Please refer to the `RegOpenKeyEx()`
 *				  documentation for `samDesired`
 * @return The subkey with the specified path
 */

- (OFWindowsRegistryKey *)openSubkeyAtPath: (OFString *)path
				   options: (DWORD)options
		   securityAndAccessRights: (REGSAM)securityAndAccessRights;

/**
 * @brief Creates a subkey at the specified path or opens it if it already
 *	  exists.
 *
 * @param path The path of the subkey to create
 * @param securityAndAccessRights Please refer to the `RegCreateKeyEx()`
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
    securityAndAccessRights: (REGSAM)securityAndAccessRights
	 securityAttributes: (nullable SECURITY_ATTRIBUTES *)securityAttributes
		disposition: (nullable DWORD *)disposition;

/**
 * @brief Returns the data for the specified value at the specified path.
 *
 * @param value The name of the value to return
 * @param type A pointer to store the type of the value, or NULL
 * @return The data for the specified value
 */
- (nullable OFData *)dataForValue: (nullable OFString *)value
			     type: (nullable DWORD *)type;

/**
 * @brief Sets the data for the specified value.
 *
 * @param data The data to set the value to
 * @param value The name of the value to set
 * @param type The type for the value
 */
- (void)setData: (nullable OFData *)data
       forValue: (nullable OFString *)value
	   type: (DWORD)type;

/**
 * @brief Returns the string for the specified value at the specified path.
 *
 * @param value The name of the value to return
 * @return The string for the specified value
 */
- (nullable OFString *)stringForValue: (nullable OFString *)value;

/**
 * @brief Returns the string for the specified value at the specified path.
 *
 * @param value The name of the value to return
 * @param type A pointer to store the type of the value, or NULL
 * @return The string for the specified value
 */
- (nullable OFString *)stringForValue: (nullable OFString *)value
				 type: (nullable DWORD *)type;

/**
 * @brief Sets the string for the specified value.
 *
 * @param string The string to set the value to
 * @param value The name of the value to set
 */
- (void)setString: (nullable OFString *)string
	 forValue: (nullable OFString *)value;

/**
 * @brief Sets the string for the specified value.
 *
 * @param string The string to set the value to
 * @param value The name of the value to set
 * @param type The type for the value
 */
- (void)setString: (nullable OFString *)string
	 forValue: (nullable OFString *)value
	     type: (DWORD)type;

/**
































 * @brief Deletes the specified value.
 *
 * @param value The value to delete
 */
- (void)deleteValue: (nullable OFString *)value;

/**
 * @brief Deletes the specified subkey.
 *
 * @param subkeyPath The path of the subkey to delete
 */
- (void)deleteSubkeyAtPath: (OFString *)subkeyPath;
@end

OF_ASSUME_NONNULL_END







|



|
|





|



|





|


|




|



|
|





|


|





|



|



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


|

|










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
    securityAndAccessRights: (REGSAM)securityAndAccessRights
	 securityAttributes: (nullable SECURITY_ATTRIBUTES *)securityAttributes
		disposition: (nullable DWORD *)disposition;

/**
 * @brief Returns the data for the specified value at the specified path.
 *
 * @param name The name of the value to return
 * @param type A pointer to store the type of the value, or NULL
 * @return The data for the specified value
 */
- (nullable OFData *)dataForValueNamed: (nullable OFString *)name
				  type: (nullable DWORD *)type;

/**
 * @brief Sets the data for the specified value.
 *
 * @param data The data to set the value to
 * @param name The name of the value to set
 * @param type The type for the value
 */
- (void)setData: (nullable OFData *)data
  forValueNamed: (nullable OFString *)name
	   type: (DWORD)type;

/**
 * @brief Returns the string for the specified value at the specified path.
 *
 * @param name The name of the value to return
 * @return The string for the specified value
 */
- (nullable OFString *)stringForValueNamed: (nullable OFString *)name;

/**
 * @brief Returns the string for the specified value at the specified path.
 *
 * @param name The name of the value to return
 * @param type A pointer to store the type of the value, or NULL
 * @return The string for the specified value
 */
- (nullable OFString *)stringForValueNamed: (nullable OFString *)name
				      type: (nullable DWORD *)type;

/**
 * @brief Sets the string for the specified value.
 *
 * @param string The string to set the value to
 * @param name The name of the value to set
 */
- (void)setString: (nullable OFString *)string
    forValueNamed: (nullable OFString *)name;

/**
 * @brief Sets the string for the specified value.
 *
 * @param string The string to set the value to
 * @param name The name of the value to set
 * @param type The type for the value
 */
- (void)setString: (nullable OFString *)string
    forValueNamed: (nullable OFString *)name
	     type: (DWORD)type;

/**
 * @brief Returns the DWORD for the specified value at the specified path.
 *
 * @param name The name of the value to return
 * @return The DWORD for the specified value
 */
- (uint32_t)DWORDForValueNamed: (nullable OFString *)name;

/**
 * @brief Sets the DWORD for the specified value.
 *
 * @param dword The DWORD to set the value to
 * @param name The name of the value to set
 */
- (void)setDWORD: (uint32_t)dword forValueNamed: (nullable OFString *)name;

/**
 * @brief Returns the QWORD for the specified value at the specified path.
 *
 * @param name The name of the value to return
 * @return The QWORD for the specified value
 */
- (uint64_t)QWORDForValueNamed: (nullable OFString *)name;

/**
 * @brief Sets the QWORD for the specified value.
 *
 * @param qword The QWORD to set the value to
 * @param name The name of the value to set
 */
- (void)setQWORD: (uint64_t)qword forValueNamed: (nullable OFString *)name;

/**
 * @brief Deletes the specified value.
 *
 * @param name The value to delete
 */
- (void)deleteValueNamed: (nullable OFString *)name;

/**
 * @brief Deletes the specified subkey.
 *
 * @param subkeyPath The path of the subkey to delete
 */
- (void)deleteSubkeyAtPath: (OFString *)subkeyPath;
@end

OF_ASSUME_NONNULL_END

Modified src/OFWindowsRegistryKey.m from [db8bc56ab6] to [d93d6521e9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
#import "OFDeleteWindowsRegistryValueFailedException.h"
#import "OFGetWindowsRegistryValueFailedException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOpenWindowsRegistryKeyFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFSetWindowsRegistryValueFailedException.h"


OF_DIRECT_MEMBERS
@interface OFWindowsRegistryKey ()
- (instancetype)of_initWithHKey: (HKEY)hKey
			  close: (bool)close;
@end

@implementation OFWindowsRegistryKey
+ (instancetype)classesRootKey
{
	return [[[self alloc] of_initWithHKey: HKEY_CLASSES_ROOT
					close: false] autorelease];







>



|
<







27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
#import "OFDeleteWindowsRegistryValueFailedException.h"
#import "OFGetWindowsRegistryValueFailedException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOpenWindowsRegistryKeyFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFSetWindowsRegistryValueFailedException.h"
#import "OFUndefinedKeyException.h"

OF_DIRECT_MEMBERS
@interface OFWindowsRegistryKey ()
- (instancetype)of_initWithHKey: (HKEY)hKey close: (bool)close;

@end

@implementation OFWindowsRegistryKey
+ (instancetype)classesRootKey
{
	return [[[self alloc] of_initWithHKey: HKEY_CLASSES_ROOT
					close: false] autorelease];
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

+ (instancetype)usersKey
{
	return [[[self alloc] of_initWithHKey: HKEY_USERS
					close: false] autorelease];
}

- (instancetype)of_initWithHKey: (HKEY)hKey
			  close: (bool)close
{
	self = [super init];

	_hKey = hKey;
	_close = close;

	return self;







|
<







65
66
67
68
69
70
71
72

73
74
75
76
77
78
79

+ (instancetype)usersKey
{
	return [[[self alloc] of_initWithHKey: HKEY_USERS
					close: false] autorelease];
}

- (instancetype)of_initWithHKey: (HKEY)hKey close: (bool)close

{
	self = [super init];

	_hKey = hKey;
	_close = close;

	return self;
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
		status = RegOpenKeyExW(_hKey, path.UTF16String, options,
		    securityAndAccessRights, &subKey);
	else
		status = RegOpenKeyExA(_hKey,
		    [path cStringWithEncoding: [OFLocale encoding]], options,
		    securityAndAccessRights, &subKey);

	if (status != ERROR_SUCCESS) {
		if (status == ERROR_FILE_NOT_FOUND) {
			objc_autoreleasePoolPop(pool);
			return nil;
		}

		@throw [OFOpenWindowsRegistryKeyFailedException
		    exceptionWithRegistryKey: self
					path: path
				     options: options
		     securityAndAccessRights: securityAndAccessRights
				      status: status];
	}

	objc_autoreleasePoolPop(pool);

	return [[[OFWindowsRegistryKey alloc] of_initWithHKey: subKey
							close: true]
	    autorelease];
}







|
<
<
<
<
<






<







112
113
114
115
116
117
118
119





120
121
122
123
124
125

126
127
128
129
130
131
132
		status = RegOpenKeyExW(_hKey, path.UTF16String, options,
		    securityAndAccessRights, &subKey);
	else
		status = RegOpenKeyExA(_hKey,
		    [path cStringWithEncoding: [OFLocale encoding]], options,
		    securityAndAccessRights, &subKey);

	if (status != ERROR_SUCCESS)





		@throw [OFOpenWindowsRegistryKeyFailedException
		    exceptionWithRegistryKey: self
					path: path
				     options: options
		     securityAndAccessRights: securityAndAccessRights
				      status: status];


	objc_autoreleasePoolPop(pool);

	return [[[OFWindowsRegistryKey alloc] of_initWithHKey: subKey
							close: true]
	    autorelease];
}
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
	objc_autoreleasePoolPop(pool);

	return [[[OFWindowsRegistryKey alloc] of_initWithHKey: subKey
							close: true]
	    autorelease];
}

- (OFData *)dataForValue: (OFString *)value
		    type: (DWORD *)type
{
	void *pool = objc_autoreleasePoolPush();
	BYTE stackBuffer[256], *buffer = stackBuffer;
	DWORD length = sizeof(stackBuffer);
	OFMutableData *ret = nil;
	bool winNT = [OFSystemInfo isWindowsNT];
	LSTATUS status;

	for (;;) {
		if (winNT)
			status = RegQueryValueExW(_hKey, value.UTF16String,
			    NULL, type, buffer, &length);
		else
			status = RegQueryValueExA(_hKey,
			    [value cStringWithEncoding: [OFLocale encoding]],
			    NULL, type, buffer, &length);

		switch (status) {
		case ERROR_SUCCESS:
			if (buffer == stackBuffer) {
				objc_autoreleasePoolPop(pool);








|
<










|



|







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
	objc_autoreleasePoolPop(pool);

	return [[[OFWindowsRegistryKey alloc] of_initWithHKey: subKey
							close: true]
	    autorelease];
}

- (OFData *)dataForValueNamed: (OFString *)name type: (DWORD *)type

{
	void *pool = objc_autoreleasePoolPush();
	BYTE stackBuffer[256], *buffer = stackBuffer;
	DWORD length = sizeof(stackBuffer);
	OFMutableData *ret = nil;
	bool winNT = [OFSystemInfo isWindowsNT];
	LSTATUS status;

	for (;;) {
		if (winNT)
			status = RegQueryValueExW(_hKey, name.UTF16String,
			    NULL, type, buffer, &length);
		else
			status = RegQueryValueExA(_hKey,
			    [name cStringWithEncoding: [OFLocale encoding]],
			    NULL, type, buffer, &length);

		switch (status) {
		case ERROR_SUCCESS:
			if (buffer == stackBuffer) {
				objc_autoreleasePoolPop(pool);

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
			[ret increaseCountBy: length];
			buffer = ret.mutableItems;

			continue;
		default:
			@throw [OFGetWindowsRegistryValueFailedException
			    exceptionWithRegistryKey: self
					       value: value
					      status: status];
		}
	}
}

- (void)setData: (OFData *)data
       forValue: (OFString *)value
	   type: (DWORD)type
{
	size_t length = data.count * data.itemSize;
	LSTATUS status;

	if (length > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	if ([OFSystemInfo isWindowsNT])
		status = RegSetValueExW(_hKey, value.UTF16String, 0, type,
		    data.items, (DWORD)length);
	else
		status = RegSetValueExA(_hKey,
		    [value cStringWithEncoding: [OFLocale encoding]], 0, type,
		    data.items, (DWORD)length);

	if (status != ERROR_SUCCESS)
		@throw [OFSetWindowsRegistryValueFailedException
		    exceptionWithRegistryKey: self
				       value: value
					data: data
					type: type
				      status: status];
}

- (OFString *)stringForValue: (OFString *)value
{
	return [self stringForValue: value
			       type: NULL];
}

- (OFString *)stringForValue: (OFString *)value
			type: (DWORD *)typeOut
{
	void *pool = objc_autoreleasePoolPush();
	DWORD type;
	OFData *data = [self dataForValue: value
				     type: &type];
	OFString *ret;

	if (data == nil)
		return nil;

	if (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_LINK)
		@throw [OFInvalidEncodingException exception];

	if (data.itemSize != 1)
		@throw [OFInvalidFormatException exception];

	if ([OFSystemInfo isWindowsNT]) {
		const of_char16_t *UTF16String = data.items;
		size_t length = data.count;

		if (length % 2 == 1)
			@throw [OFInvalidFormatException exception];

		length /= 2;








|






|









|



|





|





|

|
<


|
<



|
<












|







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
			[ret increaseCountBy: length];
			buffer = ret.mutableItems;

			continue;
		default:
			@throw [OFGetWindowsRegistryValueFailedException
			    exceptionWithRegistryKey: self
					   valueName: name
					      status: status];
		}
	}
}

- (void)setData: (OFData *)data
  forValueNamed: (OFString *)name
	   type: (DWORD)type
{
	size_t length = data.count * data.itemSize;
	LSTATUS status;

	if (length > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	if ([OFSystemInfo isWindowsNT])
		status = RegSetValueExW(_hKey, name.UTF16String, 0, type,
		    data.items, (DWORD)length);
	else
		status = RegSetValueExA(_hKey,
		    [name cStringWithEncoding: [OFLocale encoding]], 0, type,
		    data.items, (DWORD)length);

	if (status != ERROR_SUCCESS)
		@throw [OFSetWindowsRegistryValueFailedException
		    exceptionWithRegistryKey: self
				   valueName: name
					data: data
					type: type
				      status: status];
}

- (OFString *)stringForValueNamed: (OFString *)name
{
	return [self stringForValueNamed: name type: NULL];

}

- (OFString *)stringForValueNamed: (OFString *)name type: (DWORD *)typeOut

{
	void *pool = objc_autoreleasePoolPush();
	DWORD type;
	OFData *data = [self dataForValueNamed: name type: &type];

	OFString *ret;

	if (data == nil)
		return nil;

	if (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_LINK)
		@throw [OFInvalidEncodingException exception];

	if (data.itemSize != 1)
		@throw [OFInvalidFormatException exception];

	if ([OFSystemInfo isWindowsNT]) {
		const OFChar16 *UTF16String = data.items;
		size_t length = data.count;

		if (length % 2 == 1)
			@throw [OFInvalidFormatException exception];

		length /= 2;

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
		*typeOut = type;

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (void)setString: (OFString *)string
	 forValue: (OFString *)value
{
	[self setString: string
	       forValue: value
		   type: REG_SZ];
}

- (void)setString: (OFString *)string
	 forValue: (OFString *)value
	     type: (DWORD)type
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data;

	if ([OFSystemInfo isWindowsNT])
		data = [OFData dataWithItems: string.UTF16String
				    itemSize: sizeof(of_char16_t)
				       count: string.UTF16StringLength + 1];

	else {
		of_string_encoding_t encoding = [OFLocale encoding];
		const char *cString = [string cStringWithEncoding: encoding];
		size_t length = [string cStringLengthWithEncoding: encoding];

		data = [OFData dataWithItems: cString

				       count: length + 1];

	}































	[self setData: data


	     forValue: value



		 type: type];


























	objc_autoreleasePoolPop(pool);
}

- (void)deleteValue: (OFString *)value
{
	void *pool = objc_autoreleasePoolPush();
	LSTATUS status;

	if ([OFSystemInfo isWindowsNT])
		status = RegDeleteValueW(_hKey, value.UTF16String);
	else
		status = RegDeleteValueA(_hKey,
		    [value cStringWithEncoding: [OFLocale encoding]]);

	if (status != ERROR_SUCCESS)
		@throw [OFDeleteWindowsRegistryValueFailedException
		    exceptionWithRegistryKey: self
				       value: value
				      status: status];

	objc_autoreleasePoolPop(pool);
}

- (void)deleteSubkeyAtPath: (OFString *)subkeyPath
{







|
<

|
<
<



|







<
|
>

|



|
>
|
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
|
>
>
>
|
>
>

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



|





|


|




|







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
		*typeOut = type;

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (void)setString: (OFString *)string forValueNamed: (OFString *)name

{
	[self setString: string forValueNamed: name type: REG_SZ];


}

- (void)setString: (OFString *)string
    forValueNamed: (OFString *)name
	     type: (DWORD)type
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data;

	if ([OFSystemInfo isWindowsNT])
		data = [OFData dataWithItems: string.UTF16String

				       count: string.UTF16StringLength + 1
				    itemSize: sizeof(OFChar16)];
	else {
		OFStringEncoding encoding = [OFLocale encoding];
		const char *cString = [string cStringWithEncoding: encoding];
		size_t length = [string cStringLengthWithEncoding: encoding];

		data = [OFData dataWithItems: cString count: length + 1];
	}

	[self setData: data forValueNamed: name type: type];

	objc_autoreleasePoolPop(pool);
}

- (uint32_t)DWORDForValueNamed: (OFString *)name
{
	void *pool = objc_autoreleasePoolPush();
	DWORD type, ret;
	OFData *data = [self dataForValueNamed: name type: &type];

	if (data == nil)
		@throw [OFUndefinedKeyException exceptionWithObject: self
								key: name
							      value: nil];

	if (type != REG_DWORD)
		@throw [OFInvalidEncodingException exception];

	if (data.count != sizeof(ret) || data.itemSize != 1)
		@throw [OFInvalidFormatException exception];

	memcpy(&ret, data.items, sizeof(ret));

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (void)setDWORD: (uint32_t)dword forValueNamed: (OFString *)name
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data = [OFData dataWithItems: &dword count: sizeof(dword)];
	[self setData: data forValueNamed: name type: REG_DWORD];
	objc_autoreleasePoolPop(pool);
}

- (uint64_t)QWORDForValueNamed: (OFString *)name
{
	void *pool = objc_autoreleasePoolPush();
	DWORD type;
	uint64_t ret;
	OFData *data = [self dataForValueNamed: name type: &type];

	if (data == nil)
		@throw [OFUndefinedKeyException exceptionWithObject: self
								key: name
							      value: nil];

	if (type != REG_QWORD)
		@throw [OFInvalidEncodingException exception];

	if (data.count != sizeof(ret) || data.itemSize != 1)
		@throw [OFInvalidFormatException exception];

	memcpy(&ret, data.items, sizeof(ret));

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (void)setQWORD: (uint64_t)qword forValueNamed: (OFString *)name
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data = [OFData dataWithItems: &qword count: sizeof(qword)];
	[self setData: data forValueNamed: name type: REG_QWORD];
	objc_autoreleasePoolPop(pool);
}

- (void)deleteValueNamed: (OFString *)name
{
	void *pool = objc_autoreleasePoolPush();
	LSTATUS status;

	if ([OFSystemInfo isWindowsNT])
		status = RegDeleteValueW(_hKey, name.UTF16String);
	else
		status = RegDeleteValueA(_hKey,
		    [name cStringWithEncoding: [OFLocale encoding]]);

	if (status != ERROR_SUCCESS)
		@throw [OFDeleteWindowsRegistryValueFailedException
		    exceptionWithRegistryKey: self
				   valueName: name
				      status: status];

	objc_autoreleasePoolPop(pool);
}

- (void)deleteSubkeyAtPath: (OFString *)subkeyPath
{

Modified src/OFXMLAttribute.h from [d76aef65ee] to [facd4b4e2b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
/**
 * @brief The namespace of the attribute.
 */
#ifndef __cplusplus
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *namespace;
#else
@property OF_NULLABLE_PROPERTY (readonly, nonatomic, getter=namespace)
    OFString *namespace_;
#endif

/**
 * @brief Creates a new XML attribute.
 *
 * @param name The name of the attribute
 * @param stringValue The string value of the attribute
 * @return A new autoreleased OFXMLAttribute with the specified parameters
 */
+ (instancetype)attributeWithName: (OFString *)name
		      stringValue: (OFString *)stringValue;

/**
 * @brief Creates a new XML attribute.
 *
 * @param name The name of the attribute
 * @param namespace_ The namespace of the attribute
 * @param stringValue The string value of the attribute
 * @return A new autoreleased OFXMLAttribute with the specified parameters
 */
+ (instancetype)attributeWithName: (OFString *)name
			namespace: (nullable OFString *)namespace_
		      stringValue: (OFString *)stringValue;

/**
 * @brief Initializes an already allocated OFXMLAttribute.
 *
 * @param name The name of the attribute
 * @param stringValue The string value of the attribute
 * @return An initialized OFXMLAttribute with the specified parameters
 */
- (instancetype)initWithName: (OFString *)name
		 stringValue: (OFString *)stringValue;

/**
 * @brief Initializes an already allocated OFXMLAttribute.
 *
 * @param name The name of the attribute
 * @param namespace_ The namespace of the attribute
 * @param stringValue The string value of the attribute
 * @return An initialized OFXMLAttribute with the specified parameters
 */
- (instancetype)initWithName: (OFString *)name
		   namespace: (nullable OFString *)namespace_
		 stringValue: (OFString *)stringValue;

- (instancetype)initWithSerialization: (OFXMLElement *)element;
@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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
 * @brief The namespace of the attribute.
 */
#ifndef __cplusplus
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *namespace;
#else
@property OF_NULLABLE_PROPERTY (readonly, nonatomic, getter=namespace)
    OFString *nameSpace;
#endif

/**
 * @brief Creates a new XML attribute.
 *
 * @param name The name of the attribute
 * @param stringValue The string value of the attribute
 * @return A new autoreleased OFXMLAttribute with the specified parameters
 */
+ (instancetype)attributeWithName: (OFString *)name
		      stringValue: (OFString *)stringValue;

/**
 * @brief Creates a new XML attribute.
 *
 * @param name The name of the attribute
 * @param nameSpace The namespace of the attribute
 * @param stringValue The string value of the attribute
 * @return A new autoreleased OFXMLAttribute with the specified parameters
 */
+ (instancetype)attributeWithName: (OFString *)name
			namespace: (nullable OFString *)nameSpace
		      stringValue: (OFString *)stringValue;

/**
 * @brief Initializes an already allocated OFXMLAttribute.
 *
 * @param name The name of the attribute
 * @param stringValue The string value of the attribute
 * @return An initialized OFXMLAttribute with the specified parameters
 */
- (instancetype)initWithName: (OFString *)name
		 stringValue: (OFString *)stringValue;

/**
 * @brief Initializes an already allocated OFXMLAttribute.
 *
 * @param name The name of the attribute
 * @param nameSpace The namespace of the attribute
 * @param stringValue The string value of the attribute
 * @return An initialized OFXMLAttribute with the specified parameters
 */
- (instancetype)initWithName: (OFString *)name
		   namespace: (nullable OFString *)nameSpace
		 stringValue: (OFString *)stringValue;

- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end

OF_ASSUME_NONNULL_END

Modified src/OFXMLAttribute.m from [182fa6d94f] to [f2a6cbc394].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		_name = [[element attributeForName: @"name"].stringValue copy];
		_namespace = [[element attributeForName: @"namespace"]
		    .stringValue copy];
		_stringValue = [[element attributeForName: @"stringValue"]
		    .stringValue copy];







|







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		_name = [[element attributeForName: @"name"].stringValue copy];
		_namespace = [[element attributeForName: @"namespace"]
		    .stringValue copy];
		_stringValue = [[element attributeForName: @"stringValue"]
		    .stringValue copy];
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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD_HASH(hash, _namespace.hash);
	OF_HASH_ADD_HASH(hash, _stringValue.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

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

	element = [OFXMLElement elementWithName: self.className
				      namespace: OF_SERIALIZATION_NS];

	[element addAttributeWithName: @"name"
			  stringValue: _name];

	if (_namespace != nil)
		[element addAttributeWithName: @"namespace"
				  stringValue: _namespace];

	[element addAttributeWithName: @"stringValue"
			  stringValue: _stringValue];







|

|

|
|
|

|










|
<
|
<







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
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddHash(&hash, _namespace.hash);
	OFHashAddHash(&hash, _stringValue.hash);

	OFHashFinalize(&hash);

	return hash;
}

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

	element = [OFXMLElement elementWithName: self.className
				      namespace: OFSerializationNS];

	[element addAttributeWithName: @"name" stringValue: _name];


	if (_namespace != nil)
		[element addAttributeWithName: @"namespace"
				  stringValue: _namespace];

	[element addAttributeWithName: @"stringValue"
			  stringValue: _stringValue];

Modified src/OFXMLCDATA.h from [5fd6a953ec] to [566e9353f4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFXMLCDATA.m from [bc1aa40b4f] to [53038bdcd2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		_CDATA = [element.stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];







|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		_CDATA = [element.stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
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

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
	return self.XMLString;
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
				level: (unsigned int)level
{
	return self.XMLString;
}

- (OFString *)description
{
	return self.XMLString;
}

- (OFXMLElement *)XMLElementBySerializing
{
	OFXMLElement *element =
	    [OFXMLElement elementWithName: self.className
				namespace: OF_SERIALIZATION_NS];
	[element addChild: self];

	return element;
}
@end







|













|





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

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
	return self.XMLString;
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
				 level: (unsigned int)level
{
	return self.XMLString;
}

- (OFString *)description
{
	return self.XMLString;
}

- (OFXMLElement *)XMLElementBySerializing
{
	OFXMLElement *element =
	    [OFXMLElement elementWithName: self.className
				namespace: OFSerializationNS];
	[element addChild: self];

	return element;
}
@end

Modified src/OFXMLCharacters.h from [9150bc53a5] to [314ee7db3d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFXMLCharacters.m from [a2bb78c19c] to [9ea4b56013].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		_characters = [element.stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];







|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		_characters = [element.stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
125
126
127
128
129
130
131
132
133
134
135
{
	return _characters.stringByXMLEscaping;
}

- (OFXMLElement *)XMLElementBySerializing
{
	return [OFXMLElement elementWithName: self.className
				   namespace: OF_SERIALIZATION_NS
				 stringValue: _characters];
}
@end







|



123
124
125
126
127
128
129
130
131
132
133
{
	return _characters.stringByXMLEscaping;
}

- (OFXMLElement *)XMLElementBySerializing
{
	return [OFXMLElement elementWithName: self.className
				   namespace: OFSerializationNS
				 stringValue: _characters];
}
@end

Modified src/OFXMLComment.h from [ca8a1afadc] to [cf96f4e7b5].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
/**
 * @class OFXMLComment OFXMLComment.h ObjFW/OFXMLComment.h
 *
 * @brief A class for representing XML comments.
 */
@interface OFXMLComment: OFXMLNode
{
	OFString *_comment;
	OF_RESERVE_IVARS(OFXMLComment, 4)
}

/**





 * @brief Creates a new OFXMLComment with the specified string.
 *
 * @param string The string for the comment
 * @return A new OFXMLComment
 */
+ (instancetype)commentWithString: (OFString *)string;

/**
 * @brief Initializes an already allocated OFXMLComment with the specified
 *	  string.
 *
 * @param string The string for the comment
 * @return An initialized OFXMLComment
 */
- (instancetype)initWithString: (OFString *)string;

- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end

OF_ASSUME_NONNULL_END







|




>
>
>
>
>
|

|


|



|

|


|





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
/**
 * @class OFXMLComment OFXMLComment.h ObjFW/OFXMLComment.h
 *
 * @brief A class for representing XML comments.
 */
@interface OFXMLComment: OFXMLNode
{
	OFString *_text;
	OF_RESERVE_IVARS(OFXMLComment, 4)
}

/**
 * @brief The comment text.
 */
@property (readonly, nonatomic) OFString *text;

/**
 * @brief Creates a new OFXMLComment with the specified text.
 *
 * @param text The text for the comment
 * @return A new OFXMLComment
 */
+ (instancetype)commentWithText: (OFString *)text;

/**
 * @brief Initializes an already allocated OFXMLComment with the specified
 *	  text.
 *
 * @param text The text for the comment
 * @return An initialized OFXMLComment
 */
- (instancetype)initWithText: (OFString *)text;

- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end

OF_ASSUME_NONNULL_END

Modified src/OFXMLComment.m from [cfed32bae5] to [842e736751].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFXMLNode+Private.h"
#import "OFString.h"
#import "OFXMLElement.h"

#import "OFInvalidArgumentException.h"

@implementation OFXMLComment


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

- (instancetype)initWithString: (OFString *)string
{
	self = [super of_init];

	@try {
		_comment = [string copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		_comment = [element.stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_comment release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFXMLComment *comment;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFXMLComment class]])
		return false;

	comment = object;

	return ([comment->_comment isEqual: _comment]);
}

- (unsigned long)hash
{
	return _comment.hash;
}

- (OFString *)stringValue
{
	return @"";
}

- (OFString *)XMLString
{
	return [OFString stringWithFormat: @"<!--%@-->", _comment];
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
	return [OFString stringWithFormat: @"<!--%@-->", _comment];
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
				 level: (unsigned int)level
{
	OFString *ret;

	if (indentation > 0 && level > 0) {
		char *whitespaces = [self allocMemoryWithSize:
		    (level * indentation) + 1];
		memset(whitespaces, ' ', level * indentation);
		whitespaces[level * indentation] = 0;

		@try {
			ret = [OFString stringWithFormat: @"%s<!--%@-->",
							  whitespaces,
							  _comment];
		} @finally {
			[self freeMemory: whitespaces];
		}
	} else
		ret = [OFString stringWithFormat: @"<!--%@-->", _comment];

	return ret;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<!--%@-->", _comment];
}

- (OFXMLElement *)XMLElementBySerializing
{
	return [OFXMLElement elementWithName: self.className
				   namespace: OF_SERIALIZATION_NS
				 stringValue: _comment];
}
@end







>
>
|

|


|




|
















|


|












|
















|




|









|




|








<
|





|
<

|


|






|





|
|


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
#import "OFXMLNode+Private.h"
#import "OFString.h"
#import "OFXMLElement.h"

#import "OFInvalidArgumentException.h"

@implementation OFXMLComment
@synthesize text = _text;

+ (instancetype)commentWithText: (OFString *)text
{
	return [[[self alloc] initWithText: text] autorelease];
}

- (instancetype)initWithText: (OFString *)text
{
	self = [super of_init];

	@try {
		_text = [text copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		_text = [element.stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_text release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFXMLComment *comment;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFXMLComment class]])
		return false;

	comment = object;

	return ([comment->_text isEqual: _text]);
}

- (unsigned long)hash
{
	return _text.hash;
}

- (OFString *)stringValue
{
	return @"";
}

- (OFString *)XMLString
{
	return [OFString stringWithFormat: @"<!--%@-->", _text];
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
	return [OFString stringWithFormat: @"<!--%@-->", _text];
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
				 level: (unsigned int)level
{
	OFString *ret;

	if (indentation > 0 && level > 0) {

		char *whitespaces = OFAllocMemory((level * indentation) + 1, 1);
		memset(whitespaces, ' ', level * indentation);
		whitespaces[level * indentation] = 0;

		@try {
			ret = [OFString stringWithFormat: @"%s<!--%@-->",
							  whitespaces, _text];

		} @finally {
			OFFreeMemory(whitespaces);
		}
	} else
		ret = [OFString stringWithFormat: @"<!--%@-->", _text];

	return ret;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<!--%@-->", _text];
}

- (OFXMLElement *)XMLElementBySerializing
{
	return [OFXMLElement elementWithName: self.className
				   namespace: OFSerializationNS
				 stringValue: _text];
}
@end

Modified src/OFXMLElement+Serialization.h from [a15c443f2f] to [fdd03ade73].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFXMLElement+Serialization.m from [22e42fb0f7] to [c71645309e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
- (id)objectByDeserializing
{
	void *pool = objc_autoreleasePoolPush();
	Class class;
	id object;

	if ((class = objc_getClass([_name cStringWithEncoding:
	    OF_STRING_ENCODING_ASCII])) == Nil)
		@throw [OFInvalidArgumentException exception];

	if (![class conformsToProtocol: @protocol(OFSerialization)])
		@throw [OFInvalidArgumentException exception];

	object = [[class alloc] initWithSerialization: self];

	objc_autoreleasePoolPop(pool);

	return [object autorelease];
}
@end







|












28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
- (id)objectByDeserializing
{
	void *pool = objc_autoreleasePoolPush();
	Class class;
	id object;

	if ((class = objc_getClass([_name cStringWithEncoding:
	    OFStringEncodingASCII])) == Nil)
		@throw [OFInvalidArgumentException exception];

	if (![class conformsToProtocol: @protocol(OFSerialization)])
		@throw [OFInvalidArgumentException exception];

	object = [[class alloc] initWithSerialization: self];

	objc_autoreleasePoolPop(pool);

	return [object autorelease];
}
@end

Modified src/OFXMLElement.h from [817f97f00f] to [470c1e0790].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25

26
27
28
29
30
31
32

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableString;

@class OFString;
@class OFXMLAttribute;

/**
 * @class OFXMLElement OFXMLElement.h ObjFW/OFXMLElement.h
 *
 * @brief A class which stores an XML element.







>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableString;
@class OFStream;
@class OFString;
@class OFXMLAttribute;

/**
 * @class OFXMLElement OFXMLElement.h ObjFW/OFXMLElement.h
 *
 * @brief A class which stores an XML element.
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
 * @brief The namespace of the element.
 */
#ifndef __cplusplus
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *namespace;
#else
@property OF_NULLABLE_PROPERTY (copy, nonatomic,
    getter=namespace, setter=setNamespace:) OFString *namespace_;
#endif

/**
 * @brief The default namespace for the element to be used if there is no
 *	  parent.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *defaultNamespace;







|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
 * @brief The namespace of the element.
 */
#ifndef __cplusplus
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *namespace;
#else
@property OF_NULLABLE_PROPERTY (copy, nonatomic,
    getter=namespace, setter=setNamespace:) OFString *nameSpace;
#endif

/**
 * @brief The default namespace for the element to be used if there is no
 *	  parent.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *defaultNamespace;
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
+ (instancetype)elementWithName: (OFString *)name
		    stringValue: (nullable OFString *)stringValue;

/**
 * @brief Creates a new XML element with the specified name and namespace.
 *
 * @param name The name for the element
 * @param namespace_ The namespace for the element
 * @return A new autoreleased OFXMLElement with the specified element name and
 *	   namespace
 */
+ (instancetype)elementWithName: (OFString *)name
		      namespace: (nullable OFString *)namespace_;

/**
 * @brief Creates a new XML element with the specified name, namespace and
 * 	  string value.
 *
 * @param name The name for the element
 * @param namespace_ The namespace for the element
 * @param stringValue The value for the element
 * @return A new autoreleased OFXMLElement with the specified element name,
 *	   namespace and value
 */
+ (instancetype)elementWithName: (OFString *)name
		      namespace: (nullable OFString *)namespace_
		    stringValue: (nullable OFString *)stringValue;

/**
 * @brief Creates a new element with the specified element.
 *
 * @param element An OFXMLElement to initialize the OFXMLElement with
 * @return A new autoreleased OFXMLElement with the contents of the specified
 *	   element
 */
+ (instancetype)elementWithElement: (OFXMLElement *)element;

/**
 * @brief Parses the string and returns an OFXMLElement for it.
 *
 * @param string The string to parse
 * @return A new autoreleased OFXMLElement with the contents of the string
 */
+ (instancetype)elementWithXMLString: (OFString *)string;

#ifdef OF_HAVE_FILES
/**
 * @brief Parses the specified file and returns an OFXMLElement for it.
 *
 * @param path The path to the file
 * @return A new autoreleased OFXMLElement with the contents of the specified
 *	   file
 */
+ (instancetype)elementWithFile: (OFString *)path;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFXMLElement with the specified name.
 *
 * @param name The name for the element







|




|






|





|



















<

|

|

|

|
<







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
+ (instancetype)elementWithName: (OFString *)name
		    stringValue: (nullable OFString *)stringValue;

/**
 * @brief Creates a new XML element with the specified name and namespace.
 *
 * @param name The name for the element
 * @param nameSpace The namespace for the element
 * @return A new autoreleased OFXMLElement with the specified element name and
 *	   namespace
 */
+ (instancetype)elementWithName: (OFString *)name
		      namespace: (nullable OFString *)nameSpace;

/**
 * @brief Creates a new XML element with the specified name, namespace and
 * 	  string value.
 *
 * @param name The name for the element
 * @param nameSpace The namespace for the element
 * @param stringValue The value for the element
 * @return A new autoreleased OFXMLElement with the specified element name,
 *	   namespace and value
 */
+ (instancetype)elementWithName: (OFString *)name
		      namespace: (nullable OFString *)nameSpace
		    stringValue: (nullable OFString *)stringValue;

/**
 * @brief Creates a new element with the specified element.
 *
 * @param element An OFXMLElement to initialize the OFXMLElement with
 * @return A new autoreleased OFXMLElement with the contents of the specified
 *	   element
 */
+ (instancetype)elementWithElement: (OFXMLElement *)element;

/**
 * @brief Parses the string and returns an OFXMLElement for it.
 *
 * @param string The string to parse
 * @return A new autoreleased OFXMLElement with the contents of the string
 */
+ (instancetype)elementWithXMLString: (OFString *)string;


/**
 * @brief Parses the specified stream and returns an OFXMLElement for it.
 *
 * @param stream The stream to parse
 * @return A new autoreleased OFXMLElement with the contents of the specified
 *	   stream
 */
+ (instancetype)elementWithStream: (OFStream *)stream;


- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFXMLElement with the specified name.
 *
 * @param name The name for the element
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
		 stringValue: (nullable OFString *)stringValue;

/**
 * @brief Initializes an already allocated OFXMLElement with the specified name
 *	  and namespace.
 *
 * @param name The name for the element
 * @param namespace_ The namespace for the element
 * @return An initialized OFXMLElement with the specified element name and
 *	   namespace
 */
- (instancetype)initWithName: (OFString *)name
		   namespace: (nullable OFString *)namespace_;

/**
 * @brief Initializes an already allocated OFXMLElement with the specified name,
 *	  namespace and value.
 *
 * @param name The name for the element
 * @param namespace_ The namespace for the element
 * @param stringValue The value for the element
 * @return An initialized OFXMLElement with the specified element name,
 *	   namespace and value
 */
- (instancetype)initWithName: (OFString *)name
		   namespace: (nullable OFString *)namespace_
		 stringValue: (nullable OFString *)stringValue;

/**
 * @brief Initializes an already allocated OFXMLElement with the specified
 *	  element.
 *
 * @param element An OFXMLElement to initialize the OFXMLElement with







|




|






|





|







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
		 stringValue: (nullable OFString *)stringValue;

/**
 * @brief Initializes an already allocated OFXMLElement with the specified name
 *	  and namespace.
 *
 * @param name The name for the element
 * @param nameSpace The namespace for the element
 * @return An initialized OFXMLElement with the specified element name and
 *	   namespace
 */
- (instancetype)initWithName: (OFString *)name
		   namespace: (nullable OFString *)nameSpace;

/**
 * @brief Initializes an already allocated OFXMLElement with the specified name,
 *	  namespace and value.
 *
 * @param name The name for the element
 * @param nameSpace The namespace for the element
 * @param stringValue The value for the element
 * @return An initialized OFXMLElement with the specified element name,
 *	   namespace and value
 */
- (instancetype)initWithName: (OFString *)name
		   namespace: (nullable OFString *)nameSpace
		 stringValue: (nullable OFString *)stringValue;

/**
 * @brief Initializes an already allocated OFXMLElement with the specified
 *	  element.
 *
 * @param element An OFXMLElement to initialize the OFXMLElement with
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
 *	  with it.
 *
 * @param string The string to parse
 * @return An initialized OFXMLElement with the contents of the string
 */
- (instancetype)initWithXMLString: (OFString *)string;

#ifdef OF_HAVE_FILES
/**
 * @brief Parses the specified file and initializes an already allocated
 *	  OFXMLElement with it.
 *
 * @param path The path to the file
 * @return An initialized OFXMLElement with the contents of the specified file
 */
- (instancetype)initWithFile: (OFString *)path;
#endif

- (instancetype)initWithSerialization: (OFXMLElement *)element;

/**
 * @brief Sets a prefix for a namespace.
 *
 * @param prefix The prefix for the namespace
 * @param namespace_ The namespace for which the prefix is set
 */
- (void)setPrefix: (OFString *)prefix
     forNamespace: (OFString *)namespace_;

/**
 * @brief Binds a prefix for a namespace.
 *
 * @param prefix The prefix for the namespace
 * @param namespace_ The namespace for which the prefix is bound
 */
- (void)bindPrefix: (OFString *)prefix
      forNamespace: (OFString *)namespace_;

/**
 * @brief Adds the specified attribute.
 *
 * If an attribute with the same name and namespace already exists, it is not
 * added.
 *







<

|


|
|

|
<







|

|
<





|

|
<







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
 *	  with it.
 *
 * @param string The string to parse
 * @return An initialized OFXMLElement with the contents of the string
 */
- (instancetype)initWithXMLString: (OFString *)string;


/**
 * @brief Parses the specified stream and initializes an already allocated
 *	  OFXMLElement with it.
 *
 * @param stream The stream to parse
 * @return An initialized OFXMLElement with the contents of the specified stream
 */
- (instancetype)initWithStream: (OFStream *)stream;


- (instancetype)initWithSerialization: (OFXMLElement *)element;

/**
 * @brief Sets a prefix for a namespace.
 *
 * @param prefix The prefix for the namespace
 * @param nameSpace The namespace for which the prefix is set
 */
- (void)setPrefix: (OFString *)prefix forNamespace: (OFString *)nameSpace;


/**
 * @brief Binds a prefix for a namespace.
 *
 * @param prefix The prefix for the namespace
 * @param nameSpace The namespace for which the prefix is bound
 */
- (void)bindPrefix: (OFString *)prefix forNamespace: (OFString *)nameSpace;


/**
 * @brief Adds the specified attribute.
 *
 * If an attribute with the same name and namespace already exists, it is not
 * added.
 *
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
 * @brief Adds the specified attribute with the specified namespace and string
 *	  value.
 *
 * If an attribute with the same name and namespace already exists, it is not
 * added.
 *
 * @param name The name of the attribute
 * @param namespace_ The namespace of the attribute
 * @param stringValue The value of the attribute
 */
- (void)addAttributeWithName: (OFString *)name
		   namespace: (nullable OFString *)namespace_
		 stringValue: (OFString *)stringValue;

/**
 * @brief Returns the attribute with the specified name.
 *
 * @param attributeName The name of the attribute
 * @return The attribute with the specified name







|



|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
 * @brief Adds the specified attribute with the specified namespace and string
 *	  value.
 *
 * If an attribute with the same name and namespace already exists, it is not
 * added.
 *
 * @param name The name of the attribute
 * @param nameSpace The namespace of the attribute
 * @param stringValue The value of the attribute
 */
- (void)addAttributeWithName: (OFString *)name
		   namespace: (nullable OFString *)nameSpace
		 stringValue: (OFString *)stringValue;

/**
 * @brief Returns the attribute with the specified name.
 *
 * @param attributeName The name of the attribute
 * @return The attribute with the specified name
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344

/**
 * @brief Inserts a child at the specified index.
 *
 * @param child An OFXMLNode which is added as a child
 * @param index The index where the child is added
 */
- (void)insertChild: (OFXMLNode *)child
	    atIndex: (size_t)index;

/**
 * @brief Inserts the specified children at the specified index.
 *
 * @param children An array of OFXMLNodes which are added as children
 * @param index The index where the child is added
 */







|
<







322
323
324
325
326
327
328
329

330
331
332
333
334
335
336

/**
 * @brief Inserts a child at the specified index.
 *
 * @param child An OFXMLNode which is added as a child
 * @param index The index where the child is added
 */
- (void)insertChild: (OFXMLNode *)child atIndex: (size_t)index;


/**
 * @brief Inserts the specified children at the specified index.
 *
 * @param children An array of OFXMLNodes which are added as children
 * @param index The index where the child is added
 */
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
/**
 * @brief Replaces the first child that is equal to the specified OFXMLNode
 *	  with the specified node.
 *
 * @param child The child to replace
 * @param node The node to replace the child with
 */
- (void)replaceChild: (OFXMLNode *)child
	    withNode: (OFXMLNode *)node;

/**
 * @brief Replaces the child at the specified index with the specified node.
 *
 * @param index The index of the child to replace
 * @param node The node to replace the child with
 */
- (void)replaceChildAtIndex: (size_t)index
		   withNode: (OFXMLNode *)node;

/**
 * @brief Returns all children that have the specified namespace.
 *
 * @return All children that have the specified namespace
 */
- (OFArray OF_GENERIC(OFXMLElement *) *)elementsForNamespace:







|
<







|
<







354
355
356
357
358
359
360
361

362
363
364
365
366
367
368
369

370
371
372
373
374
375
376
/**
 * @brief Replaces the first child that is equal to the specified OFXMLNode
 *	  with the specified node.
 *
 * @param child The child to replace
 * @param node The node to replace the child with
 */
- (void)replaceChild: (OFXMLNode *)child withNode: (OFXMLNode *)node;


/**
 * @brief Replaces the child at the specified index with the specified node.
 *
 * @param index The index of the child to replace
 * @param node The node to replace the child with
 */
- (void)replaceChildAtIndex: (size_t)index withNode: (OFXMLNode *)node;


/**
 * @brief Returns all children that have the specified namespace.
 *
 * @return All children that have the specified namespace
 */
- (OFArray OF_GENERIC(OFXMLElement *) *)elementsForNamespace:

Modified src/OFXMLElement.m from [804319c0a1] to [5df92b5be9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#include <stdlib.h>
#include <string.h>

#include <assert.h>

#import "OFXMLElement.h"
#import "OFXMLNode+Private.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFData.h"
#import "OFXMLAttribute.h"

#import "OFXMLCharacters.h"
#import "OFXMLCDATA.h"
#import "OFXMLParser.h"
#import "OFXMLElementBuilder.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFMalformedXMLException.h"
#import "OFUnboundNamespaceException.h"

/* References for static linking */
void
_references_to_categories_of_OFXMLElement(void)
{
	_OFXMLElement_Serialization_reference = 1;
}

static Class charactersClass = Nil;
static Class CDATAClass = Nil;

@interface OFXMLElementElementBuilderDelegate: OFObject
    <OFXMLElementBuilderDelegate>
{
@public
	OFXMLElement *_element;
}
@end







|
|
|
|
|

>

|
|
|













<
<
<







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

#include <stdlib.h>
#include <string.h>

#include <assert.h>

#import "OFXMLElement.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFStream.h"
#import "OFString.h"
#import "OFXMLAttribute.h"
#import "OFXMLCDATA.h"
#import "OFXMLCharacters.h"
#import "OFXMLElementBuilder.h"
#import "OFXMLNode+Private.h"
#import "OFXMLParser.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFMalformedXMLException.h"
#import "OFUnboundNamespaceException.h"

/* References for static linking */
void
_references_to_categories_of_OFXMLElement(void)
{
	_OFXMLElement_Serialization_reference = 1;
}




@interface OFXMLElementElementBuilderDelegate: OFObject
    <OFXMLElementBuilderDelegate>
{
@public
	OFXMLElement *_element;
}
@end
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
}
@end

@implementation OFXMLElement
@synthesize name = _name, namespace = _namespace;
@synthesize defaultNamespace = _defaultNamespace;

+ (void)initialize
{
	if (self == [OFXMLElement class]) {
		charactersClass = [OFXMLCharacters class];
		CDATAClass = [OFXMLCDATA class];
	}
}

+ (instancetype)elementWithName: (OFString *)name
{
	return [[[self alloc] initWithName: name] autorelease];
}

+ (instancetype)elementWithName: (OFString *)name
		    stringValue: (OFString *)stringValue







<
<
<
<
<
<
<
<







71
72
73
74
75
76
77








78
79
80
81
82
83
84
}
@end

@implementation OFXMLElement
@synthesize name = _name, namespace = _namespace;
@synthesize defaultNamespace = _defaultNamespace;









+ (instancetype)elementWithName: (OFString *)name
{
	return [[[self alloc] initWithName: name] autorelease];
}

+ (instancetype)elementWithName: (OFString *)name
		    stringValue: (OFString *)stringValue
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
}

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

#ifdef OF_HAVE_FILES
+ (instancetype)elementWithFile: (OFString *)path
{
	return [[[self alloc] initWithFile: path] autorelease];
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
{
	return [self initWithName: name
			namespace: nil
		      stringValue: nil];
}

- (instancetype)initWithName: (OFString *)name
		 stringValue: (OFString *)stringValue
{
	return [self initWithName: name
			namespace: nil
		      stringValue: stringValue];
}

- (instancetype)initWithName: (OFString *)name
		   namespace: (OFString *)namespace
{
	return [self initWithName: name
			namespace: namespace
		      stringValue: nil];
}

- (instancetype)initWithName: (OFString *)name
		   namespace: (OFString *)namespace
		 stringValue: (OFString *)stringValue
{
	self = [super of_init];







<
|

|

<








|
<
<













|
<
<







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
}

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


+ (instancetype)elementWithStream: (OFStream *)stream
{
	return [[[self alloc] initWithStream: stream] autorelease];
}


- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithName: (OFString *)name
{
	return [self initWithName: name namespace: nil stringValue: nil];


}

- (instancetype)initWithName: (OFString *)name
		 stringValue: (OFString *)stringValue
{
	return [self initWithName: name
			namespace: nil
		      stringValue: stringValue];
}

- (instancetype)initWithName: (OFString *)name
		   namespace: (OFString *)namespace
{
	return [self initWithName: name namespace: namespace stringValue: nil];


}

- (instancetype)initWithName: (OFString *)name
		   namespace: (OFString *)namespace
		 stringValue: (OFString *)stringValue
{
	self = [super of_init];
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

	if (string == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

	parser = [OFXMLParser parser];
	builder = [OFXMLElementBuilder elementBuilder];
	delegate = [[[OFXMLElementElementBuilderDelegate alloc] init]
	    autorelease];

	parser.delegate = builder;
	builder.delegate = delegate;

	[parser parseString: string];

	if (!parser.hasFinishedParsing)
		@throw [OFMalformedXMLException exceptionWithParser: parser];

	self = [delegate->_element retain];

	objc_autoreleasePoolPop(pool);

	return self;
}

#ifdef OF_HAVE_FILES
- (instancetype)initWithFile: (OFString *)path
{
	void *pool;
	OFXMLParser *parser;
	OFXMLElementBuilder *builder;
	OFXMLElementElementBuilderDelegate *delegate;

	[self release];

	pool = objc_autoreleasePoolPush();

	parser = [OFXMLParser parser];
	builder = [OFXMLElementBuilder elementBuilder];
	delegate = [[[OFXMLElementElementBuilderDelegate alloc] init]
	    autorelease];

	parser.delegate = builder;
	builder.delegate = delegate;

	[parser parseFile: path];

	if (!parser.hasFinishedParsing)
		@throw [OFMalformedXMLException exceptionWithParser: parser];

	self = [delegate->_element retain];

	objc_autoreleasePoolPop(pool);

	return self;
}
#endif

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFXMLElement *attributesElement, *namespacesElement;
		OFXMLElement *childrenElement;
		OFEnumerator *keyEnumerator, *objectEnumerator;
		OFString *key, *object;

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		_name = [[element attributeForName: @"name"].stringValue copy];
		_namespace = [[element attributeForName: @"namespace"]
		    .stringValue copy];
		_defaultNamespace = [[element attributeForName:
		    @"defaultNamespace"].stringValue copy];

		attributesElement = [[element
		    elementForName: @"attributes"
			 namespace: OF_SERIALIZATION_NS] elementsForNamespace:
		    OF_SERIALIZATION_NS].firstObject;
		namespacesElement = [[element
		    elementForName: @"namespaces"
			 namespace: OF_SERIALIZATION_NS] elementsForNamespace:
		    OF_SERIALIZATION_NS].firstObject;
		childrenElement = [[element
		    elementForName: @"children"
			 namespace: OF_SERIALIZATION_NS] elementsForNamespace:
		    OF_SERIALIZATION_NS].firstObject;

		_attributes = [attributesElement.objectByDeserializing
		    mutableCopy];
		_namespaces = [namespacesElement.objectByDeserializing
		    mutableCopy];
		_children = [childrenElement.objectByDeserializing
		    mutableCopy];







|


















<
|











|






|










<













|










|
|


|
|


|
|







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

	if (string == nil)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();

	parser = [OFXMLParser parser];
	builder = [OFXMLElementBuilder builder];
	delegate = [[[OFXMLElementElementBuilderDelegate alloc] init]
	    autorelease];

	parser.delegate = builder;
	builder.delegate = delegate;

	[parser parseString: string];

	if (!parser.hasFinishedParsing)
		@throw [OFMalformedXMLException exceptionWithParser: parser];

	self = [delegate->_element retain];

	objc_autoreleasePoolPop(pool);

	return self;
}


- (instancetype)initWithStream: (OFStream *)stream
{
	void *pool;
	OFXMLParser *parser;
	OFXMLElementBuilder *builder;
	OFXMLElementElementBuilderDelegate *delegate;

	[self release];

	pool = objc_autoreleasePoolPush();

	parser = [OFXMLParser parser];
	builder = [OFXMLElementBuilder builder];
	delegate = [[[OFXMLElementElementBuilderDelegate alloc] init]
	    autorelease];

	parser.delegate = builder;
	builder.delegate = delegate;

	[parser parseStream: stream];

	if (!parser.hasFinishedParsing)
		@throw [OFMalformedXMLException exceptionWithParser: parser];

	self = [delegate->_element retain];

	objc_autoreleasePoolPop(pool);

	return self;
}


- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFXMLElement *attributesElement, *namespacesElement;
		OFXMLElement *childrenElement;
		OFEnumerator *keyEnumerator, *objectEnumerator;
		OFString *key, *object;

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		_name = [[element attributeForName: @"name"].stringValue copy];
		_namespace = [[element attributeForName: @"namespace"]
		    .stringValue copy];
		_defaultNamespace = [[element attributeForName:
		    @"defaultNamespace"].stringValue copy];

		attributesElement = [[element
		    elementForName: @"attributes"
			 namespace: OFSerializationNS] elementsForNamespace:
		    OFSerializationNS].firstObject;
		namespacesElement = [[element
		    elementForName: @"namespaces"
			 namespace: OFSerializationNS] elementsForNamespace:
		    OFSerializationNS].firstObject;
		childrenElement = [[element
		    elementForName: @"children"
			 namespace: OFSerializationNS] elementsForNamespace:
		    OFSerializationNS].firstObject;

		_attributes = [attributesElement.objectByDeserializing
		    mutableCopy];
		_namespaces = [namespacesElement.objectByDeserializing
		    mutableCopy];
		_children = [childrenElement.objectByDeserializing
		    mutableCopy];
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

	[ret makeImmutable];

	return ret;
}

- (OFString *)of_XMLStringWithParent: (OFXMLElement *)parent
			  namespaces: (OFDictionary *)allNamespaces
			 indentation: (unsigned int)indentation
			       level: (unsigned int)level OF_DIRECT
{
	void *pool;
	char *cString;
	size_t length, i;
	OFString *prefix, *parentPrefix;
	OFString *ret;
	OFString *defaultNS;

	pool = objc_autoreleasePoolPush();

	parentPrefix = [allNamespaces objectForKey:
	    (parent != nil && parent->_namespace != nil
	    ? parent->_namespace : (OFString *)@"")];

	/* Add the namespaces of the current element */
	if (allNamespaces != nil) {
		OFEnumerator *keyEnumerator = [_namespaces keyEnumerator];
		OFEnumerator *objectEnumerator = [_namespaces objectEnumerator];
		OFMutableDictionary *tmp;
		OFString *key, *object;

		tmp = [[allNamespaces mutableCopy] autorelease];

		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil)
			[tmp setObject: object
				forKey: key];

		allNamespaces = tmp;
	} else
		allNamespaces = _namespaces;

	prefix = [allNamespaces objectForKey:
	    (_namespace != nil ? _namespace : (OFString *)@"")];

	if (parent != nil && parent->_namespace != nil && parentPrefix == nil)
		defaultNS = parent->_namespace;
	else if (parent != nil && parent->_defaultNamespace != nil)
		defaultNS = parent->_defaultNamespace;
	else
		defaultNS = _defaultNamespace;

	i = 0;
	length = _name.UTF8StringLength + 3 + (level * indentation);
	cString = [self allocMemoryWithSize: length];


	memset(cString + i, ' ', level * indentation);
	i += level * indentation;

	/* Start of tag */
	cString[i++] = '<';

	if (prefix != nil && ![_namespace isEqual: defaultNS]) {
		length += prefix.UTF8StringLength + 1;
		@try {
			cString = [self resizeMemory: cString
						size: length];
		} @catch (id e) {
			[self freeMemory: cString];
			@throw e;
		}

		memcpy(cString + i, prefix.UTF8String, prefix.UTF8StringLength);
		i += prefix.UTF8StringLength;
		cString[i++] = ':';
	}

	memcpy(cString + i, _name.UTF8String, _name.UTF8StringLength);
	i += _name.UTF8StringLength;

	/* xmlns if necessary */
	if (prefix == nil && ((_namespace != nil &&
	    ![_namespace isEqual: defaultNS]) ||
	    (_namespace == nil && defaultNS != nil))) {
		length += _namespace.UTF8StringLength + 9;
		@try {
			cString = [self resizeMemory: cString
						size: length];
		} @catch (id e) {
			[self freeMemory: cString];
			@throw e;
		}

		memcpy(cString + i, " xmlns='", 8);
		i += 8;
		memcpy(cString + i, _namespace.UTF8String,
		    _namespace.UTF8StringLength);
		i += _namespace.UTF8StringLength;
		cString[i++] = '\'';
	}

	/* Attributes */
	for (OFXMLAttribute *attribute in _attributes) {
		void *pool2 = objc_autoreleasePoolPush();
		const char *attributeNameCString = attribute->_name.UTF8String;
		size_t attributeNameLength = attribute->_name.UTF8StringLength;
		OFString *attributePrefix = nil;
		OFString *tmp = attribute.stringValue.stringByXMLEscaping;
		char delimiter = (attribute->_useDoubleQuotes ? '"' : '\'');

		if (attribute->_namespace != nil &&
		    (attributePrefix = [allNamespaces objectForKey:
		    attribute->_namespace]) == nil)
			@throw [OFUnboundNamespaceException
			    exceptionWithNamespace: [attribute namespace]
					   element: self];

		length += attributeNameLength + (attributePrefix != nil
		    ? attributePrefix.UTF8StringLength + 1 : 0) +
		    tmp.UTF8StringLength + 4;

		@try {
			cString = [self resizeMemory: cString
						size: length];
		} @catch (id e) {
			[self freeMemory: cString];
			@throw e;
		}

		cString[i++] = ' ';
		if (attributePrefix != nil) {
			memcpy(cString + i, attributePrefix.UTF8String,
			    attributePrefix.UTF8StringLength);
			i += attributePrefix.UTF8StringLength;
			cString[i++] = ':';
		}
		memcpy(cString + i, attributeNameCString, attributeNameLength);
		i += attributeNameLength;
		cString[i++] = '=';
		cString[i++] = delimiter;
		memcpy(cString + i, tmp.UTF8String, tmp.UTF8StringLength);
		i += tmp.UTF8StringLength;
		cString[i++] = delimiter;

		objc_autoreleasePoolPop(pool2);
	}

	/* Children */
	if (_children != nil) {
		OFMutableData *tmp = [OFMutableData data];
		bool indent;

		if (indentation > 0) {
			indent = true;

			for (OFXMLNode *child in _children) {
				if ([child isKindOfClass: charactersClass] ||
				    [child isKindOfClass: CDATAClass]) {
					indent = false;
					break;
				}
			}
		} else
			indent = false;

		for (OFXMLNode *child in _children) {
			OFString *childString;
			unsigned int ind = (indent ? indentation : 0);

			if (ind)
				[tmp addItem: "\n"];

			if ([child isKindOfClass: [OFXMLElement class]])
				childString = [(OFXMLElement *)child
				    of_XMLStringWithParent: self
						namespaces: allNamespaces
					       indentation: ind
						     level: level + 1];
			else
				childString = [child
				    XMLStringWithIndentation: ind
						       level: level + 1];

			[tmp addItems: childString.UTF8String
				count: childString.UTF8StringLength];
		}

		if (indent)
			[tmp addItem: "\n"];

		length += tmp.count + _name.UTF8StringLength + 2 +
		    (indent ? level * indentation : 0);
		@try {
			cString = [self resizeMemory: cString
						size: length];
		} @catch (id e) {
			[self freeMemory: cString];
			@throw e;
		}

		cString[i++] = '>';

		memcpy(cString + i, tmp.items, tmp.count);
		i += tmp.count;

		if (indent) {
			memset(cString + i, ' ', level * indentation);
			i += level * indentation;
		}

		cString[i++] = '<';
		cString[i++] = '/';
		if (prefix != nil) {
			length += prefix.UTF8StringLength + 1;
			@try {
				cString = [self resizeMemory: cString
							size: length];
			} @catch (id e) {
				[self freeMemory: cString];
				@throw e;
			}

			memcpy(cString + i, prefix.UTF8String,
			    prefix.UTF8StringLength);
			i += prefix.UTF8StringLength;
			cString[i++] = ':';
		}

		memcpy(cString + i, _name.UTF8String, _name.UTF8StringLength);
		i += _name.UTF8StringLength;







































































































































	} else
		cString[i++] = '/';

	cString[i++] = '>';
	assert(i == length);

	objc_autoreleasePoolPop(pool);

	@try {
		ret = [OFString stringWithUTF8String: cString
					      length: length];
	} @finally {
		[self freeMemory: cString];
	}
	return ret;
}

- (OFString *)XMLString
{
	return [self of_XMLStringWithParent: nil







|












|




|





|



|
<

|

|

|











|

>
|
|

|
|

|
|
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|

|
|

|

<



|







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

	[ret makeImmutable];

	return ret;
}

- (OFString *)of_XMLStringWithParent: (OFXMLElement *)parent
			  namespaces: (OFDictionary *)allNS
			 indentation: (unsigned int)indentation
			       level: (unsigned int)level OF_DIRECT
{
	void *pool;
	char *cString;
	size_t length, i;
	OFString *prefix, *parentPrefix;
	OFString *ret;
	OFString *defaultNS;

	pool = objc_autoreleasePoolPush();

	parentPrefix = [allNS objectForKey:
	    (parent != nil && parent->_namespace != nil
	    ? parent->_namespace : (OFString *)@"")];

	/* Add the namespaces of the current element */
	if (allNS != nil) {
		OFEnumerator *keyEnumerator = [_namespaces keyEnumerator];
		OFEnumerator *objectEnumerator = [_namespaces objectEnumerator];
		OFMutableDictionary *tmp;
		OFString *key, *object;

		tmp = [[allNS mutableCopy] autorelease];

		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil)
			[tmp setObject: object forKey: key];


		allNS = tmp;
	} else
		allNS = _namespaces;

	prefix = [allNS objectForKey:
	    (_namespace != nil ? _namespace : (OFString *)@"")];

	if (parent != nil && parent->_namespace != nil && parentPrefix == nil)
		defaultNS = parent->_namespace;
	else if (parent != nil && parent->_defaultNamespace != nil)
		defaultNS = parent->_defaultNamespace;
	else
		defaultNS = _defaultNamespace;

	i = 0;
	length = _name.UTF8StringLength + 3 + (level * indentation);
	cString = OFAllocMemory(length, 1);

	@try {
		memset(cString + i, ' ', level * indentation);
		i += level * indentation;

		/* Start of tag */
		cString[i++] = '<';

		if (prefix != nil && ![_namespace isEqual: defaultNS]) {
			length += prefix.UTF8StringLength + 1;







			cString = OFResizeMemory(cString, length, 1);






















































































































































			memcpy(cString + i, prefix.UTF8String,
			    prefix.UTF8StringLength);
			i += prefix.UTF8StringLength;
			cString[i++] = ':';
		}

		memcpy(cString + i, _name.UTF8String, _name.UTF8StringLength);
		i += _name.UTF8StringLength;

		/* xmlns if necessary */
		if (prefix == nil && ((_namespace != nil &&
		    ![_namespace isEqual: defaultNS]) ||
		    (_namespace == nil && defaultNS != nil))) {
			length += _namespace.UTF8StringLength + 9;
			cString = OFResizeMemory(cString, length, 1);

			memcpy(cString + i, " xmlns='", 8);
			i += 8;
			memcpy(cString + i, _namespace.UTF8String,
			    _namespace.UTF8StringLength);
			i += _namespace.UTF8StringLength;
			cString[i++] = '\'';
		}

		/* Attributes */
		for (OFXMLAttribute *attribute in _attributes) {
			void *pool2 = objc_autoreleasePoolPush();
			const char *attributeNameCString =
			    attribute->_name.UTF8String;
			size_t attributeNameLength =
			    attribute->_name.UTF8StringLength;
			OFString *attributePrefix = nil;
			OFString *tmp =
			    attribute.stringValue.stringByXMLEscaping;
			char delimiter = (attribute->_useDoubleQuotes
			    ? '"' : '\'');

			if (attribute->_namespace != nil &&
			    (attributePrefix = [allNS objectForKey:
			    attribute->_namespace]) == nil)
				@throw [OFUnboundNamespaceException
				    exceptionWithNamespace: attribute.namespace
						   element: self];

			length += attributeNameLength + (attributePrefix != nil
			    ? attributePrefix.UTF8StringLength + 1 : 0) +
			    tmp.UTF8StringLength + 4;
			cString = OFResizeMemory(cString, length, 1);

			cString[i++] = ' ';
			if (attributePrefix != nil) {
				memcpy(cString + i, attributePrefix.UTF8String,
				    attributePrefix.UTF8StringLength);
				i += attributePrefix.UTF8StringLength;
				cString[i++] = ':';
			}
			memcpy(cString + i, attributeNameCString,
			    attributeNameLength);
			i += attributeNameLength;
			cString[i++] = '=';
			cString[i++] = delimiter;
			memcpy(cString + i, tmp.UTF8String,
			    tmp.UTF8StringLength);
			i += tmp.UTF8StringLength;
			cString[i++] = delimiter;

			objc_autoreleasePoolPop(pool2);
		}

		/* Children */
		if (_children != nil) {
			OFMutableData *tmp = [OFMutableData data];
			bool indent;

			if (indentation > 0) {
				indent = true;

				for (OFXMLNode *child in _children) {
					if ([child isKindOfClass:
					    [OFXMLCharacters class]] ||
					    [child isKindOfClass:
					    [OFXMLCDATA class]]) {
						indent = false;
						break;
					}
				}
			} else
				indent = false;

			for (OFXMLNode *child in _children) {
				OFString *childString;
				unsigned int ind = (indent ? indentation : 0);

				if (ind)
					[tmp addItem: "\n"];

				if ([child isKindOfClass: [OFXMLElement class]])
					childString = [(OFXMLElement *)child
					    of_XMLStringWithParent: self
							namespaces: allNS
						       indentation: ind
							     level: level + 1];
				else
					childString = [child
					    XMLStringWithIndentation: ind
							       level: level +
								      1];

				[tmp addItems: childString.UTF8String
					count: childString.UTF8StringLength];
			}

			if (indent)
				[tmp addItem: "\n"];

			length += tmp.count + _name.UTF8StringLength + 2 +
			    (indent ? level * indentation : 0);
			cString = OFResizeMemory(cString, length, 1);

			cString[i++] = '>';

			memcpy(cString + i, tmp.items, tmp.count);
			i += tmp.count;

			if (indent) {
				memset(cString + i, ' ', level * indentation);
				i += level * indentation;
			}

			cString[i++] = '<';
			cString[i++] = '/';
			if (prefix != nil) {
				length += prefix.UTF8StringLength + 1;
				cString = OFResizeMemory(cString, length, 1);

				memcpy(cString + i, prefix.UTF8String,
				    prefix.UTF8StringLength);
				i += prefix.UTF8StringLength;
				cString[i++] = ':';
			}
			memcpy(cString + i, _name.UTF8String,
			    _name.UTF8StringLength);
			i += _name.UTF8StringLength;
		} else
			cString[i++] = '/';

		cString[i++] = '>';
		assert(i == length);

		objc_autoreleasePoolPop(pool);


		ret = [OFString stringWithUTF8String: cString
					      length: length];
	} @finally {
		OFFreeMemory(cString);
	}
	return ret;
}

- (OFString *)XMLString
{
	return [self of_XMLStringWithParent: nil
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

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

	element = [OFXMLElement elementWithName: self.className
				      namespace: OF_SERIALIZATION_NS];

	if (_name != nil)
		[element addAttributeWithName: @"name"
				  stringValue: _name];

	if (_namespace != nil)
		[element addAttributeWithName: @"namespace"
				  stringValue: _namespace];

	if (_defaultNamespace != nil)
		[element addAttributeWithName: @"defaultNamespace"
				  stringValue: _defaultNamespace];

	if (_attributes != nil) {
		OFXMLElement *attributesElement;

		attributesElement =
		    [OFXMLElement elementWithName: @"attributes"
					namespace: OF_SERIALIZATION_NS];
		[attributesElement addChild:
		    _attributes.XMLElementBySerializing];
		[element addChild: attributesElement];
	}

	if (_namespaces != nil) {
		OFXMLElement *namespacesElement;
		OFMutableDictionary *namespacesCopy =
		    [[_namespaces mutableCopy] autorelease];

		[namespacesCopy removeObjectForKey:
		    @"http://www.w3.org/XML/1998/namespace"];
		[namespacesCopy removeObjectForKey:
		    @"http://www.w3.org/2000/xmlns/"];

		if (namespacesCopy.count > 0) {
			namespacesElement =
			    [OFXMLElement elementWithName: @"namespaces"
						namespace: OF_SERIALIZATION_NS];
			[namespacesElement addChild:
			    namespacesCopy.XMLElementBySerializing];
			[element addChild: namespacesElement];
		}
	}

	if (_children != nil) {
		OFXMLElement *childrenElement;

		childrenElement =
		    [OFXMLElement elementWithName: @"children"
					namespace: OF_SERIALIZATION_NS];
		[childrenElement addChild: _children.XMLElementBySerializing];
		[element addChild: childrenElement];
	}

	[element retain];

	objc_autoreleasePoolPop(pool);







|


|
<














|


















|











|







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

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

	element = [OFXMLElement elementWithName: self.className
				      namespace: OFSerializationNS];

	if (_name != nil)
		[element addAttributeWithName: @"name" stringValue: _name];


	if (_namespace != nil)
		[element addAttributeWithName: @"namespace"
				  stringValue: _namespace];

	if (_defaultNamespace != nil)
		[element addAttributeWithName: @"defaultNamespace"
				  stringValue: _defaultNamespace];

	if (_attributes != nil) {
		OFXMLElement *attributesElement;

		attributesElement =
		    [OFXMLElement elementWithName: @"attributes"
					namespace: OFSerializationNS];
		[attributesElement addChild:
		    _attributes.XMLElementBySerializing];
		[element addChild: attributesElement];
	}

	if (_namespaces != nil) {
		OFXMLElement *namespacesElement;
		OFMutableDictionary *namespacesCopy =
		    [[_namespaces mutableCopy] autorelease];

		[namespacesCopy removeObjectForKey:
		    @"http://www.w3.org/XML/1998/namespace"];
		[namespacesCopy removeObjectForKey:
		    @"http://www.w3.org/2000/xmlns/"];

		if (namespacesCopy.count > 0) {
			namespacesElement =
			    [OFXMLElement elementWithName: @"namespaces"
						namespace: OFSerializationNS];
			[namespacesElement addChild:
			    namespacesCopy.XMLElementBySerializing];
			[element addChild: namespacesElement];
		}
	}

	if (_children != nil) {
		OFXMLElement *childrenElement;

		childrenElement =
		    [OFXMLElement elementWithName: @"children"
					namespace: OFSerializationNS];
		[childrenElement addChild: _children.XMLElementBySerializing];
		[element addChild: childrenElement];
	}

	[element retain];

	objc_autoreleasePoolPop(pool);
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
		    [objects[i]->_name isEqual: attributeName]) {
			[_attributes removeObjectAtIndex: i];
				return;
		}
	}
}

- (void)setPrefix: (OFString *)prefix
     forNamespace: (OFString *)namespace
{
	if (prefix.length == 0)
		@throw [OFInvalidArgumentException exception];
	if (namespace == nil)
		namespace = @"";

	[_namespaces setObject: prefix
			forKey: namespace];
}

- (void)bindPrefix: (OFString *)prefix
      forNamespace: (OFString *)namespace
{
	[self setPrefix: prefix
	   forNamespace: namespace];
	[self addAttributeWithName: prefix
			 namespace: @"http://www.w3.org/2000/xmlns/"
		       stringValue: namespace];
}

- (void)addChild: (OFXMLNode *)child
{
	if ([child isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	if (_children == nil)
		_children = [[OFMutableArray alloc] init];

	[_children addObject: child];
}

- (void)insertChild: (OFXMLNode *)child
	    atIndex: (size_t)idx
{
	if ([child isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	if (_children == nil)
		_children = [[OFMutableArray alloc] init];

	[_children insertObject: child
			atIndex: idx];
}

- (void)insertChildren: (OFArray *)children
	       atIndex: (size_t)idx
{
	for (OFXMLNode *node in children)
		if ([node isKindOfClass: [OFXMLAttribute class]])
			@throw [OFInvalidArgumentException exception];

	[_children insertObjectsFromArray: children
				  atIndex: idx];
}

- (void)removeChild: (OFXMLNode *)child
{
	if ([child isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	[_children removeObject: child];
}

- (void)removeChildAtIndex: (size_t)idx
{
	[_children removeObjectAtIndex: idx];
}

- (void)replaceChild: (OFXMLNode *)child
	    withNode: (OFXMLNode *)node
{
	if ([node isKindOfClass: [OFXMLAttribute class]] ||
	    [child isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	[_children replaceObject: child
		      withObject: node];
}

- (void)replaceChildAtIndex: (size_t)idx
		   withNode: (OFXMLNode *)node
{
	if ([node isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	[_children replaceObjectAtIndex: idx
			     withObject: node];
}

- (OFXMLElement *)elementForName: (OFString *)elementName
{
	return [self elementsForName: elementName].firstObject;
}








|
<






|
<


|
<

|
<
















|
<







|
<


|
<





|
<















|
<





|
<


|
<




|
<







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
		    [objects[i]->_name isEqual: attributeName]) {
			[_attributes removeObjectAtIndex: i];
				return;
		}
	}
}

- (void)setPrefix: (OFString *)prefix forNamespace: (OFString *)namespace

{
	if (prefix.length == 0)
		@throw [OFInvalidArgumentException exception];
	if (namespace == nil)
		namespace = @"";

	[_namespaces setObject: prefix forKey: namespace];

}

- (void)bindPrefix: (OFString *)prefix forNamespace: (OFString *)namespace

{
	[self setPrefix: prefix forNamespace: namespace];

	[self addAttributeWithName: prefix
			 namespace: @"http://www.w3.org/2000/xmlns/"
		       stringValue: namespace];
}

- (void)addChild: (OFXMLNode *)child
{
	if ([child isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	if (_children == nil)
		_children = [[OFMutableArray alloc] init];

	[_children addObject: child];
}

- (void)insertChild: (OFXMLNode *)child atIndex: (size_t)idx

{
	if ([child isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	if (_children == nil)
		_children = [[OFMutableArray alloc] init];

	[_children insertObject: child atIndex: idx];

}

- (void)insertChildren: (OFArray *)children atIndex: (size_t)idx

{
	for (OFXMLNode *node in children)
		if ([node isKindOfClass: [OFXMLAttribute class]])
			@throw [OFInvalidArgumentException exception];

	[_children insertObjectsFromArray: children atIndex: idx];

}

- (void)removeChild: (OFXMLNode *)child
{
	if ([child isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	[_children removeObject: child];
}

- (void)removeChildAtIndex: (size_t)idx
{
	[_children removeObjectAtIndex: idx];
}

- (void)replaceChild: (OFXMLNode *)child withNode: (OFXMLNode *)node

{
	if ([node isKindOfClass: [OFXMLAttribute class]] ||
	    [child isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	[_children replaceObject: child withObject: node];

}

- (void)replaceChildAtIndex: (size_t)idx withNode: (OFXMLNode *)node

{
	if ([node isKindOfClass: [OFXMLAttribute class]])
		@throw [OFInvalidArgumentException exception];

	[_children replaceObjectAtIndex: idx withObject: node];

}

- (OFXMLElement *)elementForName: (OFString *)elementName
{
	return [self elementsForName: elementName].firstObject;
}

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
		return false;

	return true;
}

- (unsigned long)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD_HASH(hash, _namespace.hash);
	OF_HASH_ADD_HASH(hash, _defaultNamespace.hash);
	OF_HASH_ADD_HASH(hash, _attributes.hash);
	OF_HASH_ADD_HASH(hash, _namespaces.hash);
	OF_HASH_ADD_HASH(hash, _children.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
	return [[[self class] alloc] initWithElement: self];
}
@end







|

|

|
|
|
|
|
|

|









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
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);

	OFHashAddHash(&hash, _name.hash);
	OFHashAddHash(&hash, _namespace.hash);
	OFHashAddHash(&hash, _defaultNamespace.hash);
	OFHashAddHash(&hash, _attributes.hash);
	OFHashAddHash(&hash, _namespaces.hash);
	OFHashAddHash(&hash, _children.hash);

	OFHashFinalize(&hash);

	return hash;
}

- (id)copy
{
	return [[[self class] alloc] initWithElement: self];
}
@end

Modified src/OFXMLElementBuilder.h from [6a04a51f54] to [657f65f42e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 *
 * If this method is not implemented in the delegate, the default is to throw
 * an OFMalformedXMLException.
 *
 * @param builder The builder which did not expect the close tag
 * @param name The name of the close tag
 * @param prefix The prefix of the close tag
 * @param namespace_ The namespace of the close tag
 */
- (void)elementBuilder: (OFXMLElementBuilder *)builder
  didNotExpectCloseTag: (OFString *)name
		prefix: (nullable OFString *)prefix
	     namespace: (nullable OFString *)namespace_;

/**
 * @brief This callback is called when the XML parser for the element builder
 *	  found an unknown entity.
 *
 * @param builder The element builder which found an unknown entity
 * @param entity The name of the entity







|




|







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
 *
 * If this method is not implemented in the delegate, the default is to throw
 * an OFMalformedXMLException.
 *
 * @param builder The builder which did not expect the close tag
 * @param name The name of the close tag
 * @param prefix The prefix of the close tag
 * @param nameSpace The namespace of the close tag
 */
- (void)elementBuilder: (OFXMLElementBuilder *)builder
  didNotExpectCloseTag: (OFString *)name
		prefix: (nullable OFString *)prefix
	     namespace: (nullable OFString *)nameSpace;

/**
 * @brief This callback is called when the XML parser for the element builder
 *	  found an unknown entity.
 *
 * @param builder The element builder which found an unknown entity
 * @param entity The name of the entity
118
119
120
121
122
123
124
125
126
127
128
    id <OFXMLElementBuilderDelegate> delegate;

/**
 * @brief Creates a new element builder.
 *
 * @return A new, autoreleased OFXMLElementBuilder
 */
+ (instancetype)elementBuilder;
@end

OF_ASSUME_NONNULL_END







|



116
117
118
119
120
121
122
123
124
125
126
    id <OFXMLElementBuilderDelegate> delegate;

/**
 * @brief Creates a new element builder.
 *
 * @return A new, autoreleased OFXMLElementBuilder
 */
+ (instancetype)builder;
@end

OF_ASSUME_NONNULL_END

Modified src/OFXMLElementBuilder.m from [8b8caf6ff6] to [b8984275c9].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFXMLElementBuilder.h"
#import "OFXMLElement.h"
#import "OFXMLAttribute.h"
#import "OFXMLCharacters.h"
#import "OFXMLCDATA.h"
#import "OFXMLComment.h"
#import "OFXMLProcessingInstructions.h"
#import "OFXMLParser.h"
#import "OFArray.h"

#import "OFMalformedXMLException.h"

@implementation OFXMLElementBuilder
@synthesize delegate = _delegate;

+ (instancetype)elementBuilder
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

<
<
|
















|

|
|

|

|






|







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


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

#import "OFXMLElementBuilder.h"
#import "OFArray.h"
#import "OFXMLAttribute.h"
#import "OFXMLCDATA.h"
#import "OFXMLCharacters.h"
#import "OFXMLComment.h"
#import "OFXMLElement.h"
#import "OFXMLParser.h"
#import "OFXMLProcessingInstruction.h"

#import "OFMalformedXMLException.h"

@implementation OFXMLElementBuilder
@synthesize delegate = _delegate;

+ (instancetype)builder
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];
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
- (void)dealloc
{
	[_stack release];

	[super dealloc];
}

-		 (void)parser: (OFXMLParser *)parser
  foundProcessingInstructions: (OFString *)pi

{
	OFXMLProcessingInstructions *node = [OFXMLProcessingInstructions
	    processingInstructionsWithString: pi];

	OFXMLElement *parent = _stack.lastObject;

	if (parent != nil)
		[parent addChild: node];
	else if ([_delegate respondsToSelector:
	    @selector(elementBuilder:didBuildParentlessNode:)])
		[_delegate elementBuilder: self
		   didBuildParentlessNode: node];
}

-    (void)parser: (OFXMLParser *)parser
  didStartElement: (OFString *)name
	   prefix: (OFString *)prefix
	namespace: (OFString *)namespace
       attributes: (OFArray *)attributes







|
|
>

|
|
>






|
<







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
- (void)dealloc
{
	[_stack release];

	[super dealloc];
}

-			  (void)parser: (OFXMLParser *)parser
  foundProcessingInstructionWithTarget: (OFString *)target
				  data: (OFString *)data
{
	OFXMLProcessingInstruction *node = [OFXMLProcessingInstruction
	    processingInstructionWithTarget: target
				       data: data];
	OFXMLElement *parent = _stack.lastObject;

	if (parent != nil)
		[parent addChild: node];
	else if ([_delegate respondsToSelector:
	    @selector(elementBuilder:didBuildParentlessNode:)])
		[_delegate elementBuilder: self didBuildParentlessNode: node];

}

-    (void)parser: (OFXMLParser *)parser
  didStartElement: (OFString *)name
	   prefix: (OFString *)prefix
	namespace: (OFString *)namespace
       attributes: (OFArray *)attributes
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
	node = [OFXMLCharacters charactersWithString: characters];
	parent = _stack.lastObject;

	if (parent != nil)
		[parent addChild: node];
	else if ([_delegate respondsToSelector:
	    @selector(elementBuilder:didBuildParentlessNode:)])
		[_delegate  elementBuilder: self
		    didBuildParentlessNode: node];
}

- (void)parser: (OFXMLParser *)parser
    foundCDATA: (OFString *)CDATA
{
	OFXMLCDATA *node = [OFXMLCDATA CDATAWithString: CDATA];
	OFXMLElement *parent = _stack.lastObject;

	if (parent != nil)
		[parent addChild: node];
	else if ([_delegate respondsToSelector:
	    @selector(elementBuilder:didBuildParentlessNode:)])
		[_delegate elementBuilder: self
		   didBuildParentlessNode: node];
}

- (void)parser: (OFXMLParser *)parser
  foundComment: (OFString *)comment
{
	OFXMLComment *node = [OFXMLComment commentWithString: comment];
	OFXMLElement *parent = _stack.lastObject;

	if (parent != nil)
		[parent addChild: node];
	else if ([_delegate respondsToSelector:
	    @selector(elementBuilder:didBuildParentlessNode:)])
		[_delegate elementBuilder: self
		   didBuildParentlessNode: node];
}

-      (OFString *)parser: (OFXMLParser *)parser
  foundUnknownEntityNamed: (OFString *)entity
{
	if ([_delegate respondsToSelector:
	    @selector(elementBuilder:foundUnknownEntityNamed:)])
		return [_delegate elementBuilder: self
			 foundUnknownEntityNamed: entity];

	return nil;
}
@end







|
<












|
<





|






|
<













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
	node = [OFXMLCharacters charactersWithString: characters];
	parent = _stack.lastObject;

	if (parent != nil)
		[parent addChild: node];
	else if ([_delegate respondsToSelector:
	    @selector(elementBuilder:didBuildParentlessNode:)])
		[_delegate  elementBuilder: self didBuildParentlessNode: node];

}

- (void)parser: (OFXMLParser *)parser
    foundCDATA: (OFString *)CDATA
{
	OFXMLCDATA *node = [OFXMLCDATA CDATAWithString: CDATA];
	OFXMLElement *parent = _stack.lastObject;

	if (parent != nil)
		[parent addChild: node];
	else if ([_delegate respondsToSelector:
	    @selector(elementBuilder:didBuildParentlessNode:)])
		[_delegate elementBuilder: self didBuildParentlessNode: node];

}

- (void)parser: (OFXMLParser *)parser
  foundComment: (OFString *)comment
{
	OFXMLComment *node = [OFXMLComment commentWithText: comment];
	OFXMLElement *parent = _stack.lastObject;

	if (parent != nil)
		[parent addChild: node];
	else if ([_delegate respondsToSelector:
	    @selector(elementBuilder:didBuildParentlessNode:)])
		[_delegate elementBuilder: self didBuildParentlessNode: node];

}

-      (OFString *)parser: (OFXMLParser *)parser
  foundUnknownEntityNamed: (OFString *)entity
{
	if ([_delegate respondsToSelector:
	    @selector(elementBuilder:foundUnknownEntityNamed:)])
		return [_delegate elementBuilder: self
			 foundUnknownEntityNamed: entity];

	return nil;
}
@end

Modified src/OFXMLNode+Private.h from [19f5e39b7a] to [fcadbb17a0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFXMLNode.h from [ec230c5902] to [506d741d52].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFXMLNode.m from [089c365f8a] to [fd3b7789fd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
- (double)doubleValue
{
	return self.stringValue.doubleValue;
}

- (OFString *)XMLString
{
	return [self XMLStringWithIndentation: 0
					level: 0];
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
	return [self XMLStringWithIndentation: 0
					level: 0];
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
				 level: (unsigned int)level
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFString *)description
{
	return [self XMLStringWithIndentation: 2];
}

- (OFXMLElement *)XMLElementBySerializing
{
	OF_UNRECOGNIZED_SELECTOR
}

- (id)copy
{
	return [self retain];
}
@end







|
<




|
<










|












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
- (double)doubleValue
{
	return self.stringValue.doubleValue;
}

- (OFString *)XMLString
{
	return [self XMLStringWithIndentation: 0 level: 0];

}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
	return [self XMLStringWithIndentation: 0 level: 0];

}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
				 level: (unsigned int)level
{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFString *)description
{
	return [self XMLStringWithIndentation: 2 level: 0];
}

- (OFXMLElement *)XMLElementBySerializing
{
	OF_UNRECOGNIZED_SELECTOR
}

- (id)copy
{
	return [self retain];
}
@end

Modified src/OFXMLParser.h from [9632092363] to [e4ba7bdb79].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46

47
48
49
50
51
52
53
 * @protocol OFXMLParserDelegate OFXMLParser.h ObjFW/OFXMLParser.h
 *
 * @brief A protocol that needs to be implemented by delegates for OFXMLParser.
 */
@protocol OFXMLParserDelegate <OFObject>
@optional
/**
 * @brief This callback is called when the XML parser found processing
 *	  instructions.
 *
 * @param parser The parser which found processing instructions

 * @param processingInstructions The processing instructions
 */
-		 (void)parser: (OFXMLParser *)parser
  foundProcessingInstructions: (OFString *)processingInstructions;


/**
 * @brief This callback is called when the XML parser found the start of a new
 *	  tag.
 *
 * @param parser The parser which found a new tag
 * @param name The name of the tag which just started







|
|

|
>
|

|
|
>







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 * @protocol OFXMLParserDelegate OFXMLParser.h ObjFW/OFXMLParser.h
 *
 * @brief A protocol that needs to be implemented by delegates for OFXMLParser.
 */
@protocol OFXMLParserDelegate <OFObject>
@optional
/**
 * @brief This callback is called when the XML parser found a processing
 *	  instruction.
 *
 * @param parser The parser which found a processing instruction
 * @param target The target of the processing instruction
 * @param data The data of the processing instruction
 */
-			  (void)parser: (OFXMLParser *)parser
  foundProcessingInstructionWithTarget: (OFString *)target
				  data: (OFString *)data;

/**
 * @brief This callback is called when the XML parser found the start of a new
 *	  tag.
 *
 * @param parser The parser which found a new tag
 * @param name The name of the tag which just started
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
 *
 * In case there are comments or CDATA, it is possible that this callback is
 * called multiple times in a row.
 *
 * @param parser The parser which found a string
 * @param characters The characters the XML parser found
 */
-    (void)parser: (OFXMLParser *)parser
  foundCharacters: (OFString *)characters;

/**
 * @brief This callback is called when the XML parser found CDATA.
 *
 * @param parser The parser which found a string
 * @param CDATA The CDATA the XML parser found
 */
- (void)parser: (OFXMLParser *)parser
    foundCDATA: (OFString *)CDATA;

/**
 * @brief This callback is called when the XML parser found a comment.
 *
 * @param parser The parser which found a comment
 * @param comment The comment the XML parser found
 */
- (void)parser: (OFXMLParser *)parser
  foundComment: (OFString *)comment;

/**
 * @brief This callback is called when the XML parser found an entity it
 *	  doesn't know.
 *
 * The callback is supposed to return a substitution for the entity or `nil` if
 * it is not known to the callback as well, in which case an exception will be







<
|







|
<







|
<







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
 *
 * In case there are comments or CDATA, it is possible that this callback is
 * called multiple times in a row.
 *
 * @param parser The parser which found a string
 * @param characters The characters the XML parser found
 */

- (void)parser: (OFXMLParser *)parser foundCharacters: (OFString *)characters;

/**
 * @brief This callback is called when the XML parser found CDATA.
 *
 * @param parser The parser which found a string
 * @param CDATA The CDATA the XML parser found
 */
- (void)parser: (OFXMLParser *)parser foundCDATA: (OFString *)CDATA;


/**
 * @brief This callback is called when the XML parser found a comment.
 *
 * @param parser The parser which found a comment
 * @param comment The comment the XML parser found
 */
- (void)parser: (OFXMLParser *)parser foundComment: (OFString *)comment;


/**
 * @brief This callback is called when the XML parser found an entity it
 *	  doesn't know.
 *
 * The callback is supposed to return a substitution for the entity or `nil` if
 * it is not known to the callback as well, in which case an exception will be
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
 * OFXMLParser is an event-based XML parser which calls the delegate's callbacks
 * as soon as it finds something, thus suitable for streams as well.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFXMLParser: OFObject
{
	id <OFXMLParserDelegate> _Nullable _delegate;
	enum of_xml_parser_state {
		OF_XMLPARSER_IN_BYTE_ORDER_MARK,
		OF_XMLPARSER_OUTSIDE_TAG,
		OF_XMLPARSER_TAG_OPENED,
		OF_XMLPARSER_IN_PROCESSING_INSTRUCTIONS,
		OF_XMLPARSER_IN_TAG_NAME,
		OF_XMLPARSER_IN_CLOSE_TAG_NAME,
		OF_XMLPARSER_IN_TAG,
		OF_XMLPARSER_IN_ATTRIBUTE_NAME,
		OF_XMLPARSER_EXPECT_ATTRIBUTE_EQUAL_SIGN,
		OF_XMLPARSER_EXPECT_ATTRIBUTE_DELIMITER,
		OF_XMLPARSER_IN_ATTRIBUTE_VALUE,
		OF_XMLPARSER_EXPECT_TAG_CLOSE,
		OF_XMLPARSER_EXPECT_SPACE_OR_TAG_CLOSE,
		OF_XMLPARSER_IN_EXCLAMATION_MARK,
		OF_XMLPARSER_IN_CDATA_OPENING,
		OF_XMLPARSER_IN_CDATA,
		OF_XMLPARSER_IN_COMMENT_OPENING,
		OF_XMLPARSER_IN_COMMENT_1,
		OF_XMLPARSER_IN_COMMENT_2,
		OF_XMLPARSER_IN_DOCTYPE
	} _state;
	size_t _i, _last;
	const char *_Nullable _data;
	OFMutableData *_buffer;
	OFString *_Nullable _name, *_Nullable _prefix;
	OFMutableArray
	    OF_GENERIC(OFMutableDictionary OF_GENERIC(OFString *, OFString *) *)
	    *_namespaces;
	OFMutableArray OF_GENERIC(OFXMLAttribute *) *_attributes;
	OFString *_Nullable _attributeName, *_Nullable _attributePrefix;
	char _delimiter;
	OFMutableArray OF_GENERIC(OFString *) *_previous;
	size_t _level;
	bool _acceptProlog;
	size_t _lineNumber;
	bool _lastCarriageReturn, _finishedParsing;
	of_string_encoding_t _encoding;
	size_t _depthLimit;
}

/**
 * @brief The delegate that is used by the XML parser.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFXMLParserDelegate> delegate;

/**
 * @brief The current line number.
 */
@property (readonly, nonatomic) size_t lineNumber;

/**
 * @brief Whether the XML parser has finished parsing.
 */
@property (readonly, nonatomic) bool hasFinishedParsing;

/**
 * @brief The depth limit for the XML parser.
 *
 * If the depth limit is exceeded, an OFMalformedXMLException is thrown.
 *
 * The default is 32. 0 means unlimited (insecure!).







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|















|

















|







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
 * OFXMLParser is an event-based XML parser which calls the delegate's callbacks
 * as soon as it finds something, thus suitable for streams as well.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFXMLParser: OFObject
{
	id <OFXMLParserDelegate> _Nullable _delegate;





















	uint_least8_t _state;
	size_t _i, _last;
	const char *_Nullable _data;
	OFMutableData *_buffer;
	OFString *_Nullable _name, *_Nullable _prefix;
	OFMutableArray
	    OF_GENERIC(OFMutableDictionary OF_GENERIC(OFString *, OFString *) *)
	    *_namespaces;
	OFMutableArray OF_GENERIC(OFXMLAttribute *) *_attributes;
	OFString *_Nullable _attributeName, *_Nullable _attributePrefix;
	char _delimiter;
	OFMutableArray OF_GENERIC(OFString *) *_previous;
	size_t _level;
	bool _acceptProlog;
	size_t _lineNumber;
	bool _lastCarriageReturn, _finishedParsing;
	OFStringEncoding _encoding;
	size_t _depthLimit;
}

/**
 * @brief The delegate that is used by the XML parser.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFXMLParserDelegate> delegate;

/**
 * @brief The current line number.
 */
@property (readonly, nonatomic) size_t lineNumber;

/**
 * @brief Whether the XML parser has finished parsing.
 */
@property (readonly, nonatomic, getter=hasFinishedParsing) bool finishedParsing;

/**
 * @brief The depth limit for the XML parser.
 *
 * If the depth limit is exceeded, an OFMalformedXMLException is thrown.
 *
 * The default is 32. 0 means unlimited (insecure!).
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

/**
 * @brief Parses the specified buffer with the specified size.
 *
 * @param buffer The buffer to parse
 * @param length The length of the buffer
 */
- (void)parseBuffer: (const char *)buffer
	     length: (size_t)length;

/**
 * @brief Parses the specified string.
 *
 * @param string The string to parse
 */
- (void)parseString: (OFString *)string;

/**
 * @brief Parses the specified stream.
 *
 * @param stream The stream to parse
 */
- (void)parseStream: (OFStream *)stream;

#ifdef OF_HAVE_FILES
/**
 * @brief Parses the specified file.
 *
 * @param path The path to the file to parse
*/
- (void)parseFile: (OFString *)path;
#endif
@end

OF_ASSUME_NONNULL_END







|
<














<
<
<
<
<
<
<
<
<



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

/**
 * @brief Parses the specified buffer with the specified size.
 *
 * @param buffer The buffer to parse
 * @param length The length of the buffer
 */
- (void)parseBuffer: (const char *)buffer length: (size_t)length;


/**
 * @brief Parses the specified string.
 *
 * @param string The string to parse
 */
- (void)parseString: (OFString *)string;

/**
 * @brief Parses the specified stream.
 *
 * @param stream The stream to parse
 */
- (void)parseStream: (OFStream *)stream;









@end

OF_ASSUME_NONNULL_END

Modified src/OFXMLParser.m from [ff15f62058] to [beb767fa68].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#define OF_XML_PARSER_M

#include <string.h>

#import "OFXMLParser.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFData.h"
#import "OFXMLAttribute.h"
#import "OFStream.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif


#import "OFSystemInfo.h"


#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFMalformedXMLException.h"
#import "OFOutOfRangeException.h"
#import "OFUnboundPrefixException.h"
























@interface OFXMLParser () <OFStringXMLUnescapingDelegate>
@end

static void inByteOrderMarkState(OFXMLParser *);
static void outsideTagState(OFXMLParser *);
static void tagOpenedState(OFXMLParser *);
static void inProcessingInstructionsState(OFXMLParser *);
static void inTagNameState(OFXMLParser *);
static void inCloseTagNameState(OFXMLParser *);
static void inTagState(OFXMLParser *);
static void inAttributeNameState(OFXMLParser *);
static void expectAttributeEqualSignState(OFXMLParser *);
static void expectAttributeDelimiterState(OFXMLParser *);
static void inAttributeValueState(OFXMLParser *);
static void expectTagCloseState(OFXMLParser *);
static void expectSpaceOrTagCloseState(OFXMLParser *);
static void inExclamationMarkState(OFXMLParser *);
static void inCDATAOpeningState(OFXMLParser *);
static void inCDATAState(OFXMLParser *);
static void inCommentOpeningState(OFXMLParser *);
static void inCommentState1(OFXMLParser *);
static void inCommentState2(OFXMLParser *);
static void inDOCTYPEState(OFXMLParser *);
typedef void (*state_function_t)(OFXMLParser *);
static state_function_t lookupTable[] = {
	[OF_XMLPARSER_IN_BYTE_ORDER_MARK] = inByteOrderMarkState,
	[OF_XMLPARSER_OUTSIDE_TAG] = outsideTagState,
	[OF_XMLPARSER_TAG_OPENED] = tagOpenedState,
	[OF_XMLPARSER_IN_PROCESSING_INSTRUCTIONS] =
	    inProcessingInstructionsState,
	[OF_XMLPARSER_IN_TAG_NAME] = inTagNameState,
	[OF_XMLPARSER_IN_CLOSE_TAG_NAME] = inCloseTagNameState,
	[OF_XMLPARSER_IN_TAG] = inTagState,
	[OF_XMLPARSER_IN_ATTRIBUTE_NAME] = inAttributeNameState,
	[OF_XMLPARSER_EXPECT_ATTRIBUTE_EQUAL_SIGN] =
	    expectAttributeEqualSignState,
	[OF_XMLPARSER_EXPECT_ATTRIBUTE_DELIMITER] =
	    expectAttributeDelimiterState,
	[OF_XMLPARSER_IN_ATTRIBUTE_VALUE] = inAttributeValueState,
	[OF_XMLPARSER_EXPECT_TAG_CLOSE] = expectTagCloseState,
	[OF_XMLPARSER_EXPECT_SPACE_OR_TAG_CLOSE] = expectSpaceOrTagCloseState,
	[OF_XMLPARSER_IN_EXCLAMATION_MARK] = inExclamationMarkState,
	[OF_XMLPARSER_IN_CDATA_OPENING] = inCDATAOpeningState,
	[OF_XMLPARSER_IN_CDATA] = inCDATAState,
	[OF_XMLPARSER_IN_COMMENT_OPENING] = inCommentOpeningState,
	[OF_XMLPARSER_IN_COMMENT_1] = inCommentState1,
	[OF_XMLPARSER_IN_COMMENT_2] = inCommentState2,
	[OF_XMLPARSER_IN_DOCTYPE] = inDOCTYPEState
};

static OF_INLINE void
appendToBuffer(OFMutableData *buffer, const char *string,
    of_string_encoding_t encoding, size_t length)
{
	if (OF_LIKELY(encoding == OF_STRING_ENCODING_UTF_8))
		[buffer addItems: string
			   count: length];
	else {
		void *pool = objc_autoreleasePoolPush();
		OFString *tmp = [OFString stringWithCString: string
						   encoding: encoding
						     length: length];
		[buffer addItems: tmp.UTF8String
			   count: tmp.UTF8StringLength];
		objc_autoreleasePoolPop(pool);
	}
}

static OFString *
transformString(OFXMLParser *parser, OFMutableData *buffer, size_t cut,
    bool unescape)

<
<
|




















|
|
<

<
|



>
>

>








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







|
















|
|
|
|
|
<
|
|
|
|
|
<
|
<
|
|
|
|
|
|
|
|
|
|
|




|

|
|
<





|
<







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


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

#define OF_XML_PARSER_M

#include <string.h>

#import "OFXMLParser.h"
#import "OFArray.h"
#import "OFCharacterSet.h"

#import "OFData.h"

#import "OFDictionary.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFStream.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFXMLAttribute.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFMalformedXMLException.h"
#import "OFOutOfRangeException.h"
#import "OFUnboundPrefixException.h"

enum {
	stateInByteOrderMark,
	stateOutsideTag,
	stateTagOpened,
	stateInProcessingInstruction,
	stateInTagName,
	stateInCloseTagName,
	stateInTag,
	stateInAttributeName,
	stateExpectAttributeEqualSign,
	stateExpectAttributeDelimiter,
	stateInAttributeValue,
	stateExpectTagClose,
	stateExpectSpaceOrTagClose,
	stateInExclamationMark,
	stateInCDATAOpening,
	stateInCDATA,
	stateInCommentOpening,
	stateInComment1,
	stateInComment2,
	stateInDOCTYPE
};

@interface OFXMLParser () <OFStringXMLUnescapingDelegate>
@end

static void inByteOrderMarkState(OFXMLParser *);
static void outsideTagState(OFXMLParser *);
static void tagOpenedState(OFXMLParser *);
static void inProcessingInstructionState(OFXMLParser *);
static void inTagNameState(OFXMLParser *);
static void inCloseTagNameState(OFXMLParser *);
static void inTagState(OFXMLParser *);
static void inAttributeNameState(OFXMLParser *);
static void expectAttributeEqualSignState(OFXMLParser *);
static void expectAttributeDelimiterState(OFXMLParser *);
static void inAttributeValueState(OFXMLParser *);
static void expectTagCloseState(OFXMLParser *);
static void expectSpaceOrTagCloseState(OFXMLParser *);
static void inExclamationMarkState(OFXMLParser *);
static void inCDATAOpeningState(OFXMLParser *);
static void inCDATAState(OFXMLParser *);
static void inCommentOpeningState(OFXMLParser *);
static void inCommentState1(OFXMLParser *);
static void inCommentState2(OFXMLParser *);
static void inDOCTYPEState(OFXMLParser *);
typedef void (*StateFunction)(OFXMLParser *);
static StateFunction lookupTable[] = {
	[stateInByteOrderMark] = inByteOrderMarkState,
	[stateOutsideTag] = outsideTagState,
	[stateTagOpened] = tagOpenedState,

	[stateInProcessingInstruction] = inProcessingInstructionState,
	[stateInTagName] = inTagNameState,
	[stateInCloseTagName] = inCloseTagNameState,
	[stateInTag] = inTagState,
	[stateInAttributeName] = inAttributeNameState,

	[stateExpectAttributeEqualSign] = expectAttributeEqualSignState,

	[stateExpectAttributeDelimiter] = expectAttributeDelimiterState,
	[stateInAttributeValue] = inAttributeValueState,
	[stateExpectTagClose] = expectTagCloseState,
	[stateExpectSpaceOrTagClose] = expectSpaceOrTagCloseState,
	[stateInExclamationMark] = inExclamationMarkState,
	[stateInCDATAOpening] = inCDATAOpeningState,
	[stateInCDATA] = inCDATAState,
	[stateInCommentOpening] = inCommentOpeningState,
	[stateInComment1] = inCommentState1,
	[stateInComment2] = inCommentState2,
	[stateInDOCTYPE] = inDOCTYPEState
};

static OF_INLINE void
appendToBuffer(OFMutableData *buffer, const char *string,
    OFStringEncoding encoding, size_t length)
{
	if OF_LIKELY(encoding == OFStringEncodingUTF8)
		[buffer addItems: string count: length];

	else {
		void *pool = objc_autoreleasePoolPush();
		OFString *tmp = [OFString stringWithCString: string
						   encoding: encoding
						     length: length];
		[buffer addItems: tmp.UTF8String count: tmp.UTF8StringLength];

		objc_autoreleasePoolPop(pool);
	}
}

static OFString *
transformString(OFXMLParser *parser, OFMutableData *buffer, size_t cut,
    bool unescape)
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
				length--;
			} else
				items[i] = '\n';
		} else if (items[i] == '&')
			hasEntities = true;
	}

	ret = [OFString stringWithUTF8String: items
				      length: length];

	if (unescape && hasEntities) {
		@try {
			return [ret stringByXMLUnescapingWithDelegate: parser];
		} @catch (OFInvalidFormatException *e) {
			@throw [OFMalformedXMLException
			    exceptionWithParser: parser];







|
<







145
146
147
148
149
150
151
152

153
154
155
156
157
158
159
				length--;
			} else
				items[i] = '\n';
		} else if (items[i] == '&')
			hasEntities = true;
	}

	ret = [OFString stringWithUTF8String: items length: length];


	if (unescape && hasEntities) {
		@try {
			return [ret stringByXMLUnescapingWithDelegate: parser];
		} @catch (OFInvalidFormatException *e) {
			@throw [OFMalformedXMLException
			    exceptionWithParser: parser];
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
		dict = [OFMutableDictionary dictionaryWithKeysAndObjects:
		    @"xml", @"http://www.w3.org/XML/1998/namespace",
		    @"xmlns", @"http://www.w3.org/2000/xmlns/", nil];
		[_namespaces addObject: dict];

		_acceptProlog = true;
		_lineNumber = 1;
		_encoding = OF_STRING_ENCODING_UTF_8;
		_depthLimit = 32;

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}







|







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
		dict = [OFMutableDictionary dictionaryWithKeysAndObjects:
		    @"xml", @"http://www.w3.org/XML/1998/namespace",
		    @"xmlns", @"http://www.w3.org/2000/xmlns/", nil];
		[_namespaces addObject: dict];

		_acceptProlog = true;
		_lineNumber = 1;
		_encoding = OFStringEncodingUTF8;
		_depthLimit = 32;

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}
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
	[_attributeName release];
	[_attributePrefix release];
	[_previous release];

	[super dealloc];
}

- (void)parseBuffer: (const char *)buffer
	     length: (size_t)length
{
	_data = buffer;

	for (_i = _last = 0; _i < length; _i++) {
		size_t j = _i;

		lookupTable[_state](self);

		/* Ensure we don't count this character twice */
		if (_i != j)
			continue;

		if (_data[_i] == '\r' || (_data[_i] == '\n' &&
		    !_lastCarriageReturn))
			_lineNumber++;

		_lastCarriageReturn = (_data[_i] == '\r');
	}

	/* In OF_XMLPARSER_IN_TAG, there can be only spaces */
	if (length - _last > 0 && _state != OF_XMLPARSER_IN_TAG)
		appendToBuffer(_buffer, _data + _last, _encoding,
		    length - _last);
}

- (void)parseString: (OFString *)string
{
	[self parseBuffer: string.UTF8String
		   length: string.UTF8StringLength];
}

- (void)parseStream: (OFStream *)stream
{
	size_t pageSize = [OFSystemInfo pageSize];
	char *buffer = [self allocMemoryWithSize: pageSize];

	@try {
		while (!stream.atEndOfStream) {
			size_t length = [stream readIntoBuffer: buffer
							length: pageSize];

			[self parseBuffer: buffer
				   length: length];
		}
	} @finally {
		[self freeMemory: buffer];
	}
}

#ifdef OF_HAVE_FILES
- (void)parseFile: (OFString *)path
{
	OFFile *file = [[OFFile alloc] initWithPath: path
					       mode: @"r"];
	@try {
		[self parseStream: file];
	} @finally {
		[file release];
	}
}
#endif

static void
inByteOrderMarkState(OFXMLParser *self)
{
	if (self->_data[self->_i] != "\xEF\xBB\xBF"[self->_level]) {
		if (self->_level == 0) {
			self->_state = OF_XMLPARSER_OUTSIDE_TAG;
			self->_i--;
			return;
		}

		@throw [OFMalformedXMLException exceptionWithParser: self];
	}

	if (self->_level++ == 2)
		self->_state = OF_XMLPARSER_OUTSIDE_TAG;

	self->_last = self->_i + 1;
}

/* Not in a tag */
static void
outsideTagState(OFXMLParser *self)







|
<



















|
|






|
<





|





<
|
<


|


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






|








|







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
	[_attributeName release];
	[_attributePrefix release];
	[_previous release];

	[super dealloc];
}

- (void)parseBuffer: (const char *)buffer length: (size_t)length

{
	_data = buffer;

	for (_i = _last = 0; _i < length; _i++) {
		size_t j = _i;

		lookupTable[_state](self);

		/* Ensure we don't count this character twice */
		if (_i != j)
			continue;

		if (_data[_i] == '\r' || (_data[_i] == '\n' &&
		    !_lastCarriageReturn))
			_lineNumber++;

		_lastCarriageReturn = (_data[_i] == '\r');
	}

	/* In stateInTag, there can be only spaces */
	if (length - _last > 0 && _state != stateInTag)
		appendToBuffer(_buffer, _data + _last, _encoding,
		    length - _last);
}

- (void)parseString: (OFString *)string
{
	[self parseBuffer: string.UTF8String length: string.UTF8StringLength];

}

- (void)parseStream: (OFStream *)stream
{
	size_t pageSize = [OFSystemInfo pageSize];
	char *buffer = OFAllocMemory(1, pageSize);

	@try {
		while (!stream.atEndOfStream) {
			size_t length = [stream readIntoBuffer: buffer
							length: pageSize];

			[self parseBuffer: buffer length: length];

		}
	} @finally {
		OFFreeMemory(buffer);
	}
}














static void
inByteOrderMarkState(OFXMLParser *self)
{
	if (self->_data[self->_i] != "\xEF\xBB\xBF"[self->_level]) {
		if (self->_level == 0) {
			self->_state = stateOutsideTag;
			self->_i--;
			return;
		}

		@throw [OFMalformedXMLException exceptionWithParser: self];
	}

	if (self->_level++ == 2)
		self->_state = stateOutsideTag;

	self->_last = self->_i + 1;
}

/* Not in a tag */
static void
outsideTagState(OFXMLParser *self)
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

		objc_autoreleasePoolPop(pool);
	}

	[self->_buffer removeAllItems];

	self->_last = self->_i + 1;
	self->_state = OF_XMLPARSER_TAG_OPENED;
}

/* Tag was just opened */
static void
tagOpenedState(OFXMLParser *self)
{
	if (self->_finishedParsing && self->_data[self->_i] != '!' &&
	    self->_data[self->_i] != '?')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	switch (self->_data[self->_i]) {
	case '?':
		self->_last = self->_i + 1;
		self->_state = OF_XMLPARSER_IN_PROCESSING_INSTRUCTIONS;
		self->_level = 0;
		break;
	case '/':
		self->_last = self->_i + 1;
		self->_state = OF_XMLPARSER_IN_CLOSE_TAG_NAME;
		self->_acceptProlog = false;
		break;
	case '!':
		self->_last = self->_i + 1;
		self->_state = OF_XMLPARSER_IN_EXCLAMATION_MARK;
		self->_acceptProlog = false;
		break;
	default:
		if (self->_depthLimit > 0 &&
		    self->_previous.count >= self->_depthLimit)
			@throw [OFOutOfRangeException exception];

		self->_state = OF_XMLPARSER_IN_TAG_NAME;
		self->_acceptProlog = false;
		self->_i--;
		break;
	}
}

/* <?xml […]?> */
static bool
parseXMLProcessingInstructions(OFXMLParser *self, OFString *pi)
{
	const char *cString;
	size_t length, last;
	int PIState = 0;
	OFString *attribute = nil;
	OFMutableString *value = nil;
	char piDelimiter = 0;
	bool hasVersion = false;

	if (!self->_acceptProlog)
		return false;

	self->_acceptProlog = false;

	pi = [pi substringFromIndex: 3];
	pi = pi.stringByDeletingEnclosingWhitespaces;

	cString = pi.UTF8String;
	length = pi.UTF8StringLength;

	last = 0;
	for (size_t i = 0; i < length; i++) {
		switch (PIState) {
		case 0:
			if (cString[i] == ' ' || cString[i] == '\t' ||
			    cString[i] == '\r' || cString[i] == '\n')







|













|




|




|







|








|














<
<
<
|
|







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

		objc_autoreleasePoolPop(pool);
	}

	[self->_buffer removeAllItems];

	self->_last = self->_i + 1;
	self->_state = stateTagOpened;
}

/* Tag was just opened */
static void
tagOpenedState(OFXMLParser *self)
{
	if (self->_finishedParsing && self->_data[self->_i] != '!' &&
	    self->_data[self->_i] != '?')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	switch (self->_data[self->_i]) {
	case '?':
		self->_last = self->_i + 1;
		self->_state = stateInProcessingInstruction;
		self->_level = 0;
		break;
	case '/':
		self->_last = self->_i + 1;
		self->_state = stateInCloseTagName;
		self->_acceptProlog = false;
		break;
	case '!':
		self->_last = self->_i + 1;
		self->_state = stateInExclamationMark;
		self->_acceptProlog = false;
		break;
	default:
		if (self->_depthLimit > 0 &&
		    self->_previous.count >= self->_depthLimit)
			@throw [OFOutOfRangeException exception];

		self->_state = stateInTagName;
		self->_acceptProlog = false;
		self->_i--;
		break;
	}
}

/* <?xml […]?> */
static bool
parseXMLProcessingInstruction(OFXMLParser *self, OFString *data)
{
	const char *cString;
	size_t length, last;
	int PIState = 0;
	OFString *attribute = nil;
	OFMutableString *value = nil;
	char piDelimiter = 0;
	bool hasVersion = false;

	if (!self->_acceptProlog)
		return false;

	self->_acceptProlog = false;




	cString = data.UTF8String;
	length = data.UTF8StringLength;

	last = 0;
	for (size_t i = 0; i < length; i++) {
		switch (PIState) {
		case 0:
			if (cString[i] == ' ' || cString[i] == '\t' ||
			    cString[i] == '\r' || cString[i] == '\n')
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

				hasVersion = true;
			}

			if ([attribute isEqual: @"encoding"]) {
				@try {
					self->_encoding =
					    of_string_parse_encoding(value);
				} @catch (OFInvalidArgumentException *e) {
					@throw [OFInvalidEncodingException
					    exception];
				}
			}

			last = i + 1;
			PIState = 0;

			break;
		}
	}

	if (PIState != 0 || !hasVersion)
		return false;

	return true;
}

/* Inside processing instructions */
static void
inProcessingInstructionsState(OFXMLParser *self)
{
	if (self->_data[self->_i] == '?')
		self->_level = 1;
	else if (self->_level == 1 && self->_data[self->_i] == '>') {
		void *pool = objc_autoreleasePoolPush();
		OFString *PI;



		appendToBuffer(self->_buffer, self->_data + self->_last,
		    self->_encoding, self->_i - self->_last);
		PI = transformString(self, self->_buffer, 1, false);








		if ([PI isEqual: @"xml"] || [PI hasPrefix: @"xml "] ||




		    [PI hasPrefix: @"xml\t"] || [PI hasPrefix: @"xml\r"] ||
		    [PI hasPrefix: @"xml\n"])
			if (!parseXMLProcessingInstructions(self, PI))
				@throw [OFMalformedXMLException
				    exceptionWithParser: self];

		if ([self->_delegate respondsToSelector:
		    @selector(parser:foundProcessingInstructions:)])
			[self->_delegate	 parser: self
			    foundProcessingInstructions: PI];


		objc_autoreleasePoolPop(pool);

		[self->_buffer removeAllItems];

		self->_last = self->_i + 1;
		self->_state = OF_XMLPARSER_OUTSIDE_TAG;
	} else
		self->_level = 0;
}

/* Inside a tag, no name yet */
static void
inTagNameState(OFXMLParser *self)







|



















|

|





|
>
>





>
>
>
>
>
>
>
|
>
>
>
>
|
|
|



|
|
|
|
>






|







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

				hasVersion = true;
			}

			if ([attribute isEqual: @"encoding"]) {
				@try {
					self->_encoding =
					    OFStringEncodingParseName(value);
				} @catch (OFInvalidArgumentException *e) {
					@throw [OFInvalidEncodingException
					    exception];
				}
			}

			last = i + 1;
			PIState = 0;

			break;
		}
	}

	if (PIState != 0 || !hasVersion)
		return false;

	return true;
}

/* Inside processing instruction */
static void
inProcessingInstructionState(OFXMLParser *self)
{
	if (self->_data[self->_i] == '?')
		self->_level = 1;
	else if (self->_level == 1 && self->_data[self->_i] == '>') {
		void *pool = objc_autoreleasePoolPush();
		OFString *PI, *target, *data = nil;
		OFCharacterSet *whitespaceCS;
		size_t pos;

		appendToBuffer(self->_buffer, self->_data + self->_last,
		    self->_encoding, self->_i - self->_last);
		PI = transformString(self, self->_buffer, 1, false);

		whitespaceCS = [OFCharacterSet
		    characterSetWithCharactersInString: @" \r\n\r"];
		pos = [PI indexOfCharacterFromSet: whitespaceCS];
		if (pos != OFNotFound) {
			target = [PI substringToIndex: pos];
			data = [[PI substringFromIndex: pos + 1]
			    stringByDeletingEnclosingWhitespaces];

			if (data.length == 0)
				data = nil;
		} else
			target = PI;

		if ([target caseInsensitiveCompare: @"xml"] == OFOrderedSame)
			if (!parseXMLProcessingInstruction(self, data))
				@throw [OFMalformedXMLException
				    exceptionWithParser: self];

		if ([self->_delegate respondsToSelector: @selector(
		    parser:foundProcessingInstructionWithTarget:data:)])
			[self->_delegate parser: self
			    foundProcessingInstructionWithTarget: target
							    data: data];

		objc_autoreleasePoolPop(pool);

		[self->_buffer removeAllItems];

		self->_last = self->_i + 1;
		self->_state = stateOutsideTag;
	} else
		self->_level = 0;
}

/* Inside a tag, no name yet */
static void
inTagNameState(OFXMLParser *self)
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
				length: tmp - bufferCString];
	} else {
		self->_name = [bufferString copy];
		self->_prefix = nil;
	}

	if (self->_data[self->_i] == '>' || self->_data[self->_i] == '/') {
		OFString *namespace;

		namespace = namespaceForPrefix(self->_prefix,
		    self->_namespaces);

		if (self->_prefix != nil && namespace == nil)
			@throw [OFUnboundPrefixException
			    exceptionWithPrefix: self->_prefix
					 parser: self];








<
<
|







576
577
578
579
580
581
582


583
584
585
586
587
588
589
590
				length: tmp - bufferCString];
	} else {
		self->_name = [bufferString copy];
		self->_prefix = nil;
	}

	if (self->_data[self->_i] == '>' || self->_data[self->_i] == '/') {


		OFString *namespace = namespaceForPrefix(self->_prefix,
		    self->_namespaces);

		if (self->_prefix != nil && namespace == nil)
			@throw [OFUnboundPrefixException
			    exceptionWithPrefix: self->_prefix
					 parser: self];

602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
			[self->_previous addObject: bufferString];

		[self->_name release];
		[self->_prefix release];
		self->_name = self->_prefix = nil;

		self->_state = (self->_data[self->_i] == '/'
		    ? OF_XMLPARSER_EXPECT_TAG_CLOSE
		    : OF_XMLPARSER_OUTSIDE_TAG);
	} else
		self->_state = OF_XMLPARSER_IN_TAG;

	if (self->_data[self->_i] != '/')
		[self->_namespaces addObject: [OFMutableDictionary dictionary]];

	objc_autoreleasePoolPop(pool);

	[self->_buffer removeAllItems];







|
<

|







610
611
612
613
614
615
616
617

618
619
620
621
622
623
624
625
626
			[self->_previous addObject: bufferString];

		[self->_name release];
		[self->_prefix release];
		self->_name = self->_prefix = nil;

		self->_state = (self->_data[self->_i] == '/'
		    ? stateExpectTagClose : stateOutsideTag);

	} else
		self->_state = stateInTag;

	if (self->_data[self->_i] != '/')
		[self->_namespaces addObject: [OFMutableDictionary dictionary]];

	objc_autoreleasePoolPop(pool);

	[self->_buffer removeAllItems];
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
	[self->_namespaces removeLastObject];
	[self->_name release];
	[self->_prefix release];
	self->_name = self->_prefix = nil;

	self->_last = self->_i + 1;
	self->_state = (self->_data[self->_i] == '>'
	    ? OF_XMLPARSER_OUTSIDE_TAG
	    : OF_XMLPARSER_EXPECT_SPACE_OR_TAG_CLOSE);

	if (self->_previous.count == 0)
		self->_finishedParsing = true;
}

/* Inside a tag, name found */
static void
inTagState(OFXMLParser *self)
{
	void *pool;
	OFString *namespace;
	OFXMLAttribute *const *attributesObjects;
	size_t attributesCount;

	if (self->_data[self->_i] != '>' && self->_data[self->_i] != '/') {
		if (self->_data[self->_i] != ' ' &&
		    self->_data[self->_i] != '\t' &&
		    self->_data[self->_i] != '\n' &&
		    self->_data[self->_i] != '\r') {
			self->_last = self->_i;
			self->_state = OF_XMLPARSER_IN_ATTRIBUTE_NAME;
			self->_i--;
		}

		return;
	}

	attributesObjects = self->_attributes.objects;







|
<




















|







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
	[self->_namespaces removeLastObject];
	[self->_name release];
	[self->_prefix release];
	self->_name = self->_prefix = nil;

	self->_last = self->_i + 1;
	self->_state = (self->_data[self->_i] == '>'
	    ? stateOutsideTag : stateExpectSpaceOrTagClose);


	if (self->_previous.count == 0)
		self->_finishedParsing = true;
}

/* Inside a tag, name found */
static void
inTagState(OFXMLParser *self)
{
	void *pool;
	OFString *namespace;
	OFXMLAttribute *const *attributesObjects;
	size_t attributesCount;

	if (self->_data[self->_i] != '>' && self->_data[self->_i] != '/') {
		if (self->_data[self->_i] != ' ' &&
		    self->_data[self->_i] != '\t' &&
		    self->_data[self->_i] != '\n' &&
		    self->_data[self->_i] != '\r') {
			self->_last = self->_i;
			self->_state = stateInAttributeName;
			self->_i--;
		}

		return;
	}

	attributesObjects = self->_attributes.objects;
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
	[self->_name release];
	[self->_prefix release];
	[self->_attributes removeAllObjects];
	self->_name = self->_prefix = nil;

	self->_last = self->_i + 1;
	self->_state = (self->_data[self->_i] == '/'
	    ? OF_XMLPARSER_EXPECT_TAG_CLOSE
	    : OF_XMLPARSER_OUTSIDE_TAG);
}

/* Looking for attribute name */
static void
inAttributeNameState(OFXMLParser *self)
{
	void *pool;







|
<







770
771
772
773
774
775
776
777

778
779
780
781
782
783
784
	[self->_name release];
	[self->_prefix release];
	[self->_attributes removeAllObjects];
	self->_name = self->_prefix = nil;

	self->_last = self->_i + 1;
	self->_state = (self->_data[self->_i] == '/'
	    ? stateExpectTagClose : stateOutsideTag);

}

/* Looking for attribute name */
static void
inAttributeNameState(OFXMLParser *self)
{
	void *pool;
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

	objc_autoreleasePoolPop(pool);

	[self->_buffer removeAllItems];

	self->_last = self->_i + 1;
	self->_state = (self->_data[self->_i] == '='
	    ? OF_XMLPARSER_EXPECT_ATTRIBUTE_DELIMITER
	    : OF_XMLPARSER_EXPECT_ATTRIBUTE_EQUAL_SIGN);
}

/* Expecting equal sign of an attribute */
static void
expectAttributeEqualSignState(OFXMLParser *self)
{
	if (self->_data[self->_i] == '=') {
		self->_last = self->_i + 1;
		self->_state = OF_XMLPARSER_EXPECT_ATTRIBUTE_DELIMITER;
		return;
	}

	if (self->_data[self->_i] != ' '  && self->_data[self->_i] != '\t' &&
	    self->_data[self->_i] != '\n' && self->_data[self->_i] != '\r')
		@throw [OFMalformedXMLException exceptionWithParser: self];
}







|
<








|







818
819
820
821
822
823
824
825

826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841

	objc_autoreleasePoolPop(pool);

	[self->_buffer removeAllItems];

	self->_last = self->_i + 1;
	self->_state = (self->_data[self->_i] == '='
	    ? stateExpectAttributeDelimiter : stateExpectAttributeEqualSign);

}

/* Expecting equal sign of an attribute */
static void
expectAttributeEqualSignState(OFXMLParser *self)
{
	if (self->_data[self->_i] == '=') {
		self->_last = self->_i + 1;
		self->_state = stateExpectAttributeDelimiter;
		return;
	}

	if (self->_data[self->_i] != ' '  && self->_data[self->_i] != '\t' &&
	    self->_data[self->_i] != '\n' && self->_data[self->_i] != '\r')
		@throw [OFMalformedXMLException exceptionWithParser: self];
}
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
	    self->_data[self->_i] == '\n' || self->_data[self->_i] == '\r')
		return;

	if (self->_data[self->_i] != '\'' && self->_data[self->_i] != '"')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	self->_delimiter = self->_data[self->_i];
	self->_state = OF_XMLPARSER_IN_ATTRIBUTE_VALUE;
}

/* Looking for attribute value */
static void
inAttributeValueState(OFXMLParser *self)
{
	void *pool;







|







850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
	    self->_data[self->_i] == '\n' || self->_data[self->_i] == '\r')
		return;

	if (self->_data[self->_i] != '\'' && self->_data[self->_i] != '"')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	self->_delimiter = self->_data[self->_i];
	self->_state = stateInAttributeValue;
}

/* Looking for attribute value */
static void
inAttributeValueState(OFXMLParser *self)
{
	void *pool;
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

	[self->_buffer removeAllItems];
	[self->_attributeName release];
	[self->_attributePrefix release];
	self->_attributeName = self->_attributePrefix = nil;

	self->_last = self->_i + 1;
	self->_state = OF_XMLPARSER_IN_TAG;
}

/* Expecting closing '>' */
static void
expectTagCloseState(OFXMLParser *self)
{
	if (self->_data[self->_i] == '>') {
		self->_last = self->_i + 1;
		self->_state = OF_XMLPARSER_OUTSIDE_TAG;
	} else
		@throw [OFMalformedXMLException exceptionWithParser: self];
}

/* Expecting closing '>' or space */
static void
expectSpaceOrTagCloseState(OFXMLParser *self)
{
	if (self->_data[self->_i] == '>') {
		self->_last = self->_i + 1;
		self->_state = OF_XMLPARSER_OUTSIDE_TAG;
	} else if (self->_data[self->_i] != ' ' &&
	    self->_data[self->_i] != '\t' && self->_data[self->_i] != '\n' &&
	    self->_data[self->_i] != '\r')
		@throw [OFMalformedXMLException exceptionWithParser: self];
}

/* In <! */
static void
inExclamationMarkState(OFXMLParser *self)
{
	if (self->_finishedParsing && self->_data[self->_i] != '-')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	if (self->_data[self->_i] == '-')
		self->_state = OF_XMLPARSER_IN_COMMENT_OPENING;
	else if (self->_data[self->_i] == '[') {
		self->_state = OF_XMLPARSER_IN_CDATA_OPENING;
		self->_level = 0;
	} else if (self->_data[self->_i] == 'D') {
		self->_state = OF_XMLPARSER_IN_DOCTYPE;
		self->_level = 0;
	} else
		@throw [OFMalformedXMLException exceptionWithParser: self];

	self->_last = self->_i + 1;
}

/* CDATA */
static void
inCDATAOpeningState(OFXMLParser *self)
{
	if (self->_data[self->_i] != "CDATA["[self->_level])
		@throw [OFMalformedXMLException exceptionWithParser: self];

	if (++self->_level == 6) {
		self->_state = OF_XMLPARSER_IN_CDATA;
		self->_level = 0;
	}

	self->_last = self->_i + 1;
}

static void







|








|










|














|

|


|















|







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

	[self->_buffer removeAllItems];
	[self->_attributeName release];
	[self->_attributePrefix release];
	self->_attributeName = self->_attributePrefix = nil;

	self->_last = self->_i + 1;
	self->_state = stateInTag;
}

/* Expecting closing '>' */
static void
expectTagCloseState(OFXMLParser *self)
{
	if (self->_data[self->_i] == '>') {
		self->_last = self->_i + 1;
		self->_state = stateOutsideTag;
	} else
		@throw [OFMalformedXMLException exceptionWithParser: self];
}

/* Expecting closing '>' or space */
static void
expectSpaceOrTagCloseState(OFXMLParser *self)
{
	if (self->_data[self->_i] == '>') {
		self->_last = self->_i + 1;
		self->_state = stateOutsideTag;
	} else if (self->_data[self->_i] != ' ' &&
	    self->_data[self->_i] != '\t' && self->_data[self->_i] != '\n' &&
	    self->_data[self->_i] != '\r')
		@throw [OFMalformedXMLException exceptionWithParser: self];
}

/* In <! */
static void
inExclamationMarkState(OFXMLParser *self)
{
	if (self->_finishedParsing && self->_data[self->_i] != '-')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	if (self->_data[self->_i] == '-')
		self->_state = stateInCommentOpening;
	else if (self->_data[self->_i] == '[') {
		self->_state = stateInCDATAOpening;
		self->_level = 0;
	} else if (self->_data[self->_i] == 'D') {
		self->_state = stateInDOCTYPE;
		self->_level = 0;
	} else
		@throw [OFMalformedXMLException exceptionWithParser: self];

	self->_last = self->_i + 1;
}

/* CDATA */
static void
inCDATAOpeningState(OFXMLParser *self)
{
	if (self->_data[self->_i] != "CDATA["[self->_level])
		@throw [OFMalformedXMLException exceptionWithParser: self];

	if (++self->_level == 6) {
		self->_state = stateInCDATA;
		self->_level = 0;
	}

	self->_last = self->_i + 1;
}

static void
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

		appendToBuffer(self->_buffer, self->_data + self->_last,
		    self->_encoding, self->_i - self->_last);
		CDATA = transformString(self, self->_buffer, 2, false);

		if ([self->_delegate respondsToSelector:
		    @selector(parser:foundCDATA:)])
			[self->_delegate parser: self
				     foundCDATA: CDATA];

		objc_autoreleasePoolPop(pool);

		[self->_buffer removeAllItems];

		self->_last = self->_i + 1;
		self->_state = OF_XMLPARSER_OUTSIDE_TAG;
	} else
		self->_level = 0;
}

/* Comment */
static void
inCommentOpeningState(OFXMLParser *self)
{
	if (self->_data[self->_i] != '-')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	self->_last = self->_i + 1;
	self->_state = OF_XMLPARSER_IN_COMMENT_1;
	self->_level = 0;
}

static void
inCommentState1(OFXMLParser *self)
{
	if (self->_data[self->_i] == '-')
		self->_level++;
	else
		self->_level = 0;

	if (self->_level == 2)
		self->_state = OF_XMLPARSER_IN_COMMENT_2;
}

static void
inCommentState2(OFXMLParser *self)
{
	void *pool;
	OFString *comment;

	if (self->_data[self->_i] != '>')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	pool = objc_autoreleasePoolPush();

	appendToBuffer(self->_buffer, self->_data + self->_last,
	    self->_encoding, self->_i - self->_last);
	comment = transformString(self, self->_buffer, 2, false);

	if ([self->_delegate respondsToSelector:
	    @selector(parser:foundComment:)])
		[self->_delegate parser: self
			   foundComment: comment];

	objc_autoreleasePoolPop(pool);

	[self->_buffer removeAllItems];

	self->_last = self->_i + 1;
	self->_state = OF_XMLPARSER_OUTSIDE_TAG;
}

/* In <!DOCTYPE ...> */
static void
inDOCTYPEState(OFXMLParser *self)
{
	if ((self->_level < 6 &&
	    self->_data[self->_i] != "OCTYPE"[self->_level]) ||
	    (self->_level == 6 && self->_data[self->_i] != ' ' &&
	    self->_data[self->_i] != '\t' && self->_data[self->_i] != '\n' &&
	    self->_data[self->_i] != '\r'))
		@throw [OFMalformedXMLException exceptionWithParser: self];

	self->_level++;

	if (self->_level > 6 && self->_data[self->_i] == '>')
		self->_state = OF_XMLPARSER_OUTSIDE_TAG;

	self->_last = self->_i + 1;
}

- (size_t)lineNumber
{
	return _lineNumber;
}

- (bool)hasFinishedParsing
{
	return _finishedParsing;
}

-	  (OFString *)string: (OFString *)string
  containsUnknownEntityNamed: (OFString *)entity
{
	if ([_delegate respondsToSelector:
	    @selector(parser:foundUnknownEntityNamed:)])
		return [_delegate parser: self
		 foundUnknownEntityNamed: entity];

	return nil;
}
@end







|
<






|












|












|



















|
<






|
















|



















|
<




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

		appendToBuffer(self->_buffer, self->_data + self->_last,
		    self->_encoding, self->_i - self->_last);
		CDATA = transformString(self, self->_buffer, 2, false);

		if ([self->_delegate respondsToSelector:
		    @selector(parser:foundCDATA:)])
			[self->_delegate parser: self foundCDATA: CDATA];


		objc_autoreleasePoolPop(pool);

		[self->_buffer removeAllItems];

		self->_last = self->_i + 1;
		self->_state = stateOutsideTag;
	} else
		self->_level = 0;
}

/* Comment */
static void
inCommentOpeningState(OFXMLParser *self)
{
	if (self->_data[self->_i] != '-')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	self->_last = self->_i + 1;
	self->_state = stateInComment1;
	self->_level = 0;
}

static void
inCommentState1(OFXMLParser *self)
{
	if (self->_data[self->_i] == '-')
		self->_level++;
	else
		self->_level = 0;

	if (self->_level == 2)
		self->_state = stateInComment2;
}

static void
inCommentState2(OFXMLParser *self)
{
	void *pool;
	OFString *comment;

	if (self->_data[self->_i] != '>')
		@throw [OFMalformedXMLException exceptionWithParser: self];

	pool = objc_autoreleasePoolPush();

	appendToBuffer(self->_buffer, self->_data + self->_last,
	    self->_encoding, self->_i - self->_last);
	comment = transformString(self, self->_buffer, 2, false);

	if ([self->_delegate respondsToSelector:
	    @selector(parser:foundComment:)])
		[self->_delegate parser: self foundComment: comment];


	objc_autoreleasePoolPop(pool);

	[self->_buffer removeAllItems];

	self->_last = self->_i + 1;
	self->_state = stateOutsideTag;
}

/* In <!DOCTYPE ...> */
static void
inDOCTYPEState(OFXMLParser *self)
{
	if ((self->_level < 6 &&
	    self->_data[self->_i] != "OCTYPE"[self->_level]) ||
	    (self->_level == 6 && self->_data[self->_i] != ' ' &&
	    self->_data[self->_i] != '\t' && self->_data[self->_i] != '\n' &&
	    self->_data[self->_i] != '\r'))
		@throw [OFMalformedXMLException exceptionWithParser: self];

	self->_level++;

	if (self->_level > 6 && self->_data[self->_i] == '>')
		self->_state = stateOutsideTag;

	self->_last = self->_i + 1;
}

- (size_t)lineNumber
{
	return _lineNumber;
}

- (bool)hasFinishedParsing
{
	return _finishedParsing;
}

-	  (OFString *)string: (OFString *)string
  containsUnknownEntityNamed: (OFString *)entity
{
	if ([_delegate respondsToSelector:
	    @selector(parser:foundUnknownEntityNamed:)])
		return [_delegate parser: self foundUnknownEntityNamed: entity];


	return nil;
}
@end

Renamed and modified src/OFXMLProcessingInstructions.h [a8a290f619] to src/OFXMLProcessingInstruction.h [c7ed4d9c0a].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFXMLNode.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFXMLProcessingInstructions \
 *	  OFXMLProcessingInstructions.h ObjFW/OFXMLProcessingInstructions.h
 *
 * @brief A class for representing XML processing instructions.
 */
@interface OFXMLProcessingInstructions: OFXMLNode
{
	OFString *_processingInstructions;
	OF_RESERVE_IVARS(OFXMLProcessingInstructions, 4)
}

/**










 * @brief Creates a new OFXMLProcessingInstructions with the specified string.

 *

 * @param string The string for the processing instructions
 * @return A new OFXMLProcessingInstructions
 */
+ (instancetype)processingInstructionsWithString: (OFString *)string;


/**
 * @brief Initializes an already allocated OFXMLProcessingInstructions with the
 *	  specified string.
 *

 * @param string The string for the processing instructions
 * @return An initialized OFXMLProcessingInstructions
 */
- (instancetype)initWithString: (OFString *)string;


- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end

OF_ASSUME_NONNULL_END

<
<
|


















|
|

|

|

|
|



>
>
>
>
>
>
>
>
>
>
|
>

>
|
|

|
>


|
|

>
|
|

|
>





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


 * 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 "OFXMLNode.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFXMLProcessingInstruction \
 *	  OFXMLProcessingInstruction.h ObjFW/OFXMLProcessingInstruction.h
 *
 * @brief A class for representing an XML processing instruction.
 */
@interface OFXMLProcessingInstruction: OFXMLNode
{
	OFString *_target, *_data;
	OF_RESERVE_IVARS(OFXMLProcessingInstruction, 4)
}

/**
 * @brief The target of the processing instruction.
 */
@property (readonly, nonatomic) OFString *target;

/**
 * @brief The data of the processing instruction.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *data;

/**
 * @brief Creates a new OFXMLProcessingInstruction with the specified target
 *	  and data.
 *
 * @param target The target for the processing instruction
 * @param data The data for the processing instruction
 * @return A new OFXMLProcessingInstruction
 */
+ (instancetype)processingInstructionWithTarget: (OFString *)target
					   data: (OFString *)data;

/**
 * @brief Initializes an already allocated OFXMLProcessingInstruction with the
 *	  specified target and data.
 *
 * @param target The target for the processing instruction
 * @param data The data for the processing instruction
 * @return An initialized OFXMLProcessingInstruction
 */
- (instancetype)initWithTarget: (OFString *)target
			  data: (OFString *)data OF_DESIGNATED_INITIALIZER;

- (instancetype)initWithSerialization: (OFXMLElement *)element;
@end

OF_ASSUME_NONNULL_END

Renamed and modified src/OFXMLProcessingInstructions.m [7c38ca2413] to src/OFXMLProcessingInstruction.m [16bf6dae0d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "OFXMLProcessingInstructions.h"
#import "OFXMLNode+Private.h"
#import "OFString.h"
#import "OFXMLElement.h"


#import "OFInvalidArgumentException.h"

@implementation OFXMLProcessingInstructions


+ (instancetype)processingInstructionsWithString: (OFString *)string

{
	return [[[self alloc] initWithString: string] autorelease];

}

- (instancetype)initWithString: (OFString *)string

{
	self = [super of_init];

	@try {

		_processingInstructions = [string copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [super of_init];

	@try {
		void *pool = objc_autoreleasePoolPush();


		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];







		_processingInstructions = [element.stringValue copy];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{

	[_processingInstructions release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFXMLProcessingInstructions *processingInstructions;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFXMLProcessingInstructions class]])
		return false;

	processingInstructions = object;




	return [processingInstructions->_processingInstructions
	    isEqual: _processingInstructions];



}

- (unsigned long)hash
{







	return _processingInstructions.hash;
}

- (OFString *)stringValue
{
	return @"";
}

- (OFString *)XMLString
{

	return [OFString stringWithFormat: @"<?%@?>", _processingInstructions];



}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
	return [OFString stringWithFormat: @"<?%@?>", _processingInstructions];
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
				 level: (unsigned int)level
{
	OFString *ret;

	if (indentation > 0 && level > 0) {
		char *whitespaces = [self allocMemoryWithSize:

		    (level * indentation) + 1];
		memset(whitespaces, ' ', level * indentation);
		whitespaces[level * indentation] = 0;

		@try {

			ret = [OFString stringWithFormat:




			    @"%s<?%@?>", whitespaces, _processingInstructions];
		} @finally {
			[self freeMemory: whitespaces];
		}
	} else
		ret = [OFString stringWithFormat: @"<?%@?>",
						  _processingInstructions];

	return ret;


}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<?%@?>", _processingInstructions];
}

- (OFXMLElement *)XMLElementBySerializing
{
	return [OFXMLElement elementWithName: self.className
				   namespace: OF_SERIALIZATION_NS
				 stringValue: _processingInstructions];

}







@end

<
<
|

















|
|
|

>



|
>
>
|
>

|
>


|
>




>
|










<
<


>


|


>
>
>
>
>
>
|












>
|






|




|


|

>
>
>
|
|
>
>
>




>
>
>
>
>
>
>
|









>
|
>
>
>




|





<
<

<
>
|




>
|
>
>
>
>
|

|

<
<
<

|
>
>




|




|
|
|
>
|
>
>
>
>
>
>
>

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


 * 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 <string.h>

#import "OFXMLProcessingInstruction.h"
#import "OFString.h"
#import "OFXMLAttribute.h"
#import "OFXMLElement.h"
#import "OFXMLNode+Private.h"

#import "OFInvalidArgumentException.h"

@implementation OFXMLProcessingInstruction
@synthesize target = _target, data = _data;

+ (instancetype)processingInstructionWithTarget: (OFString *)target
					   data: (OFString *)data
{
	return [[[self alloc] initWithTarget: target
					data: data] autorelease];
}

- (instancetype)initWithTarget: (OFString *)target
			  data: (OFString *)data
{
	self = [super of_init];

	@try {
		_target = [target copy];
		_data = [data copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{


	@try {
		void *pool = objc_autoreleasePoolPush();
		OFXMLAttribute *targetAttr;

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OFSerializationNS])
			@throw [OFInvalidArgumentException exception];

		targetAttr = [element attributeForName: @"target"
					     namespace: OFSerializationNS];
		if (targetAttr.stringValue.length == 0)
			@throw [OFInvalidArgumentException exception];

		self = [self initWithTarget: targetAttr.stringValue
				       data: element.stringValue];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_target release];
	[_data release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFXMLProcessingInstruction *processingInstruction;

	if (object == self)
		return true;

	if (![object isKindOfClass: [OFXMLProcessingInstruction class]])
		return false;

	processingInstruction = object;

	if (![processingInstruction->_target isEqual: _target])
		return false;

	if (processingInstruction->_data != _data &&
	    ![processingInstruction->_data isEqual: _data])
		return false;

	return true;
}

- (unsigned long)hash
{
	unsigned long hash;

	OFHashInit(&hash);
	OFHashAddHash(&hash, _target.hash);
	OFHashAddHash(&hash, _data.hash);
	OFHashFinalize(&hash);

	return hash;
}

- (OFString *)stringValue
{
	return @"";
}

- (OFString *)XMLString
{
	if (_data.length > 0)
		return [OFString stringWithFormat: @"<?%@ %@?>",
						   _target, _data];
	else
		return [OFString stringWithFormat: @"<?%@?>", _target];
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
{
	return self.XMLString;
}

- (OFString *)XMLStringWithIndentation: (unsigned int)indentation
				 level: (unsigned int)level
{


	if (indentation > 0 && level > 0) {

		OFString *ret;
		char *whitespaces = OFAllocMemory((level * indentation) + 1, 1);
		memset(whitespaces, ' ', level * indentation);
		whitespaces[level * indentation] = 0;

		@try {
			if (_data.length > 0)
				ret = [OFString stringWithFormat:
				    @"%s<?%@ %@?>", whitespaces,
				    _target, _data];
			else
				ret = [OFString stringWithFormat:
				    @"%s<?%@?>", whitespaces, _target];
		} @finally {
			OFFreeMemory(whitespaces);
		}




		return ret;
	} else
		return self.XMLString;
}

- (OFString *)description
{
	return self.XMLString;
}

- (OFXMLElement *)XMLElementBySerializing
{
	OFXMLElement *ret = [OFXMLElement elementWithName: self.className
						namespace: OFSerializationNS
					      stringValue: _data];
	void *pool = objc_autoreleasePoolPush();

	[ret addAttribute: [OFXMLAttribute attributeWithName: @"target"
						 stringValue: _target]];

	objc_autoreleasePoolPop(pool);

	return ret;
}
@end

Modified src/OFZIPArchive.h from [e60923cb94] to [cd08e80056].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 * @brief A class for accessing and manipulating ZIP files.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFZIPArchive: OFObject
{
	OFStream *_stream;
	int64_t _offset;
	enum {
		OF_ZIP_ARCHIVE_MODE_READ,
		OF_ZIP_ARCHIVE_MODE_WRITE,
		OF_ZIP_ARCHIVE_MODE_APPEND
	} _mode;
	uint32_t _diskNumber, _centralDirectoryDisk;
	uint64_t _centralDirectoryEntriesInDisk, _centralDirectoryEntries;
	uint64_t _centralDirectorySize;
	int64_t _centralDirectoryOffset;
	OFString *_Nullable _archiveComment;
	OFMutableArray OF_GENERIC(OFZIPArchiveEntry *) *_entries;
	OFMutableDictionary OF_GENERIC(OFString *, OFZIPArchiveEntry *)







<
<
<
<
|







30
31
32
33
34
35
36




37
38
39
40
41
42
43
44
 * @brief A class for accessing and manipulating ZIP files.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFZIPArchive: OFObject
{
	OFStream *_stream;
	int64_t _offset;




	uint_least8_t _mode;
	uint32_t _diskNumber, _centralDirectoryDisk;
	uint64_t _centralDirectoryEntriesInDisk, _centralDirectoryEntries;
	uint64_t _centralDirectorySize;
	int64_t _centralDirectoryOffset;
	OFString *_Nullable _archiveComment;
	OFMutableArray OF_GENERIC(OFZIPArchiveEntry *) *_entries;
	OFMutableDictionary OF_GENERIC(OFString *, OFZIPArchiveEntry *)
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
 * @param stream A stream from which the ZIP archive will be read.
 *		 For read and append mode, this needs to be an OFSeekableStream.
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFZIPArchive
 */
+ (instancetype)archiveWithStream: (OFStream *)stream
			     mode: (OFString *)mode;

#ifdef OF_HAVE_FILES
/**
 * @brief Creates a new OFZIPArchive object with the specified file.
 *
 * @param path The path to the ZIP file
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFZIPArchive
 */
+ (instancetype)archiveWithPath: (OFString *)path
			   mode: (OFString *)mode;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFZIPArchive object with the
 *	  specified stream.







|
<











|
<







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
 * @param stream A stream from which the ZIP archive will be read.
 *		 For read and append mode, this needs to be an OFSeekableStream.
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFZIPArchive
 */
+ (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode;


#ifdef OF_HAVE_FILES
/**
 * @brief Creates a new OFZIPArchive object with the specified file.
 *
 * @param path The path to the ZIP file
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return A new, autoreleased OFZIPArchive
 */
+ (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode;

#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated OFZIPArchive object with the
 *	  specified stream.
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 *
 * @param path The path to the ZIP file
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return An initialized OFZIPArchive
 */
- (instancetype)initWithPath: (OFString *)path
			mode: (OFString *)mode;
#endif

/**
 * @brief Returns a stream for reading the specified file from the archive.
 *
 * @note This method is only available in read mode.
 *







|
<







110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
 *
 * @param path The path to the ZIP file
 * @param mode The mode for the ZIP file. Valid modes are "r" for reading,
 *	       "w" for creating a new file and "a" for appending to an existing
 *	       archive.
 * @return An initialized OFZIPArchive
 */
- (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode;

#endif

/**
 * @brief Returns a stream for reading the specified file from the archive.
 *
 * @note This method is only available in read mode.
 *
172
173
174
175
176
177
178
179











180
- (OFStream *)streamForWritingEntry: (OFZIPArchiveEntry *)entry;

/**
 * @brief Closes the OFZIPArchive.
 */
- (void)close;
@end












OF_ASSUME_NONNULL_END








>
>
>
>
>
>
>
>
>
>
>

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
- (OFStream *)streamForWritingEntry: (OFZIPArchiveEntry *)entry;

/**
 * @brief Closes the OFZIPArchive.
 */
- (void)close;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t OFZIPArchiveReadField32(const uint8_t *_Nonnull *_Nonnull,
    uint16_t *_Nonnull);
extern uint64_t OFZIPArchiveReadField64(const uint8_t *_Nonnull *_Nonnull,
    uint16_t *_Nonnull);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFZIPArchive.m from [6d9893fc0c] to [2bbedc5f4d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFZIPArchive.h"
#import "OFZIPArchiveEntry.h"
#import "OFZIPArchiveEntry+Private.h"

#import "OFData.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFStream.h"
#import "OFSeekableStream.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFInflateStream.h"
#import "OFInflate64Stream.h"

#import "crc32.h"

#import "OFChecksumMismatchException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOpenItemFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFSeekFailedException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedVersionException.h"


/*
 * FIXME: Current limitations:
 *  - Split archives are not supported.
 *  - Encrypted files cannot be read.
 */







OF_DIRECT_MEMBERS
@interface OFZIPArchive ()
- (void)of_readZIPInfo;
- (void)of_readEntries;
- (void)of_closeLastReturnedStream;
- (void)of_writeCentralDirectory;

<
<
|




















>











<
<










>






>
>
>
>
>
>







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


 * 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 "OFZIPArchive.h"
#import "OFZIPArchiveEntry.h"
#import "OFZIPArchiveEntry+Private.h"
#import "OFCRC32.h"
#import "OFData.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFStream.h"
#import "OFSeekableStream.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFInflateStream.h"
#import "OFInflate64Stream.h"



#import "OFChecksumMismatchException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOpenItemFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFSeekFailedException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedVersionException.h"
#import "OFWriteFailedException.h"

/*
 * FIXME: Current limitations:
 *  - Split archives are not supported.
 *  - Encrypted files cannot be read.
 */

enum {
	modeRead,
	modeWrite,
	modeAppend
};

OF_DIRECT_MEMBERS
@interface OFZIPArchive ()
- (void)of_readZIPInfo;
- (void)of_readEntries;
- (void)of_closeLastReturnedStream;
- (void)of_writeCentralDirectory;
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
}

- (instancetype)initWithStream: (OFStream *)stream
			 entry: (OFMutableZIPArchiveEntry *)entry;
@end

uint32_t
of_zip_archive_read_field32(const uint8_t **data, uint16_t *size)
{
	uint32_t field = 0;

	if (*size < 4)
		@throw [OFInvalidFormatException exception];

	for (uint8_t i = 0; i < 4; i++)
		field |= (uint32_t)(*data)[i] << (i * 8);

	*data += 4;
	*size -= 4;

	return field;
}

uint64_t
of_zip_archive_read_field64(const uint8_t **data, uint16_t *size)
{
	uint64_t field = 0;

	if (*size < 8)
		@throw [OFInvalidFormatException exception];

	for (uint8_t i = 0; i < 8; i++)
		field |= (uint64_t)(*data)[i] << (i * 8);

	*data += 8;
	*size -= 8;

	return field;
}

static void
seekOrThrowInvalidFormat(OFSeekableStream *stream,
    of_offset_t offset, int whence)
{
	@try {
		[stream seekToOffset: offset
			      whence: whence];
	} @catch (OFSeekFailedException *e) {
		if (e.errNo == EINVAL)
			@throw [OFInvalidFormatException exception];

		@throw e;
	}
}

@implementation OFZIPArchive
@synthesize archiveComment = _archiveComment;

+ (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
{
	self = [super init];

	@try {
		if ([mode isEqual: @"r"])
			_mode = OF_ZIP_ARCHIVE_MODE_READ;
		else if ([mode isEqual: @"w"])
			_mode = OF_ZIP_ARCHIVE_MODE_WRITE;
		else if ([mode isEqual: @"a"])
			_mode = OF_ZIP_ARCHIVE_MODE_APPEND;
		else
			@throw [OFInvalidArgumentException exception];

		_stream = [stream retain];
		_entries = [[OFMutableArray alloc] init];
		_pathToEntryMap = [[OFMutableDictionary alloc] init];

		if (_mode == OF_ZIP_ARCHIVE_MODE_READ ||
		    _mode == OF_ZIP_ARCHIVE_MODE_APPEND) {
			if (![stream isKindOfClass: [OFSeekableStream class]])
				@throw [OFInvalidArgumentException exception];

			[self of_readZIPInfo];
			[self of_readEntries];
		}

		if (_mode == OF_ZIP_ARCHIVE_MODE_APPEND) {
			_offset = _centralDirectoryOffset;
			seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
			    (of_offset_t)_offset, SEEK_SET);
		}
	} @catch (id e) {
		/*
		 * If we are in write or append mode, we do not want -[close]
		 * to write anything to it on error - after all, it might not
		 * be a ZIP file which we would destroy otherwise.
		 */
		[_stream release];
		_stream = nil;

		[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







|
















|

















|


|
<











|
<

|
<



|
<

|
<








|
<





|

|

|







|
<







|


|


















|
<




|
<

|
<


|
<







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
}

- (instancetype)initWithStream: (OFStream *)stream
			 entry: (OFMutableZIPArchiveEntry *)entry;
@end

uint32_t
OFZIPArchiveReadField32(const uint8_t **data, uint16_t *size)
{
	uint32_t field = 0;

	if (*size < 4)
		@throw [OFInvalidFormatException exception];

	for (uint8_t i = 0; i < 4; i++)
		field |= (uint32_t)(*data)[i] << (i * 8);

	*data += 4;
	*size -= 4;

	return field;
}

uint64_t
OFZIPArchiveReadField64(const uint8_t **data, uint16_t *size)
{
	uint64_t field = 0;

	if (*size < 8)
		@throw [OFInvalidFormatException exception];

	for (uint8_t i = 0; i < 8; i++)
		field |= (uint64_t)(*data)[i] << (i * 8);

	*data += 8;
	*size -= 8;

	return field;
}

static void
seekOrThrowInvalidFormat(OFSeekableStream *stream,
    OFFileOffset offset, int whence)
{
	@try {
		[stream seekToOffset: offset whence: whence];

	} @catch (OFSeekFailedException *e) {
		if (e.errNo == EINVAL)
			@throw [OFInvalidFormatException exception];

		@throw e;
	}
}

@implementation OFZIPArchive
@synthesize archiveComment = _archiveComment;

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

{
	self = [super init];

	@try {
		if ([mode isEqual: @"r"])
			_mode = modeRead;
		else if ([mode isEqual: @"w"])
			_mode = modeWrite;
		else if ([mode isEqual: @"a"])
			_mode = modeAppend;
		else
			@throw [OFInvalidArgumentException exception];

		_stream = [stream retain];
		_entries = [[OFMutableArray alloc] init];
		_pathToEntryMap = [[OFMutableDictionary alloc] init];

		if (_mode == modeRead || _mode == modeAppend) {

			if (![stream isKindOfClass: [OFSeekableStream class]])
				@throw [OFInvalidArgumentException exception];

			[self of_readZIPInfo];
			[self of_readEntries];
		}

		if (_mode == modeAppend) {
			_offset = _centralDirectoryOffset;
			seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
			    (OFFileOffset)_offset, SEEK_SET);
		}
	} @catch (id e) {
		/*
		 * If we are in write or append mode, we do not want -[close]
		 * to write anything to it on error - after all, it might not
		 * be a ZIP file which we would destroy otherwise.
		 */
		[_stream release];
		_stream = nil;

		[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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
	[super dealloc];
}

- (void)of_readZIPInfo
{
	void *pool = objc_autoreleasePoolPush();
	uint16_t commentLength;
	of_offset_t offset = -22;
	bool valid = false;

	do {
		seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
		    offset, SEEK_END);

		if ([_stream readLittleEndianInt32] == 0x06054B50) {







|







257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
	[super dealloc];
}

- (void)of_readZIPInfo
{
	void *pool = objc_autoreleasePoolPush();
	uint16_t commentLength;
	OFFileOffset offset = -22;
	bool valid = false;

	do {
		seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
		    offset, SEEK_END);

		if ([_stream readLittleEndianInt32] == 0x06054B50) {
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
	_centralDirectoryEntries = [_stream readLittleEndianInt16];
	_centralDirectorySize = [_stream readLittleEndianInt32];
	_centralDirectoryOffset = [_stream readLittleEndianInt32];

	commentLength = [_stream readLittleEndianInt16];
	_archiveComment = [[_stream
	    readStringWithLength: commentLength
			encoding: OF_STRING_ENCODING_CODEPAGE_437] copy];

	if (_diskNumber == 0xFFFF ||
	    _centralDirectoryDisk == 0xFFFF ||
	    _centralDirectoryEntriesInDisk == 0xFFFF ||
	    _centralDirectoryEntries == 0xFFFF ||
	    _centralDirectorySize == 0xFFFFFFFF ||
	    _centralDirectoryOffset == 0xFFFFFFFF) {







|







283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
	_centralDirectoryEntries = [_stream readLittleEndianInt16];
	_centralDirectorySize = [_stream readLittleEndianInt32];
	_centralDirectoryOffset = [_stream readLittleEndianInt32];

	commentLength = [_stream readLittleEndianInt16];
	_archiveComment = [[_stream
	    readStringWithLength: commentLength
			encoding: OFStringEncodingCodepage437] copy];

	if (_diskNumber == 0xFFFF ||
	    _centralDirectoryDisk == 0xFFFF ||
	    _centralDirectoryEntriesInDisk == 0xFFFF ||
	    _centralDirectoryEntries == 0xFFFF ||
	    _centralDirectorySize == 0xFFFFFFFF ||
	    _centralDirectoryOffset == 0xFFFFFFFF) {
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
		/*
		 * FIXME: Handle number of the disk containing ZIP64 end of
		 * central directory record.
		 */
		[_stream readLittleEndianInt32];
		offset64 = [_stream readLittleEndianInt64];

		if (offset64 < 0 || (of_offset_t)offset64 != offset64)
			@throw [OFOutOfRangeException exception];

		seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
		    (of_offset_t)offset64, SEEK_SET);

		if ([_stream readLittleEndianInt32] != 0x06064B50)
			@throw [OFInvalidFormatException exception];

		size = [_stream readLittleEndianInt64];
		if (size < 44)
			@throw [OFInvalidFormatException exception];







|



|







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
		/*
		 * FIXME: Handle number of the disk containing ZIP64 end of
		 * central directory record.
		 */
		[_stream readLittleEndianInt32];
		offset64 = [_stream readLittleEndianInt64];

		if (offset64 < 0 || (OFFileOffset)offset64 != offset64)
			@throw [OFOutOfRangeException exception];

		seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
		    (OFFileOffset)offset64, SEEK_SET);

		if ([_stream readLittleEndianInt32] != 0x06064B50)
			@throw [OFInvalidFormatException exception];

		size = [_stream readLittleEndianInt64];
		if (size < 44)
			@throw [OFInvalidFormatException exception];
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
		_centralDirectoryEntriesInDisk =
		    [_stream readLittleEndianInt64];
		_centralDirectoryEntries = [_stream readLittleEndianInt64];
		_centralDirectorySize = [_stream readLittleEndianInt64];
		_centralDirectoryOffset = [_stream readLittleEndianInt64];

		if (_centralDirectoryOffset < 0 ||
		    (of_offset_t)_centralDirectoryOffset !=
		    _centralDirectoryOffset)
			@throw [OFOutOfRangeException exception];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)of_readEntries
{
	void *pool = objc_autoreleasePoolPush();

	if (_centralDirectoryOffset < 0 ||
	    (of_offset_t)_centralDirectoryOffset != _centralDirectoryOffset)
		@throw [OFOutOfRangeException exception];

	seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
	    (of_offset_t)_centralDirectoryOffset, SEEK_SET);

	for (size_t i = 0; i < _centralDirectoryEntries; i++) {
		OFZIPArchiveEntry *entry = [[[OFZIPArchiveEntry alloc]
		    of_initWithStream: _stream] autorelease];

		if ([_pathToEntryMap objectForKey: entry.fileName] != nil)
			@throw [OFInvalidFormatException exception];

		[_entries addObject: entry];
		[_pathToEntryMap setObject: entry
				    forKey: entry.fileName];
	}

	objc_autoreleasePoolPop(pool);
}

- (OFArray *)entries
{







|












|



|









|
<







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
		_centralDirectoryEntriesInDisk =
		    [_stream readLittleEndianInt64];
		_centralDirectoryEntries = [_stream readLittleEndianInt64];
		_centralDirectorySize = [_stream readLittleEndianInt64];
		_centralDirectoryOffset = [_stream readLittleEndianInt64];

		if (_centralDirectoryOffset < 0 ||
		    (OFFileOffset)_centralDirectoryOffset !=
		    _centralDirectoryOffset)
			@throw [OFOutOfRangeException exception];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)of_readEntries
{
	void *pool = objc_autoreleasePoolPush();

	if (_centralDirectoryOffset < 0 ||
	    (OFFileOffset)_centralDirectoryOffset != _centralDirectoryOffset)
		@throw [OFOutOfRangeException exception];

	seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
	    (OFFileOffset)_centralDirectoryOffset, SEEK_SET);

	for (size_t i = 0; i < _centralDirectoryEntries; i++) {
		OFZIPArchiveEntry *entry = [[[OFZIPArchiveEntry alloc]
		    of_initWithStream: _stream] autorelease];

		if ([_pathToEntryMap objectForKey: entry.fileName] != nil)
			@throw [OFInvalidFormatException exception];

		[_entries addObject: entry];
		[_pathToEntryMap setObject: entry forKey: entry.fileName];

	}

	objc_autoreleasePoolPop(pool);
}

- (OFArray *)entries
{
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
{
	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}

	if ((_mode == OF_ZIP_ARCHIVE_MODE_WRITE ||
	    _mode == OF_ZIP_ARCHIVE_MODE_APPEND) &&
	    [_lastReturnedStream isKindOfClass:
	    [OFZIPArchiveFileWriteStream class]]) {
		OFZIPArchiveFileWriteStream *stream =
		    (OFZIPArchiveFileWriteStream *)_lastReturnedStream;

		if (INT64_MAX - _offset < stream->_bytesWritten)
			@throw [OFOutOfRangeException exception];







|
<







402
403
404
405
406
407
408
409

410
411
412
413
414
415
416
{
	@try {
		[_lastReturnedStream close];
	} @catch (OFNotOpenException *e) {
		/* Might have already been closed by the user - that's fine. */
	}

	if ((_mode == modeWrite || _mode == modeAppend) &&

	    [_lastReturnedStream isKindOfClass:
	    [OFZIPArchiveFileWriteStream class]]) {
		OFZIPArchiveFileWriteStream *stream =
		    (OFZIPArchiveFileWriteStream *)_lastReturnedStream;

		if (INT64_MAX - _offset < stream->_bytesWritten)
			@throw [OFOutOfRangeException exception];
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
- (OFStream *)streamForReadingFile: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFZIPArchiveEntry *entry;
	OFZIPArchiveLocalFileHeader *localFileHeader;
	int64_t offset64;




	if (_mode != OF_ZIP_ARCHIVE_MODE_READ)
		@throw [OFInvalidArgumentException exception];

	if ((entry = [_pathToEntryMap objectForKey: path]) == nil)
		@throw [OFOpenItemFailedException exceptionWithPath: path
							       mode: @"r"
							      errNo: ENOENT];

	[self of_closeLastReturnedStream];

	offset64 = entry.of_localFileHeaderOffset;
	if (offset64 < 0 || (of_offset_t)offset64 != offset64)
		@throw [OFOutOfRangeException exception];

	seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
	    (of_offset_t)offset64, SEEK_SET);
	localFileHeader = [[[OFZIPArchiveLocalFileHeader alloc]
	    initWithStream: _stream] autorelease];

	if (![localFileHeader matchesEntry: entry])
		@throw [OFInvalidFormatException exception];

	if ((localFileHeader->_minVersionNeeded & 0xFF) > 45) {







>
>
>
|










|



|







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
- (OFStream *)streamForReadingFile: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFZIPArchiveEntry *entry;
	OFZIPArchiveLocalFileHeader *localFileHeader;
	int64_t offset64;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_mode != modeRead)
		@throw [OFInvalidArgumentException exception];

	if ((entry = [_pathToEntryMap objectForKey: path]) == nil)
		@throw [OFOpenItemFailedException exceptionWithPath: path
							       mode: @"r"
							      errNo: ENOENT];

	[self of_closeLastReturnedStream];

	offset64 = entry.of_localFileHeaderOffset;
	if (offset64 < 0 || (OFFileOffset)offset64 != offset64)
		@throw [OFOutOfRangeException exception];

	seekOrThrowInvalidFormat((OFSeekableStream *)_stream,
	    (OFFileOffset)offset64, SEEK_SET);
	localFileHeader = [[[OFZIPArchiveLocalFileHeader alloc]
	    initWithStream: _stream] autorelease];

	if (![localFileHeader matchesEntry: entry])
		@throw [OFInvalidFormatException exception];

	if ((localFileHeader->_minVersionNeeded & 0xFF) > 45) {
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
	int64_t offsetAdd = 0;
	void *pool;
	OFMutableZIPArchiveEntry *entry;
	OFString *fileName;
	OFData *extraField;
	uint16_t fileNameLength, extraFieldLength;



	if (_mode != OF_ZIP_ARCHIVE_MODE_WRITE &&
	    _mode != OF_ZIP_ARCHIVE_MODE_APPEND)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();
	entry = [[entry_ mutableCopy] autorelease];

	if ([_pathToEntryMap objectForKey: entry.fileName] != nil)
		@throw [OFOpenItemFailedException
		    exceptionWithPath: entry.fileName
				 mode: @"w"
				errNo: EEXIST];

	if (entry.compressionMethod !=
	    OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_NONE)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	[self of_closeLastReturnedStream];

	fileName = entry.fileName;
	fileNameLength = fileName.UTF8StringLength;







>
>
|
|











|
<







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
	int64_t offsetAdd = 0;
	void *pool;
	OFMutableZIPArchiveEntry *entry;
	OFString *fileName;
	OFData *extraField;
	uint16_t fileNameLength, extraFieldLength;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_mode != modeWrite && _mode != modeAppend)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();
	entry = [[entry_ mutableCopy] autorelease];

	if ([_pathToEntryMap objectForKey: entry.fileName] != nil)
		@throw [OFOpenItemFailedException
		    exceptionWithPath: entry.fileName
				 mode: @"w"
				errNo: EEXIST];

	if (entry.compressionMethod != OFZIPArchiveEntryCompressionMethodNone)

		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	[self of_closeLastReturnedStream];

	fileName = entry.fileName;
	fileNameLength = fileName.UTF8StringLength;
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
	[_stream writeLittleEndianInt16: fileNameLength];
	[_stream writeLittleEndianInt16: extraFieldLength + 20];
	offsetAdd += 4 + (5 * 2) + (3 * 4) + (2 * 2);

	[_stream writeString: fileName];
	offsetAdd += fileNameLength;

	[_stream writeLittleEndianInt16:
	    OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64];
	[_stream writeLittleEndianInt16: 16];
	/* We use the data descriptor */
	[_stream writeLittleEndianInt64: 0];
	[_stream writeLittleEndianInt64: 0];
	offsetAdd += (2 * 2) + (2 * 8);

	if (extraField != nil)







|
<







539
540
541
542
543
544
545
546

547
548
549
550
551
552
553
	[_stream writeLittleEndianInt16: fileNameLength];
	[_stream writeLittleEndianInt16: extraFieldLength + 20];
	offsetAdd += 4 + (5 * 2) + (3 * 4) + (2 * 2);

	[_stream writeString: fileName];
	offsetAdd += fileNameLength;

	[_stream writeLittleEndianInt16: OFZIPArchiveEntryExtraFieldTagZIP64];

	[_stream writeLittleEndianInt16: 16];
	/* We use the data descriptor */
	[_stream writeLittleEndianInt64: 0];
	[_stream writeLittleEndianInt64: 0];
	offsetAdd += (2 * 2) + (2 * 8);

	if (extraField != nil)
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
- (void)close
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	[self of_closeLastReturnedStream];

	if (_mode == OF_ZIP_ARCHIVE_MODE_WRITE ||
	    _mode == OF_ZIP_ARCHIVE_MODE_APPEND)
		[self of_writeCentralDirectory];

	[_stream release];
	_stream = nil;
}
@end

@implementation OFZIPArchiveLocalFileHeader
- (instancetype)initWithStream: (OFStream *)stream
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFMutableData *extraField = nil;
		uint16_t fileNameLength, extraFieldLength;
		of_string_encoding_t encoding;
		size_t ZIP64Index;
		uint16_t ZIP64Size;

		if ([stream readLittleEndianInt32] != 0x04034B50)
			@throw [OFInvalidFormatException exception];

		_minVersionNeeded = [stream readLittleEndianInt16];
		_generalPurposeBitFlag = [stream readLittleEndianInt16];
		_compressionMethod = [stream readLittleEndianInt16];
		_lastModifiedFileTime = [stream readLittleEndianInt16];
		_lastModifiedFileDate = [stream readLittleEndianInt16];
		_CRC32 = [stream readLittleEndianInt32];
		_compressedSize = [stream readLittleEndianInt32];
		_uncompressedSize = [stream readLittleEndianInt32];
		fileNameLength = [stream readLittleEndianInt16];
		extraFieldLength = [stream readLittleEndianInt16];
		encoding = (_generalPurposeBitFlag & (1u << 11)
		    ? OF_STRING_ENCODING_UTF_8
		    : OF_STRING_ENCODING_CODEPAGE_437);

		_fileName = [[stream readStringWithLength: fileNameLength
						 encoding: encoding] copy];
		if (extraFieldLength > 0)
			extraField = [[[stream readDataWithCount:
			    extraFieldLength] mutableCopy] autorelease];

		ZIP64Index = of_zip_archive_entry_extra_field_find(extraField,
		    OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64, &ZIP64Size);

		if (ZIP64Index != OF_NOT_FOUND) {
			const uint8_t *ZIP64 =
			    [extraField itemAtIndex: ZIP64Index];
			of_range_t range =
			    of_range(ZIP64Index - 4, ZIP64Size + 4);

			if (_uncompressedSize == 0xFFFFFFFF)
				_uncompressedSize = of_zip_archive_read_field64(
				    &ZIP64, &ZIP64Size);
			if (_compressedSize == 0xFFFFFFFF)
				_compressedSize = of_zip_archive_read_field64(
				    &ZIP64, &ZIP64Size);

			if (ZIP64Size > 0)
				@throw [OFInvalidFormatException exception];

			[extraField removeItemsInRange: range];
		}







|
<
















|

















|
<







|
|

|


|
|


|


|







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
- (void)close
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	[self of_closeLastReturnedStream];

	if (_mode == modeWrite || _mode == modeAppend)

		[self of_writeCentralDirectory];

	[_stream release];
	_stream = nil;
}
@end

@implementation OFZIPArchiveLocalFileHeader
- (instancetype)initWithStream: (OFStream *)stream
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFMutableData *extraField = nil;
		uint16_t fileNameLength, extraFieldLength;
		OFStringEncoding encoding;
		size_t ZIP64Index;
		uint16_t ZIP64Size;

		if ([stream readLittleEndianInt32] != 0x04034B50)
			@throw [OFInvalidFormatException exception];

		_minVersionNeeded = [stream readLittleEndianInt16];
		_generalPurposeBitFlag = [stream readLittleEndianInt16];
		_compressionMethod = [stream readLittleEndianInt16];
		_lastModifiedFileTime = [stream readLittleEndianInt16];
		_lastModifiedFileDate = [stream readLittleEndianInt16];
		_CRC32 = [stream readLittleEndianInt32];
		_compressedSize = [stream readLittleEndianInt32];
		_uncompressedSize = [stream readLittleEndianInt32];
		fileNameLength = [stream readLittleEndianInt16];
		extraFieldLength = [stream readLittleEndianInt16];
		encoding = (_generalPurposeBitFlag & (1u << 11)
		    ? OFStringEncodingUTF8 : OFStringEncodingCodepage437);


		_fileName = [[stream readStringWithLength: fileNameLength
						 encoding: encoding] copy];
		if (extraFieldLength > 0)
			extraField = [[[stream readDataWithCount:
			    extraFieldLength] mutableCopy] autorelease];

		ZIP64Index = OFZIPArchiveEntryExtraFieldFind(extraField,
		    OFZIPArchiveEntryExtraFieldTagZIP64, &ZIP64Size);

		if (ZIP64Index != OFNotFound) {
			const uint8_t *ZIP64 =
			    [extraField itemAtIndex: ZIP64Index];
			OFRange range =
			    OFRangeMake(ZIP64Index - 4, ZIP64Size + 4);

			if (_uncompressedSize == 0xFFFFFFFF)
				_uncompressedSize = OFZIPArchiveReadField64(
				    &ZIP64, &ZIP64Size);
			if (_compressedSize == 0xFFFFFFFF)
				_compressedSize = OFZIPArchiveReadField64(
				    &ZIP64, &ZIP64Size);

			if (ZIP64Size > 0)
				@throw [OFInvalidFormatException exception];

			[extraField removeItemsInRange: range];
		}
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
{
	self = [super init];

	@try {
		_stream = [stream retain];

		switch (entry.compressionMethod) {
		case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_NONE:
			_decompressedStream = [stream retain];
			break;
		case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE:
			_decompressedStream = [[OFInflateStream alloc]
			    initWithStream: stream];
			break;
		case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE64:
			_decompressedStream = [[OFInflate64Stream alloc]
			    initWithStream: stream];
			break;
		default:
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: nil];







|


|



|







737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
{
	self = [super init];

	@try {
		_stream = [stream retain];

		switch (entry.compressionMethod) {
		case OFZIPArchiveEntryCompressionMethodNone:
			_decompressedStream = [stream retain];
			break;
		case OFZIPArchiveEntryCompressionMethodDeflate:
			_decompressedStream = [[OFInflateStream alloc]
			    initWithStream: stream];
			break;
		case OFZIPArchiveEntryCompressionMethodDeflate64:
			_decompressedStream = [[OFInflate64Stream alloc]
			    initWithStream: stream];
			break;
		default:
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: nil];
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
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	size_t ret;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_atEndOfStream)
		return 0;

	if (_stream.atEndOfStream && !_decompressedStream.hasDataInReadBuffer)
		@throw [OFTruncatedDataException exception];

#if SIZE_MAX >= UINT64_MAX
	if (length > UINT64_MAX)
		@throw [OFOutOfRangeException exception];
#endif

	if ((uint64_t)length > _toRead)
		length = (size_t)_toRead;

	ret = [_decompressedStream readIntoBuffer: buffer
					   length: length];

	_toRead -= ret;
	_CRC32 = of_crc32(_CRC32, buffer, ret);

	if (_toRead == 0) {
		_atEndOfStream = true;

		if (~_CRC32 != _entry.CRC32) {
			OFString *actualChecksum = [OFString stringWithFormat:
			    @"%08" PRIX32, ~_CRC32];







|
<




















|
<


|







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
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	size_t ret;

	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_atEndOfStream)
		return 0;

	if (_stream.atEndOfStream && !_decompressedStream.hasDataInReadBuffer)
		@throw [OFTruncatedDataException exception];

#if SIZE_MAX >= UINT64_MAX
	if (length > UINT64_MAX)
		@throw [OFOutOfRangeException exception];
#endif

	if ((uint64_t)length > _toRead)
		length = (size_t)_toRead;

	ret = [_decompressedStream readIntoBuffer: buffer length: length];


	_toRead -= ret;
	_CRC32 = OFCRC32(_CRC32, buffer, ret);

	if (_toRead == 0) {
		_atEndOfStream = true;

		if (~_CRC32 != _entry.CRC32) {
			OFString *actualChecksum = [OFString stringWithFormat:
			    @"%08" PRIX32, ~_CRC32];
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
		[self close];

	[_entry release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	size_t bytesWritten;

#if SIZE_MAX >= INT64_MAX
	if (length > INT64_MAX)
		@throw [OFOutOfRangeException exception];
#endif

	if (INT64_MAX - _bytesWritten < (int64_t)length)
		@throw [OFOutOfRangeException exception];


	bytesWritten = [_stream writeBuffer: buffer


				     length: length];









	_bytesWritten += (int64_t)bytesWritten;
	_CRC32 = of_crc32(_CRC32, buffer, length);

	return bytesWritten;
}

- (void)close
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];








|
<

<
<








>
|
>
>
|
>
>

>
>
>
>
>
>
|
|

|







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
		[self close];

	[_entry release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{


#if SIZE_MAX >= INT64_MAX
	if (length > INT64_MAX)
		@throw [OFOutOfRangeException exception];
#endif

	if (INT64_MAX - _bytesWritten < (int64_t)length)
		@throw [OFOutOfRangeException exception];

	@try {
		[_stream writeBuffer: buffer length: length];
	} @catch (OFWriteFailedException *e) {
		OFEnsure(e.bytesWritten <= length);

		_bytesWritten += (int64_t)e.bytesWritten;
		_CRC32 = OFCRC32(_CRC32, buffer, e.bytesWritten);

		if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN)
			return e.bytesWritten;

		@throw e;
	}

	_bytesWritten += (int64_t)length;
	_CRC32 = OFCRC32(_CRC32, buffer, length);

	return length;
}

- (void)close
{
	if (_stream == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

Modified src/OFZIPArchiveEntry+Private.h from [19e5b08dac] to [9241ef29c2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/OFZIPArchiveEntry.h from [b2722b6f23] to [053b7bad8d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

OF_ASSUME_NONNULL_BEGIN

/** @file */

enum {
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_NONE		=  0,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_SHRINK		=  1,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_REDUCE_FACTOR_1 =  2,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_REDUCE_FACTOR_2 =  3,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_REDUCE_FACTOR_3 =  4,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_REDUCE_FACTOR_4 =  5,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_IMPLODE		=  6,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE		=  8,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE64	=  9,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_BZIP2		= 12,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_LZMA		= 14,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_WAVPACK		= 97,
	OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_PPMD		= 98
};

/**
 * @brief Attribute compatibility part of ZIP versions.
 */
enum of_zip_archive_entry_attribute_compatibility {
	/** MS-DOS and OS/2 */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_MSDOS	       =  0,
	/** Amiga */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_AMIGA	       =  1,
	/** OpenVMS */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_OPENVMS       =  2,
	/** UNIX */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_UNIX	       =  3,
	/** VM/CMS */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_VM_CMS	       =  4,
	/** Atari ST */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_ATARI_ST      =  5,
	/** OS/2 HPFS */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_OS2_HPFS      =  6,
	/** Macintosh */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_MACINTOSH     =  7,
	/** Z-System */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_Z_SYSTEM      =  8,
	/** CP/M */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_CP_M	       =  9,
	/** Windows NTFS */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_WINDOWS_NTFS  = 10,
	/** MVS (OS/390 - Z/OS) */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_MVS	       = 11,
	/** VSE */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_VSE	       = 12,
	/** Acorn RISC OS */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_ACORN_RISC_OS = 13,
	/** VFAT */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_VFAT	       = 14,
	/** Alternate MVS */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_ALTERNATE_MVS = 15,
	/** BeOS */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_BEOS	       = 16,
	/** Tandem */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_TANDEM	       = 17,
	/** OS/400 */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_OS_400	       = 18,
	/** OS X (Darwin) */
	OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_OS_X	       = 19
};




enum {

	OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64 = 0x0001
};


@class OFString;
@class OFData;
@class OFFile;
@class OFDate;

/**
 * @class OFZIPArchiveEntry OFZIPArchiveEntry.h ObjFW/OFZIPArchiveEntry.h
 *
 * @brief A class which represents an entry in the central directory of a ZIP
 *	  archive.
 */
@interface OFZIPArchiveEntry: OFObject <OFCopying, OFMutableCopying>
{


	uint16_t _versionMadeBy, _minVersionNeeded, _generalPurposeBitFlag;
	uint16_t _compressionMethod;
	uint16_t _lastModifiedFileTime, _lastModifiedFileDate;
	uint32_t _CRC32;
	uint64_t _compressedSize, _uncompressedSize;
	OFString *_fileName;
	OFData *_Nullable _extraField;
	OFString *_Nullable _fileComment;
	uint32_t _startDiskNumber;

<
<
|



















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




|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|
|

>
>
>
|
>
|
<
>














>
>
|
|







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


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

OF_ASSUME_NONNULL_BEGIN

/** @file */

typedef enum {
	OFZIPArchiveEntryCompressionMethodNone		=  0,
	OFZIPArchiveEntryCompressionMethodShrink	=  1,
	OFZIPArchiveEntryCompressionMethodReduceFactor1 =  2,
	OFZIPArchiveEntryCompressionMethodReduceFactor2 =  3,
	OFZIPArchiveEntryCompressionMethodReduceFactor3 =  4,
	OFZIPArchiveEntryCompressionMethodReduceFactor4 =  5,
	OFZIPArchiveEntryCompressionMethodImplode	=  6,
	OFZIPArchiveEntryCompressionMethodDeflate	=  8,
	OFZIPArchiveEntryCompressionMethodDeflate64	=  9,
	OFZIPArchiveEntryCompressionMethodBZIP2		= 12,
	OFZIPArchiveEntryCompressionMethodLZMA		= 14,
	OFZIPArchiveEntryCompressionMethodWavPack	= 97,
	OFZIPArchiveEntryCompressionMethodPPMd		= 98
} OFZIPArchiveEntryCompressionMethod;

/**
 * @brief Attribute compatibility part of ZIP versions.
 */
typedef enum {
	/** MS-DOS and OS/2 */
	OFZIPArchiveEntryAttributeCompatibilityMSDOS	    =  0,
	/** Amiga */
	OFZIPArchiveEntryAttributeCompatibilityAmiga	    =  1,
	/** OpenVMS */
	OFZIPArchiveEntryAttributeCompatibilityOpenVMS	    =  2,
	/** UNIX */
	OFZIPArchiveEntryAttributeCompatibilityUNIX	    =  3,
	/** VM/CMS */
	OFZIPArchiveEntryAttributeCompatibilityVM_CMS	    =  4,
	/** Atari ST */
	OFZIPArchiveEntryAttributeCompatibilityAtariST	    =  5,
	/** OS/2 HPFS */
	OFZIPArchiveEntryAttributeCompatibilityOS2HPFS	    =  6,
	/** Macintosh */
	OFZIPArchiveEntryAttributeCompatibilityMacintosh    =  7,
	/** Z-System */
	OFZIPArchiveEntryAttributeCompatibilityZSystem	    =  8,
	/** CP/M */
	OFZIPArchiveEntryAttributeCompatibilityCPM	    =  9,
	/** Windows NTFS */
	OFZIPArchiveEntryAttributeCompatibilityWindowsNTFS  = 10,
	/** MVS (OS/390 - Z/OS) */
	OFZIPArchiveEntryAttributeCompatibilityMVS	    = 11,
	/** VSE */
	OFZIPArchiveEntryAttributeCompatibilityVSE	    = 12,
	/** Acorn RISC OS */
	OFZIPArchiveEntryAttributeCompatibilityAcornRISCOS  = 13,
	/** VFAT */
	OFZIPArchiveEntryAttributeCompatibilityVFAT	    = 14,
	/** Alternate MVS */
	OFZIPArchiveEntryAttributeCompatibilityAlternateMVS = 15,
	/** BeOS */
	OFZIPArchiveEntryAttributeCompatibilityBeOS	    = 16,
	/** Tandem */
	OFZIPArchiveEntryAttributeCompatibilityTandem	    = 17,
	/** OS/400 */
	OFZIPArchiveEntryAttributeCompatibilityOS400	    = 18,
	/** OS X (Darwin) */
	OFZIPArchiveEntryAttributeCompatibilityOSX	    = 19
} OFZIPArchiveEntryAttributeCompatibility;

/**
 * @brief Tags for the extra field.
 */
typedef enum {
	/** ZIP64 extra field tag */
	OFZIPArchiveEntryExtraFieldTagZIP64 = 0x0001

} OFZIPArchiveEntryExtraFieldTag;

@class OFString;
@class OFData;
@class OFFile;
@class OFDate;

/**
 * @class OFZIPArchiveEntry OFZIPArchiveEntry.h ObjFW/OFZIPArchiveEntry.h
 *
 * @brief A class which represents an entry in the central directory of a ZIP
 *	  archive.
 */
@interface OFZIPArchiveEntry: OFObject <OFCopying, OFMutableCopying>
{
	OFZIPArchiveEntryAttributeCompatibility _versionMadeBy;
	OFZIPArchiveEntryAttributeCompatibility _minVersionNeeded;
	uint16_t _generalPurposeBitFlag;
	OFZIPArchiveEntryCompressionMethod _compressionMethod;
	uint16_t _lastModifiedFileTime, _lastModifiedFileDate;
	uint32_t _CRC32;
	uint64_t _compressedSize, _uncompressedSize;
	OFString *_fileName;
	OFData *_Nullable _extraField;
	OFString *_Nullable _fileComment;
	uint32_t _startDiskNumber;
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
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFData *extraField;

/**
 * @brief The version which made the entry.
 *
 * The lower 8 bits are the ZIP specification version.@n
 * The upper 8 bits are the attribute compatibility.
 * See @ref of_zip_archive_entry_attribute_compatibility.
 */
@property (readonly, nonatomic) uint16_t versionMadeBy;


/**
 * @brief The minimum version required to extract the file.
 *
 * The lower 8 bits are the ZIP specification version.@n
 * The upper 8 bits are the attribute compatibility.
 * See @ref of_zip_archive_entry_attribute_compatibility.
 */
@property (readonly, nonatomic) uint16_t minVersionNeeded;


/**
 * @brief The last modification date of the entry's file.
 *
 * @note Due to limitations of the ZIP format, this has only 2 second precision.
 */
@property (readonly, retain, nonatomic) OFDate *modificationDate;

/**
 * @brief The compression method of the entry.
 *
 * Supported values are:
 * Value                                             | Description
 * --------------------------------------------------|---------------
 * OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_NONE      | No compression
 * OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE   | Deflate
 * OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE64 | Deflate64
 *
 * Other values may be returned, but the file cannot be extracted then.
 */
@property (readonly, nonatomic) uint16_t compressionMethod;


/**
 * @brief The compressed size of the entry's file.
 */
@property (readonly, nonatomic) uint64_t compressedSize;

/**







|

|
>






|

|
>












|
|
|
|
|



|
>







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
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFData *extraField;

/**
 * @brief The version which made the entry.
 *
 * The lower 8 bits are the ZIP specification version.@n
 * The upper 8 bits are the attribute compatibility.
 * See @ref OFZIPArchiveEntryAttributeCompatibility.
 */
@property (readonly, nonatomic)
    OFZIPArchiveEntryAttributeCompatibility versionMadeBy;

/**
 * @brief The minimum version required to extract the file.
 *
 * The lower 8 bits are the ZIP specification version.@n
 * The upper 8 bits are the attribute compatibility.
 * See @ref OFZIPArchiveEntryAttributeCompatibility.
 */
@property (readonly, nonatomic)
    OFZIPArchiveEntryAttributeCompatibility minVersionNeeded;

/**
 * @brief The last modification date of the entry's file.
 *
 * @note Due to limitations of the ZIP format, this has only 2 second precision.
 */
@property (readonly, retain, nonatomic) OFDate *modificationDate;

/**
 * @brief The compression method of the entry.
 *
 * Supported values are:
 * Value                                       | Description
 * --------------------------------------------|---------------
 * OFZIPArchiveEntryCompressionMethodNone      | No compression
 * OFZIPArchiveEntryCompressionMethodDeflate   | Deflate
 * OFZIPArchiveEntryCompressionMethodDeflate64 | Deflate64
 *
 * Other values may be returned, but the file cannot be extracted then.
 */
@property (readonly, nonatomic)
    OFZIPArchiveEntryCompressionMethod compressionMethod;

/**
 * @brief The compressed size of the entry's file.
 */
@property (readonly, nonatomic) uint64_t compressedSize;

/**
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
#endif
/**
 * @brief Converts the ZIP entry version to a string.
 *
 * @param version The ZIP entry version to convert to a string
 * @return The ZIP entry version as a string
 */
extern OFString *of_zip_archive_entry_version_to_string(uint16_t version);

/**
 * @brief Convers the ZIP entry compression method to a string.
 *
 * @param compressionMethod The ZIP entry compression method to convert to a
 *			    string
 * @return The ZIP entry compression method as a string
 */
extern OFString *of_zip_archive_entry_compression_method_to_string(
    uint16_t compressionMethod);

/**
 * @brief Gets a pointer to and the size of the extensible data field with the
 *	  specified tag.
 *
 * @param extraField The extra field to search for an extensible data field with
 *		     the specified tag
 * @param tag The tag to look for
 * @param size A pointer to an uint16_t that should be set to the size
 * @return The index at which the extra field content starts in the OFData, or
 *	   OF_NOT_FOUND
 */
extern size_t of_zip_archive_entry_extra_field_find(OFData *extraField,
    uint16_t tag, uint16_t *size);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#import "OFMutableZIPArchiveEntry.h"







|








|
|










|

|
|







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
#endif
/**
 * @brief Converts the ZIP entry version to a string.
 *
 * @param version The ZIP entry version to convert to a string
 * @return The ZIP entry version as a string
 */
extern OFString *OFZIPArchiveEntryVersionToString(uint16_t version);

/**
 * @brief Convers the ZIP entry compression method to a string.
 *
 * @param compressionMethod The ZIP entry compression method to convert to a
 *			    string
 * @return The ZIP entry compression method as a string
 */
extern OFString *OFZIPArchiveEntryCompressionMethodName(
    OFZIPArchiveEntryCompressionMethod compressionMethod);

/**
 * @brief Gets a pointer to and the size of the extensible data field with the
 *	  specified tag.
 *
 * @param extraField The extra field to search for an extensible data field with
 *		     the specified tag
 * @param tag The tag to look for
 * @param size A pointer to an uint16_t that should be set to the size
 * @return The index at which the extra field content starts in the OFData, or
 *	   `OFNotFound`
 */
extern size_t OFZIPArchiveEntryExtraFieldFind(OFData *extraField,
    OFZIPArchiveEntryExtraFieldTag tag, uint16_t *size);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#import "OFMutableZIPArchiveEntry.h"

Modified src/OFZIPArchiveEntry.m from [17d05978cb] to [6b838273dc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFStream.h"
#import "OFString.h"

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

extern uint32_t of_zip_archive_read_field32(const uint8_t **, uint16_t *);
extern uint64_t of_zip_archive_read_field64(const uint8_t **, uint16_t *);

OFString *
of_zip_archive_entry_version_to_string(uint16_t version)
{
	const char *attrCompat = NULL;

	switch (version >> 8) {
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_MSDOS:
		attrCompat = "MS-DOS or OS/2";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_AMIGA:
		attrCompat = "Amiga";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_OPENVMS:
		attrCompat = "OpenVMS";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_UNIX:
		attrCompat = "UNIX";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_VM_CMS:
		attrCompat = "VM/CMS";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_ATARI_ST:
		attrCompat = "Atari ST";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_OS2_HPFS:
		attrCompat = "OS/2 HPFS";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_MACINTOSH:
		attrCompat = "Macintosh";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_Z_SYSTEM:
		attrCompat = "Z-System";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_CP_M:
		attrCompat = "CP/M";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_WINDOWS_NTFS:
		attrCompat = "Windows NTFS";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_MVS:
		attrCompat = "MVS (OS/390 - Z/OS)";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_VSE:
		attrCompat = "VSE";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_ACORN_RISC_OS:
		attrCompat = "Acorn RISC OS";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_VFAT:
		attrCompat = "VFAT";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_ALTERNATE_MVS:
		attrCompat = "Alternate MVS";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_BEOS:
		attrCompat = "BeOS";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_TANDEM:
		attrCompat = "Tandem";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_OS_400:
		attrCompat = "OS/400";
		break;
	case OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_OS_X:
		attrCompat = "OS X (Darwin)";
		break;
	}

	if (attrCompat != NULL)
		return [OFString stringWithFormat:
		    @"%u.%u, %s",
		    (version & 0xFF) / 10, (version & 0xFF) % 10, attrCompat];
	else
		return [OFString stringWithFormat:
		    @"%u.%u, unknown %02X",
		    (version % 0xFF) / 10, (version & 0xFF) % 10, version >> 8];
}

OFString *

of_zip_archive_entry_compression_method_to_string(uint16_t compressionMethod)
{
	switch (compressionMethod) {
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_NONE:
		return @"none";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_SHRINK:
		return @"Shrink";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_REDUCE_FACTOR_1:
		return @"Reduce (factor 1)";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_REDUCE_FACTOR_2:
		return @"Reduce (factor 2)";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_REDUCE_FACTOR_3:
		return @"Reduce (factor 3)";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_REDUCE_FACTOR_4:
		return @"Reduce (factor 4)";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_IMPLODE:
		return @"Implode";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE:
		return @"Deflate";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_DEFLATE64:
		return @"Deflate64";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_BZIP2:
		return @"BZip2";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_LZMA:
		return @"LZMA";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_WAVPACK:
		return @"WavPack";
	case OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_PPMD:
		return @"PPMd";
	default:
		return @"unknown";
	}
}

size_t
of_zip_archive_entry_extra_field_find(OFData *extraField, uint16_t tag,
    uint16_t *size)
{
	const uint8_t *bytes = extraField.items;
	size_t count = extraField.count;

	for (size_t i = 0; i < count;) {
		uint16_t currentTag, currentSize;








<
<
<

|




|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|


|















>
|


|

|

|

|

|

|

|

|

|

|

|

|

|







|
|







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
#import "OFStream.h"
#import "OFString.h"

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




OFString *
OFZIPArchiveEntryVersionToString(uint16_t version)
{
	const char *attrCompat = NULL;

	switch (version >> 8) {
	case OFZIPArchiveEntryAttributeCompatibilityMSDOS:
		attrCompat = "MS-DOS or OS/2";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityAmiga:
		attrCompat = "Amiga";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityOpenVMS:
		attrCompat = "OpenVMS";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityUNIX:
		attrCompat = "UNIX";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityVM_CMS:
		attrCompat = "VM/CMS";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityAtariST:
		attrCompat = "Atari ST";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityOS2HPFS:
		attrCompat = "OS/2 HPFS";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityMacintosh:
		attrCompat = "Macintosh";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityZSystem:
		attrCompat = "Z-System";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityCPM:
		attrCompat = "CP/M";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityWindowsNTFS:
		attrCompat = "Windows NTFS";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityMVS:
		attrCompat = "MVS (OS/390 - Z/OS)";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityVSE:
		attrCompat = "VSE";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityAcornRISCOS:
		attrCompat = "Acorn RISC OS";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityVFAT:
		attrCompat = "VFAT";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityAlternateMVS:
		attrCompat = "Alternate MVS";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityBeOS:
		attrCompat = "BeOS";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityTandem:
		attrCompat = "Tandem";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityOS400:
		attrCompat = "OS/400";
		break;
	case OFZIPArchiveEntryAttributeCompatibilityOSX:
		attrCompat = "OS X (Darwin)";
		break;
	}

	if (attrCompat != NULL)
		return [OFString stringWithFormat:
		    @"%u.%u, %s",
		    (version & 0xFF) / 10, (version & 0xFF) % 10, attrCompat];
	else
		return [OFString stringWithFormat:
		    @"%u.%u, unknown %02X",
		    (version % 0xFF) / 10, (version & 0xFF) % 10, version >> 8];
}

OFString *
OFZIPArchiveEntryCompressionMethodName(
    OFZIPArchiveEntryCompressionMethod compressionMethod)
{
	switch (compressionMethod) {
	case OFZIPArchiveEntryCompressionMethodNone:
		return @"none";
	case OFZIPArchiveEntryCompressionMethodShrink:
		return @"Shrink";
	case OFZIPArchiveEntryCompressionMethodReduceFactor1:
		return @"Reduce (factor 1)";
	case OFZIPArchiveEntryCompressionMethodReduceFactor2:
		return @"Reduce (factor 2)";
	case OFZIPArchiveEntryCompressionMethodReduceFactor3:
		return @"Reduce (factor 3)";
	case OFZIPArchiveEntryCompressionMethodReduceFactor4:
		return @"Reduce (factor 4)";
	case OFZIPArchiveEntryCompressionMethodImplode:
		return @"Implode";
	case OFZIPArchiveEntryCompressionMethodDeflate:
		return @"Deflate";
	case OFZIPArchiveEntryCompressionMethodDeflate64:
		return @"Deflate64";
	case OFZIPArchiveEntryCompressionMethodBZIP2:
		return @"BZip2";
	case OFZIPArchiveEntryCompressionMethodLZMA:
		return @"LZMA";
	case OFZIPArchiveEntryCompressionMethodWavPack:
		return @"WavPack";
	case OFZIPArchiveEntryCompressionMethodPPMd:
		return @"PPMd";
	default:
		return @"unknown";
	}
}

size_t
OFZIPArchiveEntryExtraFieldFind(OFData *extraField,
    OFZIPArchiveEntryExtraFieldTag tag, uint16_t *size)
{
	const uint8_t *bytes = extraField.items;
	size_t count = extraField.count;

	for (size_t i = 0; i < count;) {
		uint16_t currentTag, currentSize;

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
			return i + 4;
		}

		i += 4 + currentSize;
	}

	*size = 0;
	return OF_NOT_FOUND;
}

@implementation OFZIPArchiveEntry
+ (instancetype)entryWithFileName: (OFString *)fileName
{
	return [[[self alloc] initWithFileName: fileName] autorelease];
}







|







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
			return i + 4;
		}

		i += 4 + currentSize;
	}

	*size = 0;
	return OFNotFound;
}

@implementation OFZIPArchiveEntry
+ (instancetype)entryWithFileName: (OFString *)fileName
{
	return [[[self alloc] initWithFileName: fileName] autorelease];
}
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFMutableData *extraField = nil;
		uint16_t fileNameLength, extraFieldLength, fileCommentLength;
		of_string_encoding_t encoding;
		size_t ZIP64Index;
		uint16_t ZIP64Size;

		if ([stream readLittleEndianInt32] != 0x02014B50)
			@throw [OFInvalidFormatException exception];

		_versionMadeBy = [stream readLittleEndianInt16];







|







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		OFMutableData *extraField = nil;
		uint16_t fileNameLength, extraFieldLength, fileCommentLength;
		OFStringEncoding encoding;
		size_t ZIP64Index;
		uint16_t ZIP64Size;

		if ([stream readLittleEndianInt32] != 0x02014B50)
			@throw [OFInvalidFormatException exception];

		_versionMadeBy = [stream readLittleEndianInt16];
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
		fileCommentLength = [stream readLittleEndianInt16];
		_startDiskNumber = [stream readLittleEndianInt16];
		_internalAttributes = [stream readLittleEndianInt16];
		_versionSpecificAttributes = [stream readLittleEndianInt32];
		_localFileHeaderOffset = [stream readLittleEndianInt32];

		encoding = (_generalPurposeBitFlag & (1u << 11)
		    ? OF_STRING_ENCODING_UTF_8
		    : OF_STRING_ENCODING_CODEPAGE_437);

		_fileName = [[stream readStringWithLength: fileNameLength
						 encoding: encoding] copy];
		if (extraFieldLength > 0)
			extraField = [[[stream readDataWithCount:
			    extraFieldLength] mutableCopy] autorelease];
		if (fileCommentLength > 0)
			_fileComment = [[stream
			    readStringWithLength: fileCommentLength
					encoding: encoding] copy];

		ZIP64Index = of_zip_archive_entry_extra_field_find(extraField,
		    OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64, &ZIP64Size);

		if (ZIP64Index != OF_NOT_FOUND) {
			const uint8_t *ZIP64 =
			    [extraField itemAtIndex: ZIP64Index];
			of_range_t range =
			    of_range(ZIP64Index - 4, ZIP64Size + 4);

			if (_uncompressedSize == 0xFFFFFFFF)
				_uncompressedSize = of_zip_archive_read_field64(
				    &ZIP64, &ZIP64Size);
			if (_compressedSize == 0xFFFFFFFF)
				_compressedSize = of_zip_archive_read_field64(
				    &ZIP64, &ZIP64Size);
			if (_localFileHeaderOffset == 0xFFFFFFFF)
				_localFileHeaderOffset =
				    of_zip_archive_read_field64(&ZIP64,
				    &ZIP64Size);
			if (_startDiskNumber == 0xFFFF)
				_startDiskNumber = of_zip_archive_read_field32(
				    &ZIP64, &ZIP64Size);

			if (ZIP64Size > 0 || _localFileHeaderOffset < 0)
				@throw [OFInvalidFormatException exception];

			[extraField removeItemsInRange: range];
		}







|
<











|
|

|


|
|


|


|



<
|

|







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
		fileCommentLength = [stream readLittleEndianInt16];
		_startDiskNumber = [stream readLittleEndianInt16];
		_internalAttributes = [stream readLittleEndianInt16];
		_versionSpecificAttributes = [stream readLittleEndianInt32];
		_localFileHeaderOffset = [stream readLittleEndianInt32];

		encoding = (_generalPurposeBitFlag & (1u << 11)
		    ? OFStringEncodingUTF8 : OFStringEncodingCodepage437);


		_fileName = [[stream readStringWithLength: fileNameLength
						 encoding: encoding] copy];
		if (extraFieldLength > 0)
			extraField = [[[stream readDataWithCount:
			    extraFieldLength] mutableCopy] autorelease];
		if (fileCommentLength > 0)
			_fileComment = [[stream
			    readStringWithLength: fileCommentLength
					encoding: encoding] copy];

		ZIP64Index = OFZIPArchiveEntryExtraFieldFind(extraField,
		    OFZIPArchiveEntryExtraFieldTagZIP64, &ZIP64Size);

		if (ZIP64Index != OFNotFound) {
			const uint8_t *ZIP64 =
			    [extraField itemAtIndex: ZIP64Index];
			OFRange range =
			    OFRangeMake(ZIP64Index - 4, ZIP64Size + 4);

			if (_uncompressedSize == 0xFFFFFFFF)
				_uncompressedSize = OFZIPArchiveReadField64(
				    &ZIP64, &ZIP64Size);
			if (_compressedSize == 0xFFFFFFFF)
				_compressedSize = OFZIPArchiveReadField64(
				    &ZIP64, &ZIP64Size);
			if (_localFileHeaderOffset == 0xFFFFFFFF)
				_localFileHeaderOffset =

				    OFZIPArchiveReadField64(&ZIP64, &ZIP64Size);
			if (_startDiskNumber == 0xFFFF)
				_startDiskNumber = OFZIPArchiveReadField32(
				    &ZIP64, &ZIP64Size);

			if (ZIP64Size > 0 || _localFileHeaderOffset < 0)
				@throw [OFInvalidFormatException exception];

			[extraField removeItemsInRange: range];
		}
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
}

- (OFData *)extraField
{
	return _extraField;
}

- (uint16_t)versionMadeBy
{
	return _versionMadeBy;
}

- (uint16_t)minVersionNeeded
{
	return _minVersionNeeded;
}

- (OFDate *)modificationDate
{
	void *pool = objc_autoreleasePoolPush();







|




|







344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
}

- (OFData *)extraField
{
	return _extraField;
}

- (OFZIPArchiveEntryAttributeCompatibility)versionMadeBy
{
	return _versionMadeBy;
}

- (OFZIPArchiveEntryAttributeCompatibility)minVersionNeeded
{
	return _minVersionNeeded;
}

- (OFDate *)modificationDate
{
	void *pool = objc_autoreleasePoolPush();
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
						format: @"%Y-%m-%d %H:%M:%S"];

	objc_autoreleasePoolPop(pool);

	return [date autorelease];
}

- (uint16_t)compressionMethod
{
	return _compressionMethod;
}

- (uint64_t)compressedSize
{
	return _compressedSize;







|







378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
						format: @"%Y-%m-%d %H:%M:%S"];

	objc_autoreleasePoolPop(pool);

	return [date autorelease];
}

- (OFZIPArchiveEntryCompressionMethod)compressionMethod
{
	return _compressionMethod;
}

- (uint64_t)compressedSize
{
	return _compressedSize;
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
	return _localFileHeaderOffset;
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	OFString *compressionMethod =
	    of_zip_archive_entry_compression_method_to_string(
	    _compressionMethod);
	OFString *ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tFile name = %@\n"
	    @"\tFile comment = %@\n"
	    @"\tGeneral purpose bit flag = %u\n"
	    @"\tCompressed size = %" @PRIu64 "\n"
	    @"\tUncompressed size = %" @PRIu64 "\n"







<
|







427
428
429
430
431
432
433

434
435
436
437
438
439
440
441
	return _localFileHeaderOffset;
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	OFString *compressionMethod =

	    OFZIPArchiveEntryCompressionMethodName(_compressionMethod);
	OFString *ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tFile name = %@\n"
	    @"\tFile comment = %@\n"
	    @"\tGeneral purpose bit flag = %u\n"
	    @"\tCompressed size = %" @PRIu64 "\n"
	    @"\tUncompressed size = %" @PRIu64 "\n"
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
	[stream writeLittleEndianInt32: _versionSpecificAttributes];
	[stream writeLittleEndianInt32: 0xFFFFFFFF];
	size += (4 + (6 * 2) + (3 * 4) + (5 * 2) + (2 * 4));

	[stream writeString: _fileName];
	size += (uint64_t)_fileName.UTF8StringLength;

	[stream writeLittleEndianInt16: OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64];
	[stream writeLittleEndianInt16: 28];
	[stream writeLittleEndianInt64: _uncompressedSize];
	[stream writeLittleEndianInt64: _compressedSize];
	[stream writeLittleEndianInt64: _localFileHeaderOffset];
	[stream writeLittleEndianInt32: _startDiskNumber];
	size += (2 * 2) + (3 * 8) + 4;








|







482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
	[stream writeLittleEndianInt32: _versionSpecificAttributes];
	[stream writeLittleEndianInt32: 0xFFFFFFFF];
	size += (4 + (6 * 2) + (3 * 4) + (5 * 2) + (2 * 4));

	[stream writeString: _fileName];
	size += (uint64_t)_fileName.UTF8StringLength;

	[stream writeLittleEndianInt16: OFZIPArchiveEntryExtraFieldTagZIP64];
	[stream writeLittleEndianInt16: 28];
	[stream writeLittleEndianInt64: _uncompressedSize];
	[stream writeLittleEndianInt64: _compressedSize];
	[stream writeLittleEndianInt64: _localFileHeaderOffset];
	[stream writeLittleEndianInt32: _startDiskNumber];
	size += (2 * 2) + (3 * 8) + 4;

Modified src/ObjFW.h from [117174bdb9] to [8e529c90da].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
43
44
45
46
47
48
49

50
51
52
53



54
55
56
57
58
59
60
#import "OFNull.h"

#import "OFMethodSignature.h"
#import "OFInvocation.h"

#import "OFNumber.h"
#import "OFDate.h"

#import "OFURL.h"
#import "OFURLHandler.h"
#import "OFColor.h"




#import "OFStream.h"
#import "OFStdIOStream.h"
#import "OFInflateStream.h"
#import "OFInflate64Stream.h"
#import "OFGZIPStream.h"
#import "OFLHAArchive.h"
#import "OFLHAArchiveEntry.h"







>




>
>
>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#import "OFNull.h"

#import "OFMethodSignature.h"
#import "OFInvocation.h"

#import "OFNumber.h"
#import "OFDate.h"
#import "OFUUID.h"
#import "OFURL.h"
#import "OFURLHandler.h"
#import "OFColor.h"

#import "OFNotification.h"
#import "OFNotificationCenter.h"

#import "OFStream.h"
#import "OFStdIOStream.h"
#import "OFInflateStream.h"
#import "OFInflate64Stream.h"
#import "OFGZIPStream.h"
#import "OFLHAArchive.h"
#import "OFLHAArchiveEntry.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
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
#endif
#ifdef OF_HAVE_SOCKETS
# import "OFStreamSocket.h"
# import "OFDatagramSocket.h"
# import "OFSequencedPacketSocket.h"
# import "OFTCPSocket.h"
# import "OFUDPSocket.h"
# import "OFTLSSocket.h"
# import "OFKernelEventObserver.h"
# import "OFDNSQuery.h"
# import "OFDNSResourceRecord.h"
# import "OFDNSResponse.h"
# import "OFDNSResolver.h"
# ifdef OF_HAVE_IPX
#  import "OFIPXSocket.h"
#  import "OFSPXSocket.h"
#  import "OFSPXStreamSocket.h"
# endif
# ifdef OF_HAVE_SCTP

#  import "OFSCTPSocket.h"
# endif
#endif
#ifdef OF_HAVE_SOCKETS
# ifdef OF_HAVE_THREADS
#  import "OFHTTPClient.h"
# endif
# import "OFHTTPCookie.h"
# import "OFHTTPCookieManager.h"
# import "OFHTTPRequest.h"
# import "OFHTTPResponse.h"
# import "OFHTTPServer.h"
#endif

#ifdef OF_HAVE_PROCESSES
# import "OFProcess.h"
#endif

#import "OFCryptoHash.h"
#import "OFMD5Hash.h"
#import "OFRIPEMD160Hash.h"
#import "OFSHA1Hash.h"
#import "OFSHA224Hash.h"
#import "OFSHA256Hash.h"
#import "OFSHA384Hash.h"
#import "OFSHA512Hash.h"

#import "OFHMAC.h"

#import "OFXMLAttribute.h"
#import "OFXMLElement.h"
#import "OFXMLAttribute.h"
#import "OFXMLCharacters.h"
#import "OFXMLCDATA.h"
#import "OFXMLComment.h"
#import "OFXMLProcessingInstructions.h"
#import "OFXMLParser.h"
#import "OFXMLElementBuilder.h"

#import "OFMessagePackExtension.h"

#import "OFApplication.h"
#import "OFSystemInfo.h"
#import "OFLocale.h"
#import "OFOptionsParser.h"
#import "OFTimer.h"
#import "OFRunLoop.h"
#import "OFSandbox.h"

#ifdef OF_WINDOWS
# import "OFWindowsRegistryKey.h"
#endif

#import "OFASN1BitString.h"
#import "OFASN1Boolean.h"
#import "OFASN1Enumerated.h"
#import "OFASN1IA5String.h"
#import "OFASN1Integer.h"
#import "OFASN1NumericString.h"
#import "OFASN1ObjectIdentifier.h"
#import "OFASN1OctetString.h"
#import "OFASN1PrintableString.h"
#import "OFASN1UTF8String.h"
#import "OFASN1Value.h"

#import "OFAllocFailedException.h"
#import "OFException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFAcceptFailedException.h"
# import "OFAlreadyConnectedException.h"
# import "OFBindFailedException.h"
#endif







|










|
>
|













|
|


|
















|











<





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







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
#endif
#ifdef OF_HAVE_SOCKETS
# import "OFStreamSocket.h"
# import "OFDatagramSocket.h"
# import "OFSequencedPacketSocket.h"
# import "OFTCPSocket.h"
# import "OFUDPSocket.h"
# import "OFTLSStream.h"
# import "OFKernelEventObserver.h"
# import "OFDNSQuery.h"
# import "OFDNSResourceRecord.h"
# import "OFDNSResponse.h"
# import "OFDNSResolver.h"
# ifdef OF_HAVE_IPX
#  import "OFIPXSocket.h"
#  import "OFSPXSocket.h"
#  import "OFSPXStreamSocket.h"
# endif
# ifdef OF_HAVE_UNIX_SOCKETS
#  import "OFUNIXDatagramSocket.h"
#  import "OFUNIXStreamSocket.h"
# endif
#endif
#ifdef OF_HAVE_SOCKETS
# ifdef OF_HAVE_THREADS
#  import "OFHTTPClient.h"
# endif
# import "OFHTTPCookie.h"
# import "OFHTTPCookieManager.h"
# import "OFHTTPRequest.h"
# import "OFHTTPResponse.h"
# import "OFHTTPServer.h"
#endif

#ifdef OF_HAVE_SUBPROCESSES
# import "OFSubprocess.h"
#endif

#import "OFCryptographicHash.h"
#import "OFMD5Hash.h"
#import "OFRIPEMD160Hash.h"
#import "OFSHA1Hash.h"
#import "OFSHA224Hash.h"
#import "OFSHA256Hash.h"
#import "OFSHA384Hash.h"
#import "OFSHA512Hash.h"

#import "OFHMAC.h"

#import "OFXMLAttribute.h"
#import "OFXMLElement.h"
#import "OFXMLAttribute.h"
#import "OFXMLCharacters.h"
#import "OFXMLCDATA.h"
#import "OFXMLComment.h"
#import "OFXMLProcessingInstruction.h"
#import "OFXMLParser.h"
#import "OFXMLElementBuilder.h"

#import "OFMessagePackExtension.h"

#import "OFApplication.h"
#import "OFSystemInfo.h"
#import "OFLocale.h"
#import "OFOptionsParser.h"
#import "OFTimer.h"
#import "OFRunLoop.h"


#ifdef OF_WINDOWS
# import "OFWindowsRegistryKey.h"
#endif













#import "OFAllocFailedException.h"
#import "OFException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFAcceptFailedException.h"
# import "OFAlreadyConnectedException.h"
# import "OFBindFailedException.h"
#endif
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
# import "OFGetCurrentDirectoryPathFailedException.h"
#endif
#import "OFGetOptionFailedException.h"
#ifdef OF_WINDOWS
# import "OFGetWindowsRegistryValueFailedException.h"
#endif
#import "OFHashAlreadyCalculatedException.h"

#ifdef OF_HAVE_SOCKETS
# import "OFHTTPRequestFailedException.h"
#endif
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidJSONException.h"
#import "OFInvalidServerReplyException.h"
#import "OFLinkFailedException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFListenFailedException.h"
#endif
#ifdef OF_HAVE_PLUGINS
# import "OFLoadPluginFailedException.h"
#endif
#import "OFLockFailedException.h"
#import "OFMalformedXMLException.h"
#import "OFMemoryNotPartOfObjectException.h"
#import "OFMoveItemFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFObserveFailedException.h"
#endif
#import "OFOpenItemFailedException.h"
#ifdef OF_WINDOWS
# import "OFOpenWindowsRegistryKeyFailedException.h"
#endif
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFReadOrWriteFailedException.h"
#import "OFRemoveItemFailedException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFResolveHostFailedException.h"
#endif
#import "OFRetrieveItemAttributesFailedException.h"
#import "OFSandboxActivationFailedException.h"
#import "OFSeekFailedException.h"
#import "OFSetItemAttributesFailedException.h"
#import "OFSetOptionFailedException.h"
#ifdef OF_WINDOWS
# import "OFSetWindowsRegistryValueFailedException.h"
#endif
#import "OFStillLockedException.h"
#ifdef OF_HAVE_THREADS
# import "OFThreadJoinFailedException.h"
# import "OFThreadStartFailedException.h"
# import "OFThreadStillRunningException.h"
#endif



#import "OFTruncatedDataException.h"
#import "OFUnboundNamespaceException.h"
#import "OFUnboundPrefixException.h"
#import "OFUndefinedKeyException.h"
#import "OFUnknownXMLEntityException.h"
#import "OFUnlockFailedException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"
#import "OFWriteFailedException.h"

#ifdef OF_HAVE_PLUGINS
# import "OFPlugin.h"
#endif

#ifdef OF_HAVE_ATOMIC_OPS
# import "atomic.h"
#endif

#import "OFLocking.h"
#import "OFThread.h"
#import "once.h"
#ifdef OF_HAVE_THREADS
# import "thread.h"
# import "tlskey.h"
# import "mutex.h"
# import "condition.h"
# import "OFThreadPool.h"
# import "OFMutex.h"
# import "OFRecursiveMutex.h"
# import "OFCondition.h"
#endif

#import "base64.h"
#import "crc16.h"
#import "crc32.h"
#import "huffman_tree.h"
#import "of_asprintf.h"
#import "of_strptime.h"
#import "pbkdf2.h"
#import "scrypt.h"
#ifdef OF_HAVE_UNICODE_TABLES
# import "unicode.h"
#endif







>


















<



















<












>
>
>















|

<

|
|

|
|
|
<
|
|

|


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








# import "OFGetCurrentDirectoryPathFailedException.h"
#endif
#import "OFGetOptionFailedException.h"
#ifdef OF_WINDOWS
# import "OFGetWindowsRegistryValueFailedException.h"
#endif
#import "OFHashAlreadyCalculatedException.h"
#import "OFHashNotCalculatedException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFHTTPRequestFailedException.h"
#endif
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidJSONException.h"
#import "OFInvalidServerReplyException.h"
#import "OFLinkFailedException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFListenFailedException.h"
#endif
#ifdef OF_HAVE_PLUGINS
# import "OFLoadPluginFailedException.h"
#endif
#import "OFLockFailedException.h"
#import "OFMalformedXMLException.h"

#import "OFMoveItemFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFObserveFailedException.h"
#endif
#import "OFOpenItemFailedException.h"
#ifdef OF_WINDOWS
# import "OFOpenWindowsRegistryKeyFailedException.h"
#endif
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFReadOrWriteFailedException.h"
#import "OFRemoveItemFailedException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFResolveHostFailedException.h"
#endif
#import "OFRetrieveItemAttributesFailedException.h"

#import "OFSeekFailedException.h"
#import "OFSetItemAttributesFailedException.h"
#import "OFSetOptionFailedException.h"
#ifdef OF_WINDOWS
# import "OFSetWindowsRegistryValueFailedException.h"
#endif
#import "OFStillLockedException.h"
#ifdef OF_HAVE_THREADS
# import "OFThreadJoinFailedException.h"
# import "OFThreadStartFailedException.h"
# import "OFThreadStillRunningException.h"
#endif
#ifdef OF_HAVE_SOCKETS
# import "OFTLSHandshakeFailedException.h"
#endif
#import "OFTruncatedDataException.h"
#import "OFUnboundNamespaceException.h"
#import "OFUnboundPrefixException.h"
#import "OFUndefinedKeyException.h"
#import "OFUnknownXMLEntityException.h"
#import "OFUnlockFailedException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"
#import "OFWriteFailedException.h"

#ifdef OF_HAVE_PLUGINS
# import "OFPlugin.h"
#endif

#ifdef OF_HAVE_ATOMIC_OPS
# import "OFAtomic.h"
#endif

#import "OFLocking.h"
#import "OFOnce.h"
#import "OFThread.h"
#ifdef OF_HAVE_THREADS
# import "OFCondition.h"
# import "OFMutex.h"
# import "OFPlainCondition.h"

# import "OFPlainMutex.h"
# import "OFPlainThread.h"
# import "OFRecursiveMutex.h"
# import "OFTLSKey.h"
#endif


#import "OFPBKDF2.h"
#import "OFScrypt.h"








Deleted src/atomic_no_threads.h version [ed5f442394].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

static OF_INLINE int
of_atomic_int_add(volatile int *_Nonnull p, int i)
{
	return (*p += i);
}

static OF_INLINE int32_t
of_atomic_int32_add(volatile int32_t *_Nonnull p, int32_t i)
{
	return (*p += i);
}

static OF_INLINE void *_Nullable
of_atomic_ptr_add(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return (*(char *volatile *)p += i);
}

static OF_INLINE int
of_atomic_int_sub(volatile int *_Nonnull p, int i)
{
	return (*p -= i);
}

static OF_INLINE int32_t
of_atomic_int32_sub(volatile int32_t *_Nonnull p, int32_t i)
{
	return (*p -= i);
}

static OF_INLINE void *_Nullable
of_atomic_ptr_sub(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return (*(char *volatile *)p -= i);
}

static OF_INLINE int
of_atomic_int_inc(volatile int *_Nonnull p)
{
	return ++*p;
}

static OF_INLINE int32_t
of_atomic_int32_inc(volatile int32_t *_Nonnull p)
{
	return ++*p;
}

static OF_INLINE int
of_atomic_int_dec(volatile int *_Nonnull p)
{
	return --*p;
}

static OF_INLINE int32_t
of_atomic_int32_dec(volatile int32_t *_Nonnull p)
{
	return --*p;
}

static OF_INLINE unsigned int
of_atomic_int_or(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return (*p |= i);
}

static OF_INLINE uint32_t
of_atomic_int32_or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return (*p |= i);
}

static OF_INLINE unsigned int
of_atomic_int_and(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return (*p &= i);
}

static OF_INLINE uint32_t
of_atomic_int32_and(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return (*p &= i);
}

static OF_INLINE unsigned int
of_atomic_int_xor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return (*p ^= i);
}

static OF_INLINE uint32_t
of_atomic_int32_xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return (*p ^= i);
}

static OF_INLINE bool
of_atomic_int_cmpswap(volatile int *_Nonnull p, int o, int n)
{
	if (*p == o) {
		*p = n;
		return true;
	}

	return false;
}

static OF_INLINE bool
of_atomic_int32_cmpswap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	if (*p == o) {
		*p = n;
		return true;
	}

	return false;
}

static OF_INLINE bool
of_atomic_ptr_cmpswap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	if (*p == o) {
		*p = n;
		return true;
	}

	return false;
}

static OF_INLINE void
of_memory_barrier(void)
{
	/* nop */
}

static OF_INLINE void
of_memory_barrier_acquire(void)
{
	/* nop */
}

static OF_INLINE void
of_memory_barrier_release(void)
{
	/* nop */
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































































































































































































































Deleted src/block.h version [6724cf2142].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

#ifndef OBJFW_BLOCK_H
#define OBJFW_BLOCK_H

#include "macros.h"

OF_ASSUME_NONNULL_BEGIN

typedef struct of_block_literal_t {
#ifdef __OBJC__
	Class isa;
#else
	void *isa;
#endif
	int flags;
	int reserved;
	void (*invoke)(void *block, ...);
	struct of_block_descriptor_t {
		unsigned long reserved;
		unsigned long size;
		void (*_Nullable copy_helper)(void *dest, void *src);
		void (*_Nullable dispose_helper)(void *src);
		const char *signature;
	} *descriptor;
} of_block_literal_t;

#ifdef __cplusplus
extern "C" {
#endif
extern void *_Block_copy(const void *);
extern void _Block_release(const void *);

# if defined(OF_WINDOWS) && \
    (defined(OF_NO_SHARED) || defined(OF_COMPILING_OBJFW))
/*
 * Clang has implicit declarations for these, but they are dllimport. When
 * compiling ObjFW itself or using it as a static library, these need to be
 * dllexport. Interestingly, this still works when using it as a shared library.
 */
extern __declspec(dllexport) struct objc_class _NSConcreteStackBlock;
extern __declspec(dllexport) struct objc_class _NSConcreteGlobalBlock;
extern __declspec(dllexport) void _Block_object_assign(void *, const void *,
    const int);
extern __declspec(dllexport) void _Block_object_dispose(const void *,
    const int);
# endif
#ifdef __cplusplus
}
#endif

#ifndef Block_copy
# define Block_copy(...) \
    ((__typeof__(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
#endif
#ifndef Block_release
# define Block_release(...) _Block_release((const void *)(__VA_ARGS__))
#endif

OF_ASSUME_NONNULL_END

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































Modified src/bridge/NSArray+OFObject.h from [96da5e364a] to [4b59b82d97].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSArray+OFObject.m from [2163b64d79] to [a7c2336812].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSBridging.h from [5f30946c24] to [1aa6518645].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSDictionary+OFObject.h from [416324c2d3] to [1dcde481c6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSDictionary+OFObject.m from [61195e4046] to [3bee09377c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSEnumerator+OFObject.h from [5d278f1dcb] to [5e37d187b7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSEnumerator+OFObject.m from [fc63efa4f6] to [dd0762c949].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSNumber+OFObject.h from [79c9621d37] to [bd31c0c3cd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSNumber+OFObject.m from [a79897d657] to [18f7ec56ae].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSOFArray.h from [66e744b7e0] to [cd8e3e0488].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSOFArray.m from [5ca650b9c7] to [41ea305080].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSOFDictionary.h from [51287f6e15] to [b628e7e6a3].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSOFDictionary.m from [992ca34ebe] to [e47773ee01].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSOFEnumerator.h from [fc80d78d26] to [fcb10c08ed].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSOFEnumerator.m from [8af097925c] to [ebbed84f33].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSOFSet.h from [05ef212378] to [33948f6157].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSOFSet.m from [bb7bda884f] to [e7387e2762].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSSet+OFObject.h from [36a48785da] to [68cb6c5ccf].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSSet+OFObject.m from [ce021d5ef0] to [5daeb1bac4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSString+OFObject.h from [a36e06c85f] to [a30f21642d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/NSString+OFObject.m from [6eccedb527] to [f6fbd9f898].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFArray+NSObject.h from [ddd32da320] to [f258ecab9a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFArray+NSObject.m from [b153739fac] to [ee4c5a25af].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFBridging.h from [64c0fc4ba9] to [abe87031bc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFDictionary+NSObject.h from [18dcc7538d] to [27bfd0d589].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFDictionary+NSObject.m from [d35c1e81fe] to [6df9e22408].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFEnumerator+NSObject.h from [d90c90d947] to [cb0752d99c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFEnumerator+NSObject.m from [f173918960] to [a3ee1dfe29].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFException+Swift.h from [3aecaa6184] to [da5307c0ad].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
 * @brief Execute the specified try block and finally call the finally block.
 *
 * @note This is only useful for Swift.
 *
 * @param try The try block to execute
 * @param finally The finally block to call at the end
 */
+ (void)try: (void (^)(void))try
    finally: (void (^)(void))finally;

/**
 * @brief Execute the specified try block and call the catch block if an
 *	  OFException occurred and finally call the finally block.
 *
 * @note This is only useful to catch OFExceptions in Swift.
 *







|
<







43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
 * @brief Execute the specified try block and finally call the finally block.
 *
 * @note This is only useful for Swift.
 *
 * @param try The try block to execute
 * @param finally The finally block to call at the end
 */
+ (void)try: (void (^)(void))try finally: (void (^)(void))finally;


/**
 * @brief Execute the specified try block and call the catch block if an
 *	  OFException occurred and finally call the finally block.
 *
 * @note This is only useful to catch OFExceptions in Swift.
 *

Modified src/bridge/OFException+Swift.m from [ebdcbc4eab] to [69103747b4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNSArray.h from [6f62085ac5] to [1c05dea53b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNSArray.m from [a1ec973a29] to [511fcf77df].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNSDictionary.h from [31d9760029] to [3699212d07].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNSDictionary.m from [2365a3fbd3] to [3228b094f1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNSEnumerator.h from [9fd573cd7f] to [f60938ebf6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNSEnumerator.m from [f6021ac455] to [9f5d7028e9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNSSet.h from [a6b44f6f41] to [7b3370ed57].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNSSet.m from [85734e4123] to [151a52ad4b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNumber+NSObject.h from [9042629e9c] to [955839af4e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFNumber+NSObject.m from [444f15ed81] to [f73968ae16].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFSet+NSObject.h from [06675274eb] to [9214bbd006].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFSet+NSObject.m from [32b967da40] to [e5bc663707].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFString+NSObject.h from [e3e9530327] to [375ab3a6b5].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/OFString+NSObject.m from [fefeea73ea] to [bf4d082579].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/bridge/ObjFWBridge.h from [2ccf4ce44e] to [1fbb08ae27].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Renamed and modified src/encodings/codepage_437.m [d62d8ff5ca] to src/encodings/codepage-437.m [1cc88f79fe].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_codepage_437_table[] = {
	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
	0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
	0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
	0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
	0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
	0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
	0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
	0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
	0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
	0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
	0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
};
const size_t of_codepage_437_table_offset =
    256 - (sizeof(of_codepage_437_table) / sizeof(*of_codepage_437_table));

static const unsigned char page0[] = {
	0xFF, 0xAD, 0x9B, 0x9C, 0x00, 0x9D, 0x00, 0x00,
	0x00, 0x00, 0xA6, 0xAE, 0xAA, 0x00, 0x00, 0x00,
	0xF8, 0xF1, 0xFD, 0x00, 0x00, 0xE6, 0x00, 0xFA,
	0x00, 0x00, 0xA7, 0xAF, 0xAC, 0xAB, 0x00, 0xA8,
	0x00, 0x00, 0x00, 0x00, 0x8E, 0x8F, 0x92, 0x80,

<
<
|



















|

















|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFCodepage437Table[] = {
	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
	0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
	0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
	0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
	0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
	0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
	0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
	0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
	0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
	0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
	0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
};
const size_t OFCodepage437TableOffset =
    256 - (sizeof(OFCodepage437Table) / sizeof(*OFCodepage437Table));

static const unsigned char page0[] = {
	0xFF, 0xAD, 0x9B, 0x9C, 0x00, 0x9D, 0x00, 0x00,
	0x00, 0x00, 0xA6, 0xAE, 0xAA, 0x00, 0x00, 0x00,
	0xF8, 0xF1, 0xFD, 0x00, 0x00, 0xE6, 0x00, 0xFA,
	0x00, 0x00, 0xA7, 0xAF, 0xAC, 0xAB, 0x00, 0xA8,
	0x00, 0x00, 0x00, 0x00, 0x8E, 0x8F, 0x92, 0x80,
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
	0xDE, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xFE
};
static const uint8_t page25Start = 0x00;

bool
of_unicode_to_codepage_437(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
	0xDE, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xFE
};
static const uint8_t page25Start = 0x00;

bool
OFUnicodeToCodepage437(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Renamed and modified src/encodings/codepage_850.m [1987cfb8b5] to src/encodings/codepage-850.m [d9cdb5c58f].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_codepage_850_table[] = {
	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
	0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
	0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
	0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
	0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
	0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
	0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
	0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
	0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
	0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
	0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
	0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
};
const size_t of_codepage_850_table_offset =
    256 - (sizeof(of_codepage_850_table) / sizeof(*of_codepage_850_table));


static const unsigned char page0[] = {
	0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5,
	0xF9, 0xB8, 0xA6, 0xAE, 0xAA, 0xF0, 0xA9, 0xEE,
	0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA,
	0xF7, 0xFB, 0xA7, 0xAF, 0xAC, 0xAB, 0xF3, 0xA8,

<
<
|



















|

















|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFCodepage850Table[] = {
	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
	0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
	0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
	0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
	0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
	0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
	0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
	0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
	0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
	0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
	0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
	0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
};
const size_t OFCodepage850TableOffset =
    256 - (sizeof(OFCodepage850Table) / sizeof(*OFCodepage850Table));


static const unsigned char page0[] = {
	0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5,
	0xF9, 0xB8, 0xA6, 0xAE, 0xAA, 0xF0, 0xA9, 0xEE,
	0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA,
	0xF7, 0xFB, 0xA7, 0xAF, 0xAC, 0xAB, 0xF3, 0xA8,
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
	0x00, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xFE
};
static const uint8_t page25Start = 0x00;

bool
of_unicode_to_codepage_850(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
	0x00, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xFE
};
static const uint8_t page25Start = 0x00;

bool
OFUnicodeToCodepage850(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Renamed and modified src/encodings/codepage_858.m [076db9f46a] to src/encodings/codepage-858.m [402e5b0a14].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_codepage_858_table[] = {
	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
	0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
	0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
	0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
	0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
	0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
	0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,
	0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
	0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
	0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
	0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
	0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
};
const size_t of_codepage_858_table_offset =
    256 - (sizeof(of_codepage_858_table) / sizeof(*of_codepage_858_table));


static const unsigned char page0[] = {
	0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5,
	0xF9, 0xB8, 0xA6, 0xAE, 0xAA, 0xF0, 0xA9, 0xEE,
	0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA,
	0xF7, 0xFB, 0xA7, 0xAF, 0xAC, 0xAB, 0xF3, 0xA8,

<
<
|



















|

















|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFCodepage858Table[] = {
	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
	0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
	0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
	0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
	0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
	0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
	0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,
	0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
	0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
	0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
	0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
	0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
};
const size_t OFCodepage858TableOffset =
    256 - (sizeof(OFCodepage858Table) / sizeof(*OFCodepage858Table));


static const unsigned char page0[] = {
	0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5,
	0xF9, 0xB8, 0xA6, 0xAE, 0xAA, 0xF0, 0xA9, 0xEE,
	0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA,
	0xF7, 0xFB, 0xA7, 0xAF, 0xAC, 0xAB, 0xF3, 0xA8,
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
	0x00, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xFE
};
static const uint8_t page25Start = 0x00;

bool
of_unicode_to_codepage_858(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
	0x00, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xFE
};
static const uint8_t page25Start = 0x00;

bool
OFUnicodeToCodepage858(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Modified src/encodings/common.h from [46b2b33ba1] to [a9b82f31be].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Renamed and modified src/encodings/iso_8859-15.m [8a992cf99b] to src/encodings/iso-8859-15.m [8197488f98].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_iso_8859_15_table[] = {
	0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7,
	0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
	0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7,
	0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF,
	0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
	0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
	0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
	0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
	0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
	0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
	0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
	0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
};
const size_t of_iso_8859_15_table_offset =
    256 - (sizeof(of_iso_8859_15_table) / sizeof(*of_iso_8859_15_table));

static const unsigned char page0[] = {
	0x00, 0xA5, 0x00, 0xA7, 0x00, 0xA9, 0xAA, 0xAB,
	0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
	0x00, 0xB5, 0xB6, 0xB7, 0x00, 0xB9, 0xBA, 0xBB,
	0x00, 0x00, 0x00
};

<
<
|



















|













|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFISO8859_15Table[] = {
	0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7,
	0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
	0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7,
	0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF,
	0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
	0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
	0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
	0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
	0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
	0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
	0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
	0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
};
const size_t OFISO8859_15TableOffset =
    256 - (sizeof(OFISO8859_15Table) / sizeof(*OFISO8859_15Table));

static const unsigned char page0[] = {
	0x00, 0xA5, 0x00, 0xA7, 0x00, 0xA9, 0xAA, 0xAB,
	0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
	0x00, 0xB5, 0xB6, 0xB7, 0x00, 0xB9, 0xBA, 0xBB,
	0x00, 0x00, 0x00
};
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

static const unsigned char page20[] = {
	0xA4
};
static const uint8_t page20Start = 0xAC;

bool
of_unicode_to_iso_8859_15(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

static const unsigned char page20[] = {
	0xA4
};
static const uint8_t page20Start = 0xAC;

bool
OFUnicodeToISO8859_15(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Renamed and modified src/encodings/iso_8859-2.m [c37ad1bb56] to src/encodings/iso-8859-2.m [b10a788d3d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_iso_8859_2_table[] = {
	0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7,
	0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B,
	0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7,
	0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C,
	0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
	0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
	0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
	0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
	0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
	0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
	0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
	0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
};
const size_t of_iso_8859_2_table_offset =
    256 - (sizeof(of_iso_8859_2_table) / sizeof(*of_iso_8859_2_table));

static const unsigned char page0[] = {
	0xA0, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0xA7,
	0xA8, 0x00, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00,
	0xB0, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00,
	0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xC1, 0xC2, 0x00, 0xC4, 0x00, 0x00, 0xC7,

<
<
|



















|













|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFISO8859_2Table[] = {
	0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7,
	0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B,
	0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7,
	0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C,
	0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
	0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
	0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
	0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
	0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
	0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
	0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
	0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
};
const size_t OFISO8859_2TableOffset =
    256 - (sizeof(OFISO8859_2Table) / sizeof(*OFISO8859_2Table));

static const unsigned char page0[] = {
	0xA0, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0xA7,
	0xA8, 0x00, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00,
	0xB0, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00,
	0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xC1, 0xC2, 0x00, 0xC4, 0x00, 0x00, 0xC7,
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
	0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xA2, 0xFF, 0x00, 0xB2, 0x00, 0xBD
};
static const uint8_t page2Start = 0xC7;

bool
of_unicode_to_iso_8859_2(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
	0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xA2, 0xFF, 0x00, 0xB2, 0x00, 0xBD
};
static const uint8_t page2Start = 0xC7;

bool
OFUnicodeToISO8859_2(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Renamed and modified src/encodings/iso_8859-3.m [5f4c6ba0f6] to src/encodings/iso-8859-3.m [57543aa317].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_iso_8859_3_table[] = {
	0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0xFFFF, 0x0124, 0x00A7,
	0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0xFFFF, 0x017B,
	0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7,
	0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0xFFFF, 0x017C,
	0x00C0, 0x00C1, 0x00C2, 0xFFFF, 0x00C4, 0x010A, 0x0108, 0x00C7,
	0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
	0xFFFF, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7,
	0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF,
	0x00E0, 0x00E1, 0x00E2, 0xFFFF, 0x00E4, 0x010B, 0x0109, 0x00E7,
	0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
	0xFFFF, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7,
	0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9
};
const size_t of_iso_8859_3_table_offset =
    256 - (sizeof(of_iso_8859_3_table) / sizeof(*of_iso_8859_3_table));

static const unsigned char page0[] = {
	0xA0, 0x00, 0x00, 0xA3, 0xA4, 0x00, 0x00, 0xA7,
	0xA8, 0x00, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00,
	0xB0, 0x00, 0xB2, 0xB3, 0xB4, 0xB5, 0x00, 0xB7,
	0xB8, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00,
	0xC0, 0xC1, 0xC2, 0x00, 0xC4, 0x00, 0x00, 0xC7,

<
<
|



















|













|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFISO8859_3Table[] = {
	0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0xFFFF, 0x0124, 0x00A7,
	0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0xFFFF, 0x017B,
	0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7,
	0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0xFFFF, 0x017C,
	0x00C0, 0x00C1, 0x00C2, 0xFFFF, 0x00C4, 0x010A, 0x0108, 0x00C7,
	0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
	0xFFFF, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7,
	0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF,
	0x00E0, 0x00E1, 0x00E2, 0xFFFF, 0x00E4, 0x010B, 0x0109, 0x00E7,
	0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
	0xFFFF, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7,
	0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9
};
const size_t OFISO8859_3TableOffset =
    256 - (sizeof(OFISO8859_3Table) / sizeof(*OFISO8859_3Table));

static const unsigned char page0[] = {
	0xA0, 0x00, 0x00, 0xA3, 0xA4, 0x00, 0x00, 0xA7,
	0xA8, 0x00, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00,
	0xB0, 0x00, 0xB2, 0xB3, 0xB4, 0xB5, 0x00, 0xB7,
	0xB8, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00,
	0xC0, 0xC1, 0xC2, 0x00, 0xC4, 0x00, 0x00, 0xC7,
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

static const unsigned char page2[] = {
	0xA2, 0xFF
};
static const uint8_t page2Start = 0xD8;

bool
of_unicode_to_iso_8859_3(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

static const unsigned char page2[] = {
	0xA2, 0xFF
};
static const uint8_t page2Start = 0xD8;

bool
OFUnicodeToISO8859_3(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Modified src/encodings/koi8-r.m from [29498b5aa9] to [e29e1b888f].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_koi8_r_table[] = {
	0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
	0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
	0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
	0x2264,	0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7,
	0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556,
	0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E,
	0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565,
	0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x00A9,
	0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
	0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
	0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
	0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A,
	0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
	0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
	0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
	0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A
};
const size_t of_koi8_r_table_offset =
    256 - (sizeof(of_koi8_r_table) / sizeof(*of_koi8_r_table));

static const unsigned char page0[] = {
	0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x9C, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x9E,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

<
<
|



















|

















|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFKOI8RTable[] = {
	0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
	0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
	0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
	0x2264,	0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7,
	0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556,
	0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E,
	0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565,
	0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x00A9,
	0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
	0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
	0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
	0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A,
	0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
	0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
	0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
	0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A
};
const size_t OFKOI8RTableOffset =
    256 - (sizeof(OFKOI8RTable) / sizeof(*OFKOI8RTable));

static const unsigned char page0[] = {
	0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x9C, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x9E,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
	0x8F, 0x90, 0x91, 0x92, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x94
};
static const uint8_t page25Start = 0x00;

bool
of_unicode_to_koi8_r(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|
|


|







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
	0x8F, 0x90, 0x91, 0x92, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x94
};
static const uint8_t page25Start = 0x00;

bool
OFUnicodeToKOI8R(const OFUnichar *input, unsigned char *output, size_t length,
    bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Modified src/encodings/koi8-u.m from [c05bd3e1cb] to [10ad0de57d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_koi8_u_table[] = {
	0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
	0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
	0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
	0x2264,	0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7,
	0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457,
	0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E,
	0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407,
	0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9,
	0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
	0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
	0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
	0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A,
	0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
	0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
	0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
	0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A
};
const size_t of_koi8_u_table_offset =
    256 - (sizeof(of_koi8_u_table) / sizeof(*of_koi8_u_table));

static const unsigned char page0[] = {
	0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x9C, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x9E,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

<
<
|



















|

















|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFKOI8UTable[] = {
	0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
	0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
	0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
	0x2264,	0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7,
	0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457,
	0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E,
	0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407,
	0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9,
	0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
	0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
	0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
	0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A,
	0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
	0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
	0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
	0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A
};
const size_t OFKOI8UTableOffset =
    256 - (sizeof(OFKOI8UTable) / sizeof(*OFKOI8UTable));

static const unsigned char page0[] = {
	0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x9C, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x9E,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
	0x8F, 0x90, 0x91, 0x92, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x94
};
static const uint8_t page25Start = 0x00;

bool
of_unicode_to_koi8_u(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|
|


|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
	0x8F, 0x90, 0x91, 0x92, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x94
};
static const uint8_t page25Start = 0x00;

bool
OFUnicodeToKOI8U(const OFUnichar *input, unsigned char *output, size_t length,
    bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Renamed and modified src/encodings/mac_roman.m [696af3cc01] to src/encodings/mac-roman.m [5811cbe352].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_mac_roman_table[] = {
	0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
	0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
	0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
	0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
	0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
	0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
	0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
	0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
	0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
	0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
	0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
	0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
	0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
	0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
	0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
	0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7
};
const size_t of_mac_roman_table_offset =
    256 - (sizeof(of_mac_roman_table) / sizeof(*of_mac_roman_table));

static const unsigned char page0[] = {
	0xCA, 0xC1, 0xA2, 0xA3, 0x00, 0xB4, 0x00, 0xA4,
	0xAC, 0xA9, 0xBB, 0xC7, 0xC2, 0x00, 0xA8, 0xF8,
	0xA1, 0xB1, 0x00, 0x00, 0xAB, 0xB5, 0xA6, 0xE1,
	0xFC, 0x00, 0xBC, 0xC8, 0x00, 0x00, 0x00, 0xC0,
	0xCB, 0xE7, 0xE5, 0xCC, 0x80, 0x81, 0xAE, 0x82,

<
<
|



















|

















|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFMacRomanTable[] = {
	0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
	0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
	0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
	0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
	0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
	0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
	0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
	0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
	0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
	0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
	0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
	0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
	0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
	0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
	0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
	0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7
};
const size_t OFMacRomanTableOffset =
    256 - (sizeof(OFMacRomanTable) / sizeof(*OFMacRomanTable));

static const unsigned char page0[] = {
	0xCA, 0xC1, 0xA2, 0xA3, 0x00, 0xB4, 0x00, 0xA4,
	0xAC, 0xA9, 0xBB, 0xC7, 0xC2, 0x00, 0xA8, 0xF8,
	0xA1, 0xB1, 0x00, 0x00, 0xAB, 0xB5, 0xA6, 0xE1,
	0xFC, 0x00, 0xBC, 0xC8, 0x00, 0x00, 0x00, 0xC0,
	0xCB, 0xE7, 0xE5, 0xCC, 0x80, 0x81, 0xAE, 0x82,
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

static const unsigned char pageFB[] = {
	0xDE, 0xDF
};
static const uint8_t pageFBStart = 0x01;

bool
of_unicode_to_mac_roman(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







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

static const unsigned char pageFB[] = {
	0xDE, 0xDF
};
static const uint8_t pageFBStart = 0x01;

bool
OFUnicodeToMacRoman(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Modified src/encodings/windows-1251.m from [18ae3df621] to [b8c4870fa7].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_windows_1251_table[] = {
	0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
	0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
	0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
	0xFFFF, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
	0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
	0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
	0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
	0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
	0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
	0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
	0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
	0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
	0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
	0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
};
const size_t of_windows_1251_table_offset =
    256 - (sizeof(of_windows_1251_table) / sizeof(*of_windows_1251_table));

static const unsigned char page0[] = {
	0xA0, 0x00, 0x00, 0x00, 0xA4, 0x00, 0xA6, 0xA7,
	0x00, 0xA9, 0x00, 0xAB, 0xAC, 0xAD, 0xAE, 0x00,
	0xB0, 0xB1, 0x00, 0x00, 0x00, 0xB5, 0xB6, 0xB7,
	0x00, 0x00, 0x00, 0xBB
};

<
<
|



















|

















|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFWindows1251Table[] = {
	0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
	0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
	0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
	0xFFFF, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
	0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
	0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
	0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
	0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
	0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
	0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
	0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
	0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
	0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
	0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
};
const size_t OFWindows1251TableOffset =
    256 - (sizeof(OFWindows1251Table) / sizeof(*OFWindows1251Table));

static const unsigned char page0[] = {
	0xA0, 0x00, 0x00, 0x00, 0xA4, 0x00, 0xA6, 0xA7,
	0x00, 0xA9, 0x00, 0xAB, 0xAC, 0xAD, 0xAE, 0x00,
	0xB0, 0xB1, 0x00, 0x00, 0x00, 0xB5, 0xB6, 0xB7,
	0x00, 0x00, 0x00, 0xBB
};
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
static const unsigned char page21[] = {
	0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x99
};
static const uint8_t page21Start = 0x16;

bool
of_unicode_to_windows_1251(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
static const unsigned char page21[] = {
	0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x99
};
static const uint8_t page21Start = 0x16;

bool
OFUnicodeToWindows1251(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Modified src/encodings/windows-1252.m from [792d858e21] to [f9fc8b9676].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

#import "common.h"

const of_char16_t of_windows_1252_table[] = {
	0x20AC, 0xFFFF, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
	0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFF, 0x017D, 0xFFFF,
	0xFFFF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
	0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0xFFFF, 0x017E, 0x0178,
	0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
	0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
	0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
	0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
	0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
	0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
	0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
	0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
	0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
	0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
	0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
	0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
};
const size_t of_windows_1252_table_offset =
    256 - (sizeof(of_windows_1252_table) / sizeof(*of_windows_1252_table));

static const unsigned char page0[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

<
<
|



















|

















|
|







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


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

#import "OFString.h"

#import "common.h"

const OFChar16 OFWindows1252Table[] = {
	0x20AC, 0xFFFF, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
	0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFF, 0x017D, 0xFFFF,
	0xFFFF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
	0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0xFFFF, 0x017E, 0x0178,
	0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
	0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
	0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
	0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
	0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
	0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
	0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
	0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
	0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
	0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
	0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
	0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
};
const size_t OFWindows1252TableOffset =
    256 - (sizeof(OFWindows1252Table) / sizeof(*OFWindows1252Table));

static const unsigned char page0[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

static const unsigned char page21[] = {
	0x99
};
static const uint8_t page21Start = 0x22;

bool
of_unicode_to_windows_1252(const of_unichar_t *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		of_unichar_t c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';







|



|







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

static const unsigned char page21[] = {
	0x99
};
static const uint8_t page21Start = 0x22;

bool
OFUnicodeToWindows1252(const OFUnichar *input, unsigned char *output,
    size_t length, bool lossy)
{
	for (size_t i = 0; i < length; i++) {
		OFUnichar c = input[i];

		if OF_UNLIKELY (c > 0x7F) {
			uint8_t idx;

			if OF_UNLIKELY (c > 0xFFFF) {
				if (lossy) {
					output[i] = '?';

Modified src/exceptions/Makefile from [103a175ba6] to [edffb84129].

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
       OFCopyItemFailedException.m			\
       OFCreateDirectoryFailedException.m		\
       OFCreateSymbolicLinkFailedException.m		\
       OFEnumerationMutationException.m			\
       OFException.m					\
       OFGetOptionFailedException.m			\
       OFHashAlreadyCalculatedException.m		\

       OFInitializationFailedException.m		\
       OFInvalidArgumentException.m			\
       OFInvalidEncodingException.m			\
       OFInvalidFormatException.m			\
       OFInvalidJSONException.m				\
       OFInvalidServerReplyException.m			\
       OFLinkFailedException.m				\
       OFLockFailedException.m				\
       OFMalformedXMLException.m			\
       OFMemoryNotPartOfObjectException.m		\
       OFMoveItemFailedException.m			\
       OFNotImplementedException.m			\
       OFNotOpenException.m				\
       OFOpenItemFailedException.m			\
       OFOutOfMemoryException.m				\
       OFOutOfRangeException.m				\
       OFReadFailedException.m				\
       OFReadOrWriteFailedException.m			\
       OFRemoveItemFailedException.m			\
       OFRetrieveItemAttributesFailedException.m	\
       OFSandboxActivationFailedException.m		\
       OFSeekFailedException.m				\
       OFSetItemAttributesFailedException.m		\
       OFSetOptionFailedException.m			\
       OFStillLockedException.m				\
       OFTruncatedDataException.m			\
       OFUnboundNamespaceException.m			\
       OFUnboundPrefixException.m			\







>









<










<







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
       OFCopyItemFailedException.m			\
       OFCreateDirectoryFailedException.m		\
       OFCreateSymbolicLinkFailedException.m		\
       OFEnumerationMutationException.m			\
       OFException.m					\
       OFGetOptionFailedException.m			\
       OFHashAlreadyCalculatedException.m		\
       OFHashNotCalculatedException.m			\
       OFInitializationFailedException.m		\
       OFInvalidArgumentException.m			\
       OFInvalidEncodingException.m			\
       OFInvalidFormatException.m			\
       OFInvalidJSONException.m				\
       OFInvalidServerReplyException.m			\
       OFLinkFailedException.m				\
       OFLockFailedException.m				\
       OFMalformedXMLException.m			\

       OFMoveItemFailedException.m			\
       OFNotImplementedException.m			\
       OFNotOpenException.m				\
       OFOpenItemFailedException.m			\
       OFOutOfMemoryException.m				\
       OFOutOfRangeException.m				\
       OFReadFailedException.m				\
       OFReadOrWriteFailedException.m			\
       OFRemoveItemFailedException.m			\
       OFRetrieveItemAttributesFailedException.m	\

       OFSeekFailedException.m				\
       OFSetItemAttributesFailedException.m		\
       OFSetOptionFailedException.m			\
       OFStillLockedException.m				\
       OFTruncatedDataException.m			\
       OFUnboundNamespaceException.m			\
       OFUnboundPrefixException.m			\
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
	       OFAlreadyConnectedException.m		\
	       OFBindFailedException.m			\
	       OFConnectionFailedException.m		\
	       OFDNSQueryFailedException.m		\
	       OFHTTPRequestFailedException.m		\
	       OFListenFailedException.m		\
	       OFObserveFailedException.m		\
	       OFResolveHostFailedException.m

SRCS_THREADS = OFConditionBroadcastFailedException.m	\
	       OFConditionSignalFailedException.m	\
	       OFConditionStillWaitingException.m	\
	       OFConditionWaitFailedException.m		\
	       OFThreadJoinFailedException.m		\
	       OFThreadStartFailedException.m		\
	       OFThreadStillRunningException.m
SRCS_WINDOWS = OFCreateWindowsRegistryKeyFailedException.m	\
	       OFDeleteWindowsRegistryKeyFailedException.m	\
	       OFDeleteWindowsRegistryValueFailedException.m	\
	       OFGetWindowsRegistryValueFailedException.m	\
	       OFOpenWindowsRegistryKeyFailedException.m	\
	       OFSetWindowsRegistryValueFailedException.m

INCLUDES = ${SRCS:.m=.h}



include ../../buildsys.mk

CPPFLAGS += -I. -I.. -I../.. -I../runtime







|
>














|
>
>




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
	       OFAlreadyConnectedException.m		\
	       OFBindFailedException.m			\
	       OFConnectionFailedException.m		\
	       OFDNSQueryFailedException.m		\
	       OFHTTPRequestFailedException.m		\
	       OFListenFailedException.m		\
	       OFObserveFailedException.m		\
	       OFResolveHostFailedException.m		\
	       OFTLSHandshakeFailedException.m
SRCS_THREADS = OFConditionBroadcastFailedException.m	\
	       OFConditionSignalFailedException.m	\
	       OFConditionStillWaitingException.m	\
	       OFConditionWaitFailedException.m		\
	       OFThreadJoinFailedException.m		\
	       OFThreadStartFailedException.m		\
	       OFThreadStillRunningException.m
SRCS_WINDOWS = OFCreateWindowsRegistryKeyFailedException.m	\
	       OFDeleteWindowsRegistryKeyFailedException.m	\
	       OFDeleteWindowsRegistryValueFailedException.m	\
	       OFGetWindowsRegistryValueFailedException.m	\
	       OFOpenWindowsRegistryKeyFailedException.m	\
	       OFSetWindowsRegistryValueFailedException.m

INCLUDES := ${SRCS:.m=.h}

SRCS += OFSandboxActivationFailedException.m

include ../../buildsys.mk

CPPFLAGS += -I. -I.. -I../.. -I../runtime

Modified src/exceptions/OFAcceptFailedException.h from [07a2f9d03f] to [257583a910].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
 * @brief Creates a new, autoreleased accept failed exception.
 *
 * @param socket The socket which could not accept a connection
 * @param errNo The errno for the error
 * @return A new, autoreleased accept failed exception
 */
+ (instancetype)exceptionWithSocket: (id)socket
			      errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated accept failed exception.
 *
 * @param socket The socket which could not accept a connection







|
<







48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
/**
 * @brief Creates a new, autoreleased accept failed exception.
 *
 * @param socket The socket which could not accept a connection
 * @param errNo The errno for the error
 * @return A new, autoreleased accept failed exception
 */
+ (instancetype)exceptionWithSocket: (id)socket errNo: (int)errNo;


- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated accept failed exception.
 *
 * @param socket The socket which could not accept a connection

Modified src/exceptions/OFAcceptFailedException.m from [a59ffc8d01] to [8a127340d9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize socket = _socket, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithSocket: (id)socket
			      errNo: (int)errNo
{
	return [[[self alloc] initWithSocket: socket
				       errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSocket: (id)socket
			 errNo: (int)errNo
{
	self = [super init];

	_socket = [socket retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_socket release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to accept connection in socket of class %@: %@",
	    [_socket class], of_strerror(_errNo)];
}
@end







|
<

|
<







|
<




















|


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
@synthesize socket = _socket, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithSocket: (id)socket errNo: (int)errNo

{
	return [[[self alloc] initWithSocket: socket errNo: errNo] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSocket: (id)socket errNo: (int)errNo

{
	self = [super init];

	_socket = [socket retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_socket release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to accept connection in socket of class %@: %@",
	    [_socket class], OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFAllocFailedException.h from [0364422b9e] to [89360977c8].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFAllocFailedException.m from [54ff75e246] to [845ed2917e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void *)allocMemoryWithSize: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)allocMemoryForNItems: (size_t)nitems
		      withSize: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)resizeMemory: (void *)ptr
		toSize: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void *)resizeMemory: (void *)ptr
	      toNItems: (size_t)nitems
	      withSize: (size_t)size
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)freeMemory: (void *)ptr
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)retain
{
	return self;
}

- (instancetype)autorelease
{
	return self;
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

- (void)release
{
}

- (void)dealloc







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












|







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
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}






























- (instancetype)retain
{
	return self;
}

- (instancetype)autorelease
{
	return self;
}

- (unsigned int)retainCount
{
	return OFMaxRetainCount;
}

- (void)release
{
}

- (void)dealloc

Modified src/exceptions/OFAlreadyConnectedException.h from [e56b6041c7] to [56f0934858].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFAlreadyConnectedException.m from [74939bf32e] to [055eee0622].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFBindFailedException.h from [c5dc0bbbc4] to [5fb5dee342].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFException.h"

#ifndef OF_HAVE_SOCKETS
# error No sockets available!
#endif

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFBindFailedException \
 *	  OFBindFailedException.h ObjFW/OFBindFailedException.h
 *
 * @brief An exception indicating that binding a socket failed.
 */
@interface OFBindFailedException: OFException
{
	id _socket;
	/* IP */
	OFString *_host;
	uint16_t _port;
	/* IPX */
	uint8_t _packetType;



	int _errNo;
}

/**
 * @brief The host on which binding failed.
 */
@property (readonly, nonatomic) OFString *host;

/**
 * @brief The port on which binding failed.
 */
@property (readonly, nonatomic) uint16_t port;

/**
 * @brief The IPX packet type for which binding failed.
 */
@property (readonly, nonatomic) uint8_t packetType;






/**
 * @brief The socket which could not be bound.
 */
@property (readonly, nonatomic) id socket;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased bind failed exception.
 *
 * @param host The host on which binding failed
 * @param port The port on which binding failed
 * @param socket The socket which could not be bound
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased bind failed exception
 */
+ (instancetype)exceptionWithHost: (OFString *)host
			     port: (uint16_t)port
			   socket: (id)socket
			    errNo: (int)errNo;



/**
 * @brief Creates a new, autoreleased bind failed exception.
 *
 * @param port The IPX port to which binding failed
 * @param packetType The IPX packet type for which binding failed
 * @param socket The socket which could not be bound
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased bind failed exception
 */
+ (instancetype)exceptionWithPort: (uint16_t)port
		       packetType: (uint8_t)packetType
			   socket: (id)socket
			    errNo: (int)errNo;









- (instancetype)init OF_UNAVAILABLE;



/**
 * @brief Initializes an already allocated bind failed exception.
 *
 * @param host The host on which binding failed
 * @param port The port on which binding failed
 * @param socket The socket which could not be bound

<
<
|



















|











<

|



>
>
>






|











>
>
>
>
>










<
<














>
>














>
>
>
>
>
>
>
>
|
>
>







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


 * 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 "OFException.h"

#ifndef OF_HAVE_SOCKETS
# error No sockets available!
#endif

#import "OFSocket.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFBindFailedException \
 *	  OFBindFailedException.h ObjFW/OFBindFailedException.h
 *
 * @brief An exception indicating that binding a socket failed.
 */
@interface OFBindFailedException: OFException
{

	/* IP */
	OFString *_Nullable _host;
	uint16_t _port;
	/* IPX */
	uint8_t _packetType;
	/* UNIX socket */
	OFString *_Nullable _path;
	id _socket;
	int _errNo;
}

/**
 * @brief The host on which binding failed.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;

/**
 * @brief The port on which binding failed.
 */
@property (readonly, nonatomic) uint16_t port;

/**
 * @brief The IPX packet type for which binding failed.
 */
@property (readonly, nonatomic) uint8_t packetType;

/**
 * @brief The path on which binding failed.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path;

/**
 * @brief The socket which could not be bound.
 */
@property (readonly, nonatomic) id socket;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased bind failed exception.
 *
 * @param host The host on which binding failed
 * @param port The port on which binding failed
 * @param socket The socket which could not be bound
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased bind failed exception
 */
+ (instancetype)exceptionWithHost: (OFString *)host
			     port: (uint16_t)port
			   socket: (id)socket
			    errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased bind failed exception.
 *
 * @param port The IPX port to which binding failed
 * @param packetType The IPX packet type for which binding failed
 * @param socket The socket which could not be bound
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased bind failed exception
 */
+ (instancetype)exceptionWithPort: (uint16_t)port
		       packetType: (uint8_t)packetType
			   socket: (id)socket
			    errNo: (int)errNo;

/**
 * @brief Creates a new, autoreleased bind failed exception.
 *
 * @param path The path on which binding failed
 * @param socket The socket which could not be bound
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased bind failed exception
 */
+ (instancetype)exceptionWithPath: (OFString *)path
			   socket: (id)socket
			    errNo: (int)errNo;

/**
 * @brief Initializes an already allocated bind failed exception.
 *
 * @param host The host on which binding failed
 * @param port The port on which binding failed
 * @param socket The socket which could not be bound
122
123
124
125
126
127
128













129
130
131
 * @param errNo The errno of the error that occurred
 * @return An initialized bind failed exception
 */
- (instancetype)initWithPort: (uint16_t)port
		  packetType: (uint8_t)packetType
		      socket: (id)socket
		       errNo: (int)errNo;













@end

OF_ASSUME_NONNULL_END







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



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
 * @param errNo The errno of the error that occurred
 * @return An initialized bind failed exception
 */
- (instancetype)initWithPort: (uint16_t)port
		  packetType: (uint8_t)packetType
		      socket: (id)socket
		       errNo: (int)errNo;
/**
 * @brief Initializes an already allocated bind failed exception.
 *
 * @param path The path on which binding failed
 * @param socket The socket which could not be bound
 * @param errNo The errno of the error that occurred
 * @return An initialized bind failed exception
 */
- (instancetype)initWithPath: (OFString *)path
		      socket: (id)socket
		       errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFBindFailedException.m from [66d49adee0] to [0fa4a1273c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFBindFailedException.h"
#import "OFString.h"

@implementation OFBindFailedException
@synthesize host = _host, port = _port, packetType = _packetType;
@synthesize socket = _socket, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}


<
<
|



















|







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


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

#import "OFBindFailedException.h"
#import "OFString.h"

@implementation OFBindFailedException
@synthesize host = _host, port = _port, packetType = _packetType, path = _path;
@synthesize socket = _socket, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

46
47
48
49
50
51
52









53
54
55
56
57
58
59
			    errNo: (int)errNo
{
	return [[[self alloc] initWithPort: port
				packetType: packetType
				    socket: sock
				     errNo: errNo] autorelease];
}










- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithHost: (OFString *)host







>
>
>
>
>
>
>
>
>







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
			    errNo: (int)errNo
{
	return [[[self alloc] initWithPort: port
				packetType: packetType
				    socket: sock
				     errNo: errNo] autorelease];
}

+ (instancetype)exceptionWithPath: (OFString *)path
			   socket: (id)sock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithPath: path
				    socket: sock
				     errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithHost: (OFString *)host
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
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}



















- (void)dealloc
{
	[_host release];

	[_socket release];

	[super dealloc];
}

- (OFString *)description
{




	if (_host != nil)
		return [OFString stringWithFormat:
		    @"Binding to port %" @PRIu16 @" on host %@ failed in "
		    @"socket of type %@: %@",
		    _port, _host, [_socket class], of_strerror(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"Binding to port %" @PRIx16 @" for packet type %" @PRIx8
		    @" failed in socket of type %@: %@",
		    _port, _packetType, [_socket class], of_strerror(_errNo)];
}
@end







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




>







>
>
>
>
|



|




|


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
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithPath: (OFString *)path
		      socket: (id)sock
		       errNo: (int)errNo
{
	self = [super init];

	@try {
		_path = [path copy];
		_socket = [sock retain];
		_errNo = errNo;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_host release];
	[_path release];
	[_socket release];

	[super dealloc];
}

- (OFString *)description
{
	if (_path != nil)
		return [OFString stringWithFormat:
		    @"Binding to path %@ failed in socket of type %@: %@",
		    _path, [_socket class], OFStrError(_errNo)];
	else if (_host != nil)
		return [OFString stringWithFormat:
		    @"Binding to port %" @PRIu16 @" on host %@ failed in "
		    @"socket of type %@: %@",
		    _port, _host, [_socket class], OFStrError(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"Binding to port %" @PRIx16 @" for packet type %" @PRIx8
		    @" failed in socket of type %@: %@",
		    _port, _packetType, [_socket class], OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFChangeCurrentDirectoryPathFailedException.h from [32789dc600] to [fce19a8f8b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFString *path;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased change current directory path failed
 *	  exception.
 *
 * @param path The path of the directory to which the current path could not be
 *	       changed
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased change current directory path failed exception
 */
+ (instancetype)exceptionWithPath: (OFString *)path
			    errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated change directory failed exception.
 *
 * @param path The path of the directory to which the current path could not be
 *	       changed
 * @param errNo The errno of the error that occurred
 * @return An initialized change current directory path failed exception
 */
- (instancetype)initWithPath: (OFString *)path
		       errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<









|
<

|











>
>



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
@property (readonly, nonatomic) OFString *path;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased change current directory path failed
 *	  exception.
 *
 * @param path The path of the directory to which the current path could not be
 *	       changed
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased change current directory path failed exception
 */
+ (instancetype)exceptionWithPath: (OFString *)path errNo: (int)errNo;


+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated change directory failed exception.
 *
 * @param path The path of the directory to which the current path could not be
 *	       changed
 * @param errNo The errno of the error that occurred
 * @return An initialized change current directory path failed exception
 */
- (instancetype)initWithPath: (OFString *)path
		       errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFChangeCurrentDirectoryPathFailedException.m from [2ba63a593c] to [35ec7fdb32].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFChangeCurrentDirectoryPathFailedException.h"
#import "OFString.h"

@implementation OFChangeCurrentDirectoryPathFailedException
@synthesize path = _path, errNo = _errNo;






+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithPath: (OFString *)path
			    errNo: (int)errNo
{
	return [[[self alloc] initWithPath: path
				     errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPath: (OFString *)path
		       errNo: (int)errNo
{
	self = [super init];

	@try {
		_path = [path copy];
		_errNo = errNo;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_path release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to change the current directory path to %@: %@",
	    _path, of_strerror(_errNo)];
}
@end

<
<
|




















>
>
>
>
>






<
<
<
<
<
<
<
<
<
<
<
<
|
<













>
>
>
>
>












|


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


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

#import "OFChangeCurrentDirectoryPathFailedException.h"
#import "OFString.h"

@implementation OFChangeCurrentDirectoryPathFailedException
@synthesize path = _path, errNo = _errNo;

+ (instancetype)exceptionWithPath: (OFString *)path errNo: (int)errNo
{
	return [[[self alloc] initWithPath: path errNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}













- (instancetype)initWithPath: (OFString *)path errNo: (int)errNo

{
	self = [super init];

	@try {
		_path = [path copy];
		_errNo = errNo;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_path release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to change the current directory path to %@: %@",
	    _path, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFChecksumMismatchException.h from [608c4452e2] to [e6f142cd8f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFChecksumMismatchException.m from [d1823bdb3b] to [194677cc27].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFConditionBroadcastFailedException.h from [5078c9d198] to [d2d182c69d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	OFCondition *_condition;
	int _errNo;
}

/**
 * @brief The condition which could not be broadcasted.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFCondition *condition;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

/**
 * @brief Returns a new, autoreleased condition broadcast failed exception.
 *
 * @param condition The condition which could not be broadcasted
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased condition broadcast failed exception
 */
+ (instancetype)exceptionWithCondition: (nullable OFCondition *)condition
				 errNo: (int)errNo;



/**
 * @brief Initializes an already allocated condition broadcast failed exception.
 *
 * @param condition The condition which could not be broadcasted
 * @param errNo The errno of the error that occurred
 * @return An initialized condition broadcast failed exception
 */
- (instancetype)initWithCondition: (nullable OFCondition *)condition
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END







|













|

>
>








|






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
	OFCondition *_condition;
	int _errNo;
}

/**
 * @brief The condition which could not be broadcasted.
 */
@property (readonly, nonatomic) OFCondition *condition;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

/**
 * @brief Returns a new, autoreleased condition broadcast failed exception.
 *
 * @param condition The condition which could not be broadcasted
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased condition broadcast failed exception
 */
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated condition broadcast failed exception.
 *
 * @param condition The condition which could not be broadcasted
 * @param errNo The errno of the error that occurred
 * @return An initialized condition broadcast failed exception
 */
- (instancetype)initWithCondition: (OFCondition *)condition
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFConditionBroadcastFailedException.m from [150053cff3] to [0c18d20911].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35





36
37
38
39
40
41
42
43
44
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo
{
	return [[[self alloc] initWithCondition: condition
					  errNo: errNo] autorelease];
}






- (instancetype)initWithCondition: (OFCondition *)condition
			    errNo: (int)errNo
{
	self = [super init];

	_condition = [condition retain];
	_errNo = errNo;

	return self;







>
>
>
>
>
|
<







27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo
{
	return [[[self alloc] initWithCondition: condition
					  errNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithCondition: (OFCondition *)condition errNo: (int)errNo

{
	self = [super init];

	_condition = [condition retain];
	_errNo = errNo;

	return self;

Modified src/exceptions/OFConditionSignalFailedException.h from [22eaf3ab3a] to [92e2796a89].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	OFCondition *_condition;
	int _errNo;
}

/**
 * @brief The condition which could not be signaled.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFCondition *condition;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

/**
 * @brief Creates a new, autoreleased condition signal failed exception.
 *
 * @param condition The condition which could not be signaled
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased condition signal failed exception
 */
+ (instancetype)exceptionWithCondition: (nullable OFCondition *)condition
				 errNo: (int)errNo;



/**
 * @brief Initializes an already allocated condition signal failed exception.
 *
 * @param condition The condition which could not be signaled
 * @param errNo The errno of the error that occurred
 * @return An initialized condition signal failed exception
 */
- (instancetype)initWithCondition: (nullable OFCondition *)condition
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END







|













|

>
>








|






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
	OFCondition *_condition;
	int _errNo;
}

/**
 * @brief The condition which could not be signaled.
 */
@property (readonly, nonatomic) OFCondition *condition;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

/**
 * @brief Creates a new, autoreleased condition signal failed exception.
 *
 * @param condition The condition which could not be signaled
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased condition signal failed exception
 */
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated condition signal failed exception.
 *
 * @param condition The condition which could not be signaled
 * @param errNo The errno of the error that occurred
 * @return An initialized condition signal failed exception
 */
- (instancetype)initWithCondition: (OFCondition *)condition
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFConditionSignalFailedException.m from [6f920729b9] to [7b1b56675f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35





36
37
38
39
40
41
42
43
44
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo
{
	return [[[self alloc] initWithCondition: condition
					  errNo: errNo] autorelease];
}






- (instancetype)initWithCondition: (OFCondition *)condition
			    errNo: (int)errNo
{
	self = [super init];

	_condition = [condition retain];
	_errNo = errNo;

	return self;







>
>
>
>
>
|
<







27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo
{
	return [[[self alloc] initWithCondition: condition
					  errNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithCondition: (OFCondition *)condition errNo: (int)errNo

{
	self = [super init];

	_condition = [condition retain];
	_errNo = errNo;

	return self;

Modified src/exceptions/OFConditionStillWaitingException.h from [c466091667] to [63592271b5].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
{
	OFCondition *_condition;
}

/**
 * @brief The condition for which is still being waited.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFCondition *condition;

/**
 * @brief Creates a new, autoreleased condition still waiting exception.
 *
 * @param condition The condition for which is still being waited
 * @return A new, autoreleased condition still waiting exception
 */
+ (instancetype)exceptionWithCondition: (nullable OFCondition *)condition;



/**
 * @brief Initializes an already allocated condition still waiting exception.
 *
 * @param condition The condition for which is still being waited
 * @return An initialized condition still waiting exception
 */
- (instancetype)initWithCondition: (nullable OFCondition *)condition
    OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







|







|
>
>







|

>
>



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
{
	OFCondition *_condition;
}

/**
 * @brief The condition for which is still being waited.
 */
@property (readonly, nonatomic) OFCondition *condition;

/**
 * @brief Creates a new, autoreleased condition still waiting exception.
 *
 * @param condition The condition for which is still being waited
 * @return A new, autoreleased condition still waiting exception
 */
+ (instancetype)exceptionWithCondition: (OFCondition *)condition;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated condition still waiting exception.
 *
 * @param condition The condition for which is still being waited
 * @return An initialized condition still waiting exception
 */
- (instancetype)initWithCondition: (OFCondition *)condition
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFConditionStillWaitingException.m from [607c9157ff] to [28d38d170f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize condition = _condition;

+ (instancetype)exceptionWithCondition: (OFCondition *)condition
{
	return [[[self alloc] initWithCondition: condition] autorelease];
}

- (instancetype)init
{
	return [self initWithCondition: nil];
}

- (instancetype)initWithCondition: (OFCondition *)condition
{
	self = [super init];

	_condition = [condition retain];

	return self;
}






- (void)dealloc
{
	[_condition release];

	[super dealloc];
}

- (OFString *)description
{
	if (_condition != nil)
		return [OFString stringWithFormat:
		    @"Deallocation of a condition of type %@ was tried, even "
		    "though a thread was still waiting for it!",
		    _condition.class];
	else
		return @"Deallocation of a condition was tried, even though a "
		    "thread was still waiting for it!";
}
@end







|

|










>
>
>
>
>










<
|
|
|
|
<
<
<


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
@synthesize condition = _condition;

+ (instancetype)exceptionWithCondition: (OFCondition *)condition
{
	return [[[self alloc] initWithCondition: condition] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithCondition: (OFCondition *)condition
{
	self = [super init];

	_condition = [condition retain];

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_condition release];

	[super dealloc];
}

- (OFString *)description
{

	return [OFString stringWithFormat:
	    @"Deallocation of a condition of type %@ was tried, even though "
	    "a thread was still waiting for it!",
	    _condition.class];



}
@end

Modified src/exceptions/OFConditionWaitFailedException.h from [f1a62ef0f5] to [4c780f1cdc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	OFCondition *_condition;
	int _errNo;
}

/**
 * @brief The condition for which could not be waited.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFCondition *condition;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

/**
 * @brief Creates a new, autoreleased condition wait failed exception.
 *
 * @param condition The condition for which could not be waited
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased condition wait failed exception
 */
+ (instancetype)exceptionWithCondition: (nullable OFCondition *)condition
				 errNo: (int)errNo;



/**
 * @brief Initializes an already allocated condition wait failed exception.
 *
 * @param condition The condition for which could not be waited
 * @param errNo The errno of the error that occurred
 * @return An initialized condition wait failed exception
 */
- (instancetype)initWithCondition: (nullable OFCondition *)condition
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END







|













|

>
>








|






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
	OFCondition *_condition;
	int _errNo;
}

/**
 * @brief The condition for which could not be waited.
 */
@property (readonly, nonatomic) OFCondition *condition;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

/**
 * @brief Creates a new, autoreleased condition wait failed exception.
 *
 * @param condition The condition for which could not be waited
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased condition wait failed exception
 */
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated condition wait failed exception.
 *
 * @param condition The condition for which could not be waited
 * @param errNo The errno of the error that occurred
 * @return An initialized condition wait failed exception
 */
- (instancetype)initWithCondition: (OFCondition *)condition
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFConditionWaitFailedException.m from [3628b82a85] to [5702b9d98a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35





36
37
38
39
40
41
42
43
44
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo
{
	return [[[self alloc] initWithCondition: condition
					  errNo: errNo] autorelease];
}






- (instancetype)initWithCondition: (OFCondition *)condition
			    errNo: (int)errNo
{
	self = [super init];

	_condition = [condition retain];
	_errNo = errNo;

	return self;







>
>
>
>
>
|
<







27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
+ (instancetype)exceptionWithCondition: (OFCondition *)condition
				 errNo: (int)errNo
{
	return [[[self alloc] initWithCondition: condition
					  errNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithCondition: (OFCondition *)condition errNo: (int)errNo

{
	self = [super init];

	_condition = [condition retain];
	_errNo = errNo;

	return self;

Modified src/exceptions/OFConnectionFailedException.h from [2ceab3ca9f] to [2687b6eb31].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFException.h"

#ifndef OF_HAVE_SOCKETS
# error No sockets available!
#endif

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFConnectionFailedException \
 *	  OFConnectionFailedException.h ObjFW/OFConnectionFailedException.h
 *
 * @brief An exception indicating that a connection could not be established.
 */
@interface OFConnectionFailedException: OFException
{
	id _socket;
	OFString *_host;
	uint16_t _port;
	unsigned char _node[IPX_NODE_LEN];
	uint32_t _network;


	int _errNo;
}

/**
 * @brief The socket which could not connect.
 */
@property (readonly, nonatomic) id socket;

/**
 * @brief The host to which the connection failed.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;

/**
 * @brief The port on the host to which the connection failed.
 */
@property (readonly, nonatomic) uint16_t port;

/**
 * @brief The IPX node to which the connection failed.
 */
@property (readonly, nonatomic) unsigned char *node;

/**
 * @brief The IPX network of the node to which the connection failed.
 */
@property (readonly, nonatomic) uint32_t network;











/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased connection failed exception.
 *
 * @param host The host to which the connection failed
 * @param port The port on the host to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased connection failed exception
 */
+ (instancetype)exceptionWithHost: (nullable OFString *)host
			     port: (uint16_t)port
			   socket: (id)socket
			    errNo: (int)errNo;

/**
 * @brief Creates a new, autoreleased connection failed exception.
 *
 * @param node The node to which the connection failed
 * @param network The IPX network of the node to which the connection failed
 * @param port The port on the node to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased connection failed exception
 */
+ (instancetype)exceptionWithNode: (unsigned char [_Nullable IPX_NODE_LEN])node
			  network: (uint32_t)network
			     port: (uint16_t)port
			   socket: (id)socket
			    errNo: (int)errNo;













- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated connection failed exception.
 *
 * @param host The host to which the connection failed
 * @param port The port on the host to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return An initialized connection failed exception
 */
- (instancetype)initWithHost: (nullable OFString *)host
			port: (uint16_t)port
		      socket: (id)socket
		       errNo: (int)errNo;

/**
 * @brief Initializes an already allocated connection failed exception.
 *
 * @param node The node to which the connection failed
 * @param network The IPX network of the node to which the connection failed
 * @param port The port on the node to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return An initialized connection failed exception
 */
- (instancetype)initWithNode: (unsigned char [_Nullable IPX_NODE_LEN])node
		     network: (uint32_t)network
			port: (uint16_t)port
		      socket: (id)socket
		       errNo: (int)errNo;














@end

OF_ASSUME_NONNULL_END

<
<
|



















|











<
|



>
>



<
<
<
<
<




















>
>
>
>
>
>
>
>
>
>





<
<









|




















>
>
>
>
>
>
>
>
>
>
>
>
|










|



















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



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


 * 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 "OFException.h"

#ifndef OF_HAVE_SOCKETS
# error No sockets available!
#endif

#import "OFSocket.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFConnectionFailedException \
 *	  OFConnectionFailedException.h ObjFW/OFConnectionFailedException.h
 *
 * @brief An exception indicating that a connection could not be established.
 */
@interface OFConnectionFailedException: OFException
{

	OFString *_Nullable _host;
	uint16_t _port;
	unsigned char _node[IPX_NODE_LEN];
	uint32_t _network;
	OFString *_Nullable _path;
	id _socket;
	int _errNo;
}






/**
 * @brief The host to which the connection failed.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;

/**
 * @brief The port on the host to which the connection failed.
 */
@property (readonly, nonatomic) uint16_t port;

/**
 * @brief The IPX node to which the connection failed.
 */
@property (readonly, nonatomic) unsigned char *node;

/**
 * @brief The IPX network of the node to which the connection failed.
 */
@property (readonly, nonatomic) uint32_t network;

/**
 * @brief The path to which the connection failed.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path;

/**
 * @brief The socket which could not connect.
 */
@property (readonly, nonatomic) id socket;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased connection failed exception.
 *
 * @param host The host to which the connection failed
 * @param port The port on the host to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased connection failed exception
 */
+ (instancetype)exceptionWithHost: (OFString *)host
			     port: (uint16_t)port
			   socket: (id)socket
			    errNo: (int)errNo;

/**
 * @brief Creates a new, autoreleased connection failed exception.
 *
 * @param node The node to which the connection failed
 * @param network The IPX network of the node to which the connection failed
 * @param port The port on the node to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased connection failed exception
 */
+ (instancetype)exceptionWithNode: (unsigned char [_Nullable IPX_NODE_LEN])node
			  network: (uint32_t)network
			     port: (uint16_t)port
			   socket: (id)socket
			    errNo: (int)errNo;

/**
 * @brief Creates a new, autoreleased connection failed exception.
 *
 * @param path The path to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased connection failed exception
 */
+ (instancetype)exceptionWithPath: (OFString *)path
			   socket: (id)socket
			    errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated connection failed exception.
 *
 * @param host The host to which the connection failed
 * @param port The port on the host to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return An initialized connection failed exception
 */
- (instancetype)initWithHost: (OFString *)host
			port: (uint16_t)port
		      socket: (id)socket
		       errNo: (int)errNo;

/**
 * @brief Initializes an already allocated connection failed exception.
 *
 * @param node The node to which the connection failed
 * @param network The IPX network of the node to which the connection failed
 * @param port The port on the node to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return An initialized connection failed exception
 */
- (instancetype)initWithNode: (unsigned char [_Nullable IPX_NODE_LEN])node
		     network: (uint32_t)network
			port: (uint16_t)port
		      socket: (id)socket
		       errNo: (int)errNo;

/**
 * @brief Initializes an already allocated connection failed exception.
 *
 * @param path The path to which the connection failed
 * @param socket The socket which could not connect
 * @param errNo The errno of the error that occurred
 * @return An initialized connection failed exception
 */
- (instancetype)initWithPath: (OFString *)path
		      socket: (id)socket
		       errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFConnectionFailedException.m from [7c382ce8cb] to [1671ca3ac8].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFConnectionFailedException.h"
#import "OFString.h"

@implementation OFConnectionFailedException
@synthesize host = _host, port = _port, network = _network, socket = _socket;
@synthesize errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithHost: (OFString *)host
			     port: (uint16_t)port
			   socket: (id)sock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithHost: host
				      port: port
				    socket: sock
				     errNo: errNo] autorelease];
}






+ (instancetype)exceptionWithNode: (unsigned char [IPX_NODE_LEN])node
			  network: (uint32_t)network
			     port: (uint16_t)port
			   socket: (id)sock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithNode: node
				   network: network
				      port: port
				    socket: sock
				     errNo: errNo] autorelease];
}

- (instancetype)init


{
	OF_INVALID_INIT_METHOD



}

- (instancetype)initWithHost: (OFString *)host
			port: (uint16_t)port
		      socket: (id)sock
		       errNo: (int)errNo
{

<
<
|



















|
|
<
<
<
<
<











>
>
>
>
>














|
>
>

<
>
>
>







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


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

#import "OFConnectionFailedException.h"
#import "OFString.h"

@implementation OFConnectionFailedException
@synthesize host = _host, port = _port, network = _network, path = _path;
@synthesize socket = _socket, errNo = _errNo;






+ (instancetype)exceptionWithHost: (OFString *)host
			     port: (uint16_t)port
			   socket: (id)sock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithHost: host
				      port: port
				    socket: sock
				     errNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithNode: (unsigned char [IPX_NODE_LEN])node
			  network: (uint32_t)network
			     port: (uint16_t)port
			   socket: (id)sock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithNode: node
				   network: network
				      port: port
				    socket: sock
				     errNo: errNo] autorelease];
}

+ (instancetype)exceptionWithPath: (OFString *)path
			   socket: (id)sock
			    errNo: (int)errNo
{

	return [[[self alloc] initWithPath: path
				    socket: sock
				     errNo: errNo] autorelease];
}

- (instancetype)initWithHost: (OFString *)host
			port: (uint16_t)port
		      socket: (id)sock
		       errNo: (int)errNo
{
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
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
























- (void)dealloc
{
	[_host release];

	[_socket release];

	[super dealloc];
}

- (unsigned char *)node
{
	return _node;
}

- (OFString *)description
{





	if (_host != nil)
		return [OFString stringWithFormat:
		    @"A connection to %@ on port %" @PRIu16 @" could not be "
		    @"established in socket of type %@: %@",
		    _host, _port, [_socket class], of_strerror(_errNo)];
	else if (memcmp(_node, "\0\0\0\0\0", IPX_NODE_LEN) == 0)
		return [OFString stringWithFormat:
		    @"A connection to %02X%02X%02X%02X%02X%02X port %" @PRIu16
		    @" on network %" @PRIX32 " could not be established in "
		    @"socket of type %@: %@",
		    _node[0], _node[1], _node[2], _node[3], _node[4], _node[5],
		    _port, _network, [_socket class], of_strerror(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"A connection could not be established in socket of "
		    @"type %@: %@",
		    [_socket class], of_strerror(_errNo)];
}
@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
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
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithPath: (OFString *)path
		      socket: (id)sock
		       errNo: (int)errNo
{
	self = [super init];

	@try {
		_path = [path copy];
		_socket = [sock retain];
		_errNo = errNo;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_host release];
	[_path release];
	[_socket release];

	[super dealloc];
}

- (unsigned char *)node
{
	return _node;
}

- (OFString *)description
{
	if (_path != nil)
		return [OFString stringWithFormat:
		    @"A connection to %@ could not be established in socket of "
		    @"type %@: %@",
		    _path, [_socket class], OFStrError(_errNo)];
	else if (_host != nil)
		return [OFString stringWithFormat:
		    @"A connection to %@ on port %" @PRIu16 @" could not be "
		    @"established in socket of type %@: %@",
		    _host, _port, [_socket class], OFStrError(_errNo)];
	else if (memcmp(_node, "\0\0\0\0\0", IPX_NODE_LEN) == 0)
		return [OFString stringWithFormat:
		    @"A connection to %02X%02X%02X%02X%02X%02X port %" @PRIu16
		    @" on network %" @PRIX32 " could not be established in "
		    @"socket of type %@: %@",
		    _node[0], _node[1], _node[2], _node[3], _node[4], _node[5],
		    _port, _network, [_socket class], OFStrError(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"A connection could not be established in socket of "
		    @"type %@: %@",
		    [_socket class], OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFCopyItemFailedException.h from [a609d68d24] to [6dbfc6ae6a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@interface OFCopyItemFailedException: OFException
{
	OFURL *_sourceURL, *_destinationURL;
	int _errNo;
}

/**
 * @brief The path of the source item.
 */
@property (readonly, nonatomic) OFURL *sourceURL;

/**
 * @brief The destination path.
 */
@property (readonly, nonatomic) OFURL *destinationURL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased copy item failed exception.
 *
 * @param sourceURL The original path
 * @param destinationURL The new path
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased copy item failed exception
 */
+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
			destinationURL: (OFURL *)destinationURL
				 errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated copy item failed exception.
 *
 * @param sourceURL The original path
 * @param destinationURL The new path
 * @param errNo The errno of the error that occurred
 * @return An initialized copy item failed exception
 */
- (instancetype)initWithSourceURL: (OFURL *)sourceURL
		   destinationURL: (OFURL *)destinationURL
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







|




|








<
<



|
|







|




|
|






>
>



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
@interface OFCopyItemFailedException: OFException
{
	OFURL *_sourceURL, *_destinationURL;
	int _errNo;
}

/**
 * @brief The URL of the source item.
 */
@property (readonly, nonatomic) OFURL *sourceURL;

/**
 * @brief The destination URL.
 */
@property (readonly, nonatomic) OFURL *destinationURL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased copy item failed exception.
 *
 * @param sourceURL The URL of the source item
 * @param destinationURL The destination URL
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased copy item failed exception
 */
+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
			destinationURL: (OFURL *)destinationURL
				 errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated copy item failed exception.
 *
 * @param sourceURL The URL of the source item
 * @param destinationURL The destination URL
 * @param errNo The errno of the error that occurred
 * @return An initialized copy item failed exception
 */
- (instancetype)initWithSourceURL: (OFURL *)sourceURL
		   destinationURL: (OFURL *)destinationURL
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFCopyItemFailedException.m from [1ccbb5f239] to [db6f54b209].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
69
70
71
72
73
74
75
76
77
78

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"Failed to copy item %@ to %@: %@",
	    _sourceURL, _destinationURL, of_strerror(_errNo)];
}
@end







|


67
68
69
70
71
72
73
74
75
76

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"Failed to copy item %@ to %@: %@",
	    _sourceURL, _destinationURL, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFCreateDirectoryFailedException.h from [f56bfddfea] to [9fb2f6c383].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased create directory failed exception.
 *
 * @param URL The URL of the directory which could not be created
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased create directory failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL
			   errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated create directory failed exception.
 *
 * @param URL The URL of the directory which could not be created
 * @param errNo The errno of the error that occurred
 * @return An initialized create directory failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<







|
<

|










>
>



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
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased create directory failed exception.
 *
 * @param URL The URL of the directory which could not be created
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased create directory failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo;


+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated create directory failed exception.
 *
 * @param URL The URL of the directory which could not be created
 * @param errNo The errno of the error that occurred
 * @return An initialized create directory failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFCreateDirectoryFailedException.m from [d8d7921ecb] to [c77cc11072].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize URL = _URL, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL
			    errNo: (int)errNo
{
	return [[[self alloc] initWithURL: URL
				    errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

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

	@try {
		_URL = [URL copy];
		_errNo = errNo;
	} @catch (id e) {







|
<

|
<







|
<







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
@synthesize URL = _URL, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo

{
	return [[[self alloc] initWithURL: URL errNo: errNo] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURL: (OFURL *)URL errNo: (int)errNo

{
	self = [super init];

	@try {
		_URL = [URL copy];
		_errNo = errNo;
	} @catch (id e) {
63
64
65
66
67
68
69
70
71
72

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to create directory %@: %@", _URL, of_strerror(_errNo)];
}
@end







|


58
59
60
61
62
63
64
65
66
67

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to create directory %@: %@", _URL, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFCreateSymbolicLinkFailedException.h from [15f0d4bcc2] to [3600a35794].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFString *target;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased create symbolic link failed exception.
 *
 * @param URL The URL where the symlink should have been created
 * @param target The target for the symbolic link
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased create symbolic link failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL
			  target: (OFString *)target
			   errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated create symbolic link failed
 *	  exception.
 *
 * @param URL The URL where the symlink should have been created
 * @param target The target for the symbolic link
 * @param errNo The errno of the error that occurred
 * @return An initialized create symbolic link failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		     target: (OFString *)target
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<












|













>
>



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
@property (readonly, nonatomic) OFString *target;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased create symbolic link failed exception.
 *
 * @param URL The URL where the symlink should have been created
 * @param target The target for the symbolic link
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased create symbolic link failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL
			  target: (OFString *)target
			   errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated create symbolic link failed
 *	  exception.
 *
 * @param URL The URL where the symlink should have been created
 * @param target The target for the symbolic link
 * @param errNo The errno of the error that occurred
 * @return An initialized create symbolic link failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		     target: (OFString *)target
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFCreateSymbolicLinkFailedException.m from [389fe64f93] to [98cc87f63e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
69
70
71
72
73
74
75
76
77
78
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to create symbolic link %@ with target %@: %@",
	    _URL, _target, of_strerror(_errNo)];
}
@end







|


67
68
69
70
71
72
73
74
75
76
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to create symbolic link %@ with target %@: %@",
	    _URL, _target, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFCreateWindowsRegistryKeyFailedException.h from [28fcec9697] to [f321fddf5e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFCreateWindowsRegistryKeyFailedException.m from [e837a7effd] to [661d98b08e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
78
79
80
81
82
83
84
85
86
87
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to create subkey at path %@: %@",
	    _path, of_windows_status_to_string(_status)];
}
@end







|


76
77
78
79
80
81
82
83
84
85
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to create subkey at path %@: %@",
	    _path, OFWindowsStatusToString(_status)];
}
@end

Modified src/exceptions/OFDNSQueryFailedException.h from [a096665706] to [852291054a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  OFDNSQueryFailedException.h ObjFW/OFDNSQueryFailedException.h
 *
 * @brief An exception indicating that a DNS query failed.
 */
@interface OFDNSQueryFailedException: OFException
{
	OFDNSQuery *_query;
	of_dns_resolver_error_t _error;
}

/**
 * @brief The query which could not be performed.
 */
@property (readonly, nonatomic) OFDNSQuery *query;

/**
 * @brief The error from the resolver.
 */
@property (readonly, nonatomic) of_dns_resolver_error_t error;

/**
 * @brief Creates a new, autoreleased DNS query failed exception.
 *
 * @param query The query which could not be performed
 * @param error The error from the resolver
 * @return A new, autoreleased address translation failed exception
 */
+ (instancetype)exceptionWithQuery: (OFDNSQuery *)query

			     error: (of_dns_resolver_error_t)error;


/**
 * @brief Initializes an already allocated DNS query failed exception.
 *
 * @param query The query which could not be performed
 * @param error The error from the resolver
 * @return An initialized address translation failed exception
 */
- (instancetype)initWithQuery: (OFDNSQuery *)query

			error: (of_dns_resolver_error_t)error;

@end

#ifdef __cplusplus
extern "C" {
#endif
extern OFString *of_dns_resolver_error_to_string(of_dns_resolver_error_t error);

#ifdef __cplusplus
}
#endif

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
 *	  OFDNSQueryFailedException.h ObjFW/OFDNSQueryFailedException.h
 *
 * @brief An exception indicating that a DNS query failed.
 */
@interface OFDNSQueryFailedException: OFException
{
	OFDNSQuery *_query;
	OFDNSResolverErrorCode _errorCode;
}

/**
 * @brief The query which could not be performed.
 */
@property (readonly, nonatomic) OFDNSQuery *query;

/**
 * @brief The error code from the resolver.
 */
@property (readonly, nonatomic) OFDNSResolverErrorCode errorCode;

/**
 * @brief Creates a new, autoreleased DNS query failed exception.
 *
 * @param query The query which could not be performed
 * @param errorCode The error from the resolver
 * @return A new, autoreleased address translation failed exception
 */
+ (instancetype)exceptionWithQuery: (OFDNSQuery *)query
			 errorCode: (OFDNSResolverErrorCode)errorCode;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated DNS query failed exception.
 *
 * @param query The query which could not be performed
 * @param errorCode The error from the resolver
 * @return An initialized address translation failed exception
 */
- (instancetype)initWithQuery: (OFDNSQuery *)query
		    errorCode: (OFDNSResolverErrorCode)errorCode;

- (instancetype)init OF_UNAVAILABLE;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern OFString *OFDNSResolverErrorCodeDescription(
    OFDNSResolverErrorCode errorCode);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFDNSQueryFailedException.m from [1f9661bb22] to [d34eb3c9de].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFDNSQueryFailedException.h"
#import "OFString.h"

OFString *
of_dns_resolver_error_to_string(of_dns_resolver_error_t error)
{
	switch (error) {
	case OF_DNS_RESOLVER_ERROR_TIMEOUT:
		return @"The query timed out.";
	case OF_DNS_RESOLVER_ERROR_CANCELED:
		return @"The query was canceled.";
	case OF_DNS_RESOLVER_ERROR_NO_RESULT:
		return @"No result for the specified host with the specified "
		    @"type and class.";
	case OF_DNS_RESOLVER_ERROR_SERVER_INVALID_FORMAT:
		return @"The server considered the query to be malformed.";
	case OF_DNS_RESOLVER_ERROR_SERVER_FAILURE:
		return @"The server was unable to process due to an internal "
		    @"error.";
	case OF_DNS_RESOLVER_ERROR_SERVER_NAME_ERROR:
		return @"The server returned an error that the domain does not "
		    @"exist.";
	case OF_DNS_RESOLVER_ERROR_SERVER_NOT_IMPLEMENTED:
		return @"The server does not have support for the requested "
		    @"query.";
	case OF_DNS_RESOLVER_ERROR_SERVER_REFUSED:
		return @"The server refused the query.";
	case OF_DNS_RESOLVER_ERROR_NO_NAME_SERVER:
		return @"There was no name server to query.";
	default:
		return @"Unknown error.";
	}
}

@implementation OFDNSQueryFailedException
@synthesize query = _query, error = _error;

+ (instancetype)exceptionWithQuery: (OFDNSQuery *)query
			     error: (of_dns_resolver_error_t)error
{
	return [[[self alloc] initWithQuery: query
				      error: error] autorelease];





}

- (instancetype)initWithQuery: (OFDNSQuery *)query
			error: (of_dns_resolver_error_t)error
{
	self = [super init];

	@try {
		_query = [query copy];
		_error = error;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_query release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"DNS query %@ could not be performed: %@",
	    _query, of_dns_resolver_error_to_string(_error)];
}
@end

<
<
|



















|

|
|

|

|


|

|


|


|


|

|







|


|


|
>
>
>
>
>



|





|







>
>
>
>
>












|


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


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

#import "OFDNSQueryFailedException.h"
#import "OFString.h"

OFString *
OFDNSResolverErrorCodeDescription(OFDNSResolverErrorCode errorCode)
{
	switch (errorCode) {
	case OFDNSResolverErrorCodeTimeout:
		return @"The query timed out.";
	case OFDNSResolverErrorCodeCanceled:
		return @"The query was canceled.";
	case OFDNSResolverErrorCodeNoResult:
		return @"No result for the specified host with the specified "
		    @"type and class.";
	case OFDNSResolverErrorCodeServerInvalidFormat:
		return @"The server considered the query to be malformed.";
	case OFDNSResolverErrorCodeServerFailure:
		return @"The server was unable to process due to an internal "
		    @"error.";
	case OFDNSResolverErrorCodeServerNameError:
		return @"The server returned an error that the domain does not "
		    @"exist.";
	case OFDNSResolverErrorCodeServerNotImplemented:
		return @"The server does not have support for the requested "
		    @"query.";
	case OFDNSResolverErrorCodeServerRefused:
		return @"The server refused the query.";
	case OFDNSResolverErrorCodeNoNameServer:
		return @"There was no name server to query.";
	default:
		return @"Unknown error.";
	}
}

@implementation OFDNSQueryFailedException
@synthesize query = _query, errorCode = _errorCode;

+ (instancetype)exceptionWithQuery: (OFDNSQuery *)query
			 errorCode: (OFDNSResolverErrorCode)errorCode
{
	return [[[self alloc] initWithQuery: query
				  errorCode: errorCode] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithQuery: (OFDNSQuery *)query
		    errorCode: (OFDNSResolverErrorCode)errorCode
{
	self = [super init];

	@try {
		_query = [query copy];
		_errorCode = errorCode;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_query release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"DNS query %@ could not be performed: %@",
	    _query, OFDNSResolverErrorCodeDescription(_errorCode)];
}
@end

Modified src/exceptions/OFDeleteWindowsRegistryKeyFailedException.h from [eaf8e19b26] to [ce80a5fb98].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFDeleteWindowsRegistryKeyFailedException.m from [f1fda05f5e] to [123f06f5d8].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
65
66
67
68
69
70
71
72
73
74
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to delete subkey at path %@: %@",
	    _subkeyPath, of_windows_status_to_string(_status)];
}
@end







|


63
64
65
66
67
68
69
70
71
72
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to delete subkey at path %@: %@",
	    _subkeyPath, OFWindowsStatusToString(_status)];
}
@end

Modified src/exceptions/OFDeleteWindowsRegistryValueFailedException.h from [c653480630] to [fb187a24ce].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  ObjFW/OFDeleteWindowsRegistryValueFailedException.h
 *
 * @brief An exception indicating that deleting a Windows registry value failed.
 */
@interface OFDeleteWindowsRegistryValueFailedException: OFException
{
	OFWindowsRegistryKey *_registryKey;
	OFString *_Nullable _value;
	LSTATUS _status;
}

/**
 * @brief The registry key on which deleting the value failed.
 */
@property (readonly, nonatomic) OFWindowsRegistryKey *registryKey;

/**
 * @brief The value which could not be deleted.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *value;

/**
 * @brief The status returned by RegDeleteValueEx().
 */
@property (readonly, nonatomic) LSTATUS status;

/**
 * @brief Creates a new, autoreleased delete Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which deleting the value failed
 * @param value The value which could not be deleted
 * @param status The status returned by RegDeleteValueEx()
 * @return A new, autoreleased delete Windows registry value failed exception
 */
+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
				   value: (nullable OFString *)value
				  status: (LSTATUS)status;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated delete Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which deleting the value failed
 * @param value The value which could not be deleted
 * @param status The status returned by RegDeleteValueEx()
 * @return An initialized delete Windows registry value failed exception
 */
- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			      value: (nullable OFString *)value
			     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;
@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
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
 *	  ObjFW/OFDeleteWindowsRegistryValueFailedException.h
 *
 * @brief An exception indicating that deleting a Windows registry value failed.
 */
@interface OFDeleteWindowsRegistryValueFailedException: OFException
{
	OFWindowsRegistryKey *_registryKey;
	OFString *_Nullable _valueName;
	LSTATUS _status;
}

/**
 * @brief The registry key on which deleting the value failed.
 */
@property (readonly, nonatomic) OFWindowsRegistryKey *registryKey;

/**
 * @brief The name of the value which could not be deleted.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *valueName;

/**
 * @brief The status returned by RegDeleteValueEx().
 */
@property (readonly, nonatomic) LSTATUS status;

/**
 * @brief Creates a new, autoreleased delete Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which deleting the value failed
 * @param valueName The name of the value which could not be deleted
 * @param status The status returned by RegDeleteValueEx()
 * @return A new, autoreleased delete Windows registry value failed exception
 */
+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			       valueName: (nullable OFString *)valueName
				  status: (LSTATUS)status;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated delete Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which deleting the value failed
 * @param valueName The name of the value which could not be deleted
 * @param status The status returned by RegDeleteValueEx()
 * @return An initialized delete Windows registry value failed exception
 */
- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			  valueName: (nullable OFString *)valueName
			     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFDeleteWindowsRegistryValueFailedException.m from [9322794e56] to [efa255c388].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFDeleteWindowsRegistryValueFailedException.h"

#import "OFData.h"

@implementation OFDeleteWindowsRegistryValueFailedException
@synthesize registryKey = _registryKey, value = _value, status = _status;


+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
				   value: (OFString *)value
				  status: (LSTATUS)status
{
	return [[[self alloc] initWithRegistryKey: registryKey
					    value: value
					   status: status] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			      value: (OFString *)value
			     status: (LSTATUS)status
{
	self = [super init];

	@try {
		_registryKey = [registryKey retain];
		_value = [value copy];
		_status = status;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_registryKey release];
	[_value release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to delete value %@: %@",
	    _value, of_windows_status_to_string(_status)];
}
@end

<
<
|




















|
>


|



|









|






|












|







|
|


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


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

#import "OFDeleteWindowsRegistryValueFailedException.h"

#import "OFData.h"

@implementation OFDeleteWindowsRegistryValueFailedException
@synthesize registryKey = _registryKey, valueName = _valueName;
@synthesize status = _status;

+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			       valueName: (OFString *)valueName
				  status: (LSTATUS)status
{
	return [[[self alloc] initWithRegistryKey: registryKey
					valueName: valueName
					   status: status] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			  valueName: (OFString *)valueName
			     status: (LSTATUS)status
{
	self = [super init];

	@try {
		_registryKey = [registryKey retain];
		_valueName = [valueName copy];
		_status = status;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_registryKey release];
	[_valueName release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to delete value named %@: %@",
	    _valueName, OFWindowsStatusToString(_status)];
}
@end

Modified src/exceptions/OFEnumerationMutationException.h from [86686cd9d1] to [2ab98f2cf4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
}

/**
 * @brief The object which was mutated during enumeration.
 */
@property (readonly, nonatomic) id object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased enumeration mutation exception.
 *
 * @param object The object which was mutated during enumeration
 * @return A new, autoreleased enumeration mutation exception
 */
+ (instancetype)exceptionWithObject: (id)object;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated enumeration mutation exception.
 *
 * @param object The object which was mutated during enumeration
 * @return An initialized enumeration mutation exception
 */
- (instancetype)initWithObject: (id)object OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<








|








>
>



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
}

/**
 * @brief The object which was mutated during enumeration.
 */
@property (readonly, nonatomic) id object;



/**
 * @brief Creates a new, autoreleased enumeration mutation exception.
 *
 * @param object The object which was mutated during enumeration
 * @return A new, autoreleased enumeration mutation exception
 */
+ (instancetype)exceptionWithObject: (id)object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated enumeration mutation exception.
 *
 * @param object The object which was mutated during enumeration
 * @return An initialized enumeration mutation exception
 */
- (instancetype)initWithObject: (id)object OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFEnumerationMutationException.m from [7fc716b283] to [f408c0df64].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFException.h from [9ead1c434e] to [e8334cf8f9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFString;

#define OF_BACKTRACE_SIZE 16

#if defined(OF_WINDOWS) && defined(OF_HAVE_SOCKETS)
# ifndef EADDRINUSE
#  define EADDRINUSE WSAEADDRINUSE
# endif
# ifndef EADDRNOTAVAIL
#  define EADDRNOTAVAIL WSAEADDRNOTAVAIL







|







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

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFString;

#define OFBacktraceSize 16

#if defined(OF_WINDOWS) && defined(OF_HAVE_SOCKETS)
# ifndef EADDRINUSE
#  define EADDRINUSE WSAEADDRINUSE
# endif
# ifndef EADDRNOTAVAIL
#  define EADDRNOTAVAIL WSAEADDRNOTAVAIL
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
#  define EUSERS WSAEUSERS
# endif
# ifndef EWOULDBLOCK
#  define EWOULDBLOCK WSAEWOULDBLOCK
# endif
#endif





/**
 * @class OFException OFException.h ObjFW/OFException.h
 *
 * @brief The base class for all exceptions in ObjFW
 *
 * The OFException class is the base class for all exceptions in ObjFW, except
 * the OFAllocFailedException.
 */
@interface OFException: OFObject
{
	void *_backtrace[OF_BACKTRACE_SIZE];
}

/**
 * @brief Creates a new, autoreleased exception.
 *
 * @return A new, autoreleased exception
 */







>
>
>
>










|







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
#  define EUSERS WSAEUSERS
# endif
# ifndef EWOULDBLOCK
#  define EWOULDBLOCK WSAEWOULDBLOCK
# endif
#endif

#ifndef EWOULDBLOCK
# define EWOULDBLOCK EAGAIN
#endif

/**
 * @class OFException OFException.h ObjFW/OFException.h
 *
 * @brief The base class for all exceptions in ObjFW
 *
 * The OFException class is the base class for all exceptions in ObjFW, except
 * the OFAllocFailedException.
 */
@interface OFException: OFObject
{
	void *_backtrace[OFBacktraceSize];
}

/**
 * @brief Creates a new, autoreleased exception.
 *
 * @return A new, autoreleased exception
 */
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
 */
- (nullable OFArray OF_GENERIC(OFString *) *)backtrace;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern OFString *of_strerror(int errNo);
#ifdef OF_WINDOWS
extern OFString *of_windows_status_to_string(LSTATUS status);
#endif
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







|

|






174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
 */
- (nullable OFArray OF_GENERIC(OFString *) *)backtrace;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern OFString *OFStrError(int errNo);
#ifdef OF_WINDOWS
extern OFString *OFWindowsStatusToString(LSTATUS status);
#endif
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFException.m from [bf7ff24437] to [21f1fa3002].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#endif

#import "OFException.h"
#import "OFArray.h"
#import "OFLocale.h"



#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFLockFailedException.h"
#import "OFUnlockFailedException.h"

#if !defined(HAVE_STRERROR_R) && defined(OF_HAVE_THREADS)
# import "mutex.h"
#endif

#if defined(OF_WINDOWS) && defined(OF_HAVE_SOCKETS)
# include <winerror.h>
#endif

#if defined(OF_ARM) && !defined(__ARM_DWARF_EH__)
# define HAVE_ARM_EHABI_EXCEPTIONS
#endif

struct _Unwind_Context;
typedef enum {
	_URC_OK		  = 0,
	_URC_END_OF_STACK = 5
}_Unwind_Reason_Code;

struct backtrace_ctx {
	void **backtrace;
	uint8_t i;
};

#ifdef HAVE__UNWIND_BACKTRACE
extern _Unwind_Reason_Code _Unwind_Backtrace(
    _Unwind_Reason_Code (*)(struct _Unwind_Context *, void *), void *);
#endif
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *);
#else
extern int _Unwind_VRS_Get(struct _Unwind_Context *, int, uint32_t, int,
    void *);
#endif

#if !defined(HAVE_STRERROR_R) && defined(OF_HAVE_THREADS)
static of_mutex_t mutex;

OF_CONSTRUCTOR()
{
	if (!of_mutex_new(&mutex))

		@throw [OFInitializationFailedException exception];



}
#endif

OFString *
of_strerror(int errNo)
{
	OFString *ret;
#ifdef HAVE_STRERROR_R
	char buffer[256];
#endif

	if (errNo == 0)







>
>
>







<
<
<
<












|

|
















|



|
>
|
>
>
>




|







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
#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#endif

#import "OFException.h"
#import "OFArray.h"
#import "OFLocale.h"
#ifdef OF_HAVE_THREADS
# import "OFPlainMutex.h"
#endif
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFLockFailedException.h"
#import "OFUnlockFailedException.h"





#if defined(OF_WINDOWS) && defined(OF_HAVE_SOCKETS)
# include <winerror.h>
#endif

#if defined(OF_ARM) && !defined(__ARM_DWARF_EH__)
# define HAVE_ARM_EHABI_EXCEPTIONS
#endif

struct _Unwind_Context;
typedef enum {
	_URC_OK		  = 0,
	_URC_END_OF_STACK = 5
} _Unwind_Reason_Code;

struct BacktraceCtx {
	void **backtrace;
	uint8_t i;
};

#ifdef HAVE__UNWIND_BACKTRACE
extern _Unwind_Reason_Code _Unwind_Backtrace(
    _Unwind_Reason_Code (*)(struct _Unwind_Context *, void *), void *);
#endif
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *);
#else
extern int _Unwind_VRS_Get(struct _Unwind_Context *, int, uint32_t, int,
    void *);
#endif

#if !defined(HAVE_STRERROR_R) && defined(OF_HAVE_THREADS)
static OFPlainMutex mutex;

OF_CONSTRUCTOR()
{
	OFEnsure(OFPlainMutexNew(&mutex) == 0);
}

OF_DESTRUCTOR()
{
	OFPlainMutexFree(&mutex);
}
#endif

OFString *
OFStrError(int errNo)
{
	OFString *ret;
#ifdef HAVE_STRERROR_R
	char buffer[256];
#endif

	if (errNo == 0)
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
	if (strerror_r(errNo, buffer, 256) != 0)
		return @"Unknown error (strerror_r failed)";

	ret = [OFString stringWithCString: buffer
				 encoding: [OFLocale encoding]];
#else
# ifdef OF_HAVE_THREADS
	if (!of_mutex_lock(&mutex))
		@throw [OFLockFailedException exception];

	@try {
# endif
		ret = [OFString
		    stringWithCString: strerror(errNo)
			     encoding: [OFLocale encoding]];
# ifdef OF_HAVE_THREADS
	} @finally {
		if (!of_mutex_unlock(&mutex))
			@throw [OFUnlockFailedException exception];
	}
# endif
#endif

	return ret;
}

#ifdef OF_WINDOWS
OFString *
of_windows_status_to_string(LSTATUS status)
{
	OFString *string = nil;
	void *buffer;

	if ([OFSystemInfo isWindowsNT]) {
		if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
		    FORMAT_MESSAGE_ALLOCATE_BUFFER |







|









|










|







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
	if (strerror_r(errNo, buffer, 256) != 0)
		return @"Unknown error (strerror_r failed)";

	ret = [OFString stringWithCString: buffer
				 encoding: [OFLocale encoding]];
#else
# ifdef OF_HAVE_THREADS
	if (OFPlainMutexLock(&mutex) != 0)
		@throw [OFLockFailedException exception];

	@try {
# endif
		ret = [OFString
		    stringWithCString: strerror(errNo)
			     encoding: [OFLocale encoding]];
# ifdef OF_HAVE_THREADS
	} @finally {
		if (OFPlainMutexUnlock(&mutex) != 0)
			@throw [OFUnlockFailedException exception];
	}
# endif
#endif

	return ret;
}

#ifdef OF_WINDOWS
OFString *
OFWindowsStatusToString(LSTATUS status)
{
	OFString *string = nil;
	void *buffer;

	if ([OFSystemInfo isWindowsNT]) {
		if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
		    FORMAT_MESSAGE_ALLOCATE_BUFFER |
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267

	return string;
}
#endif

#ifdef HAVE__UNWIND_BACKTRACE
static _Unwind_Reason_Code
backtrace_callback(struct _Unwind_Context *ctx, void *data)
{
	struct backtrace_ctx *bt = data;

	if (bt->i < OF_BACKTRACE_SIZE) {
# ifndef HAVE_ARM_EHABI_EXCEPTIONS
		bt->backtrace[bt->i++] = (void *)_Unwind_GetIP(ctx);
# else
		uintptr_t ip;

		_Unwind_VRS_Get(ctx, 0, 15, 0, &ip);
		bt->backtrace[bt->i++] = (void *)(ip & ~1);







|

|

|







250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

	return string;
}
#endif

#ifdef HAVE__UNWIND_BACKTRACE
static _Unwind_Reason_Code
backtraceCallback(struct _Unwind_Context *ctx, void *data)
{
	struct BacktraceCtx *bt = data;

	if (bt->i < OFBacktraceSize) {
# ifndef HAVE_ARM_EHABI_EXCEPTIONS
		bt->backtrace[bt->i++] = (void *)_Unwind_GetIP(ctx);
# else
		uintptr_t ip;

		_Unwind_VRS_Get(ctx, 0, 15, 0, &ip);
		bt->backtrace[bt->i++] = (void *)(ip & ~1);
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
{
	return [[[self alloc] init] autorelease];
}

#ifdef HAVE__UNWIND_BACKTRACE
- (instancetype)init
{
	struct backtrace_ctx ctx;

	self = [super init];

	ctx.backtrace = _backtrace;
	ctx.i = 0;
	_Unwind_Backtrace(backtrace_callback, &ctx);

	return self;
}
#endif

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"An exception of type %@ occurred!", self.class];
}

- (OFArray OF_GENERIC(OFString *) *)backtrace
{
#ifdef HAVE__UNWIND_BACKTRACE
	OFMutableArray OF_GENERIC(OFString *) *backtrace =
	    [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();

	for (uint8_t i = 0;
	    i < OF_BACKTRACE_SIZE && _backtrace[i] != NULL; i++) {
# ifdef HAVE_DLADDR
		Dl_info info;

		if (dladdr(_backtrace[i], &info)) {
			OFString *frame;

			if (info.dli_sname != NULL) {







|





|


















|
<







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
{
	return [[[self alloc] init] autorelease];
}

#ifdef HAVE__UNWIND_BACKTRACE
- (instancetype)init
{
	struct BacktraceCtx ctx;

	self = [super init];

	ctx.backtrace = _backtrace;
	ctx.i = 0;
	_Unwind_Backtrace(backtraceCallback, &ctx);

	return self;
}
#endif

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"An exception of type %@ occurred!", self.class];
}

- (OFArray OF_GENERIC(OFString *) *)backtrace
{
#ifdef HAVE__UNWIND_BACKTRACE
	OFMutableArray OF_GENERIC(OFString *) *backtrace =
	    [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();

	for (uint8_t i = 0; i < OFBacktraceSize && _backtrace[i] != NULL; i++) {

# ifdef HAVE_DLADDR
		Dl_info info;

		if (dladdr(_backtrace[i], &info)) {
			OFString *frame;

			if (info.dli_sname != NULL) {

Modified src/exceptions/OFGetCurrentDirectoryPathFailedException.h from [df493545f4] to [8e5eb876c7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
}

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased get current directory path failed
 *	  exception.
 *
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased get current directory failed exception
 */
+ (instancetype)exceptionWithErrNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated get current directory path failed
 *	  exception.
 *
 * @param errNo The errno of the error that occurred
 * @return An initialized get current directory path failed exception
 */
- (instancetype)initWithErrNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<









|









>
>



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
}

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased get current directory path failed
 *	  exception.
 *
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased get current directory failed exception
 */
+ (instancetype)exceptionWithErrNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated get current directory path failed
 *	  exception.
 *
 * @param errNo The errno of the error that occurred
 * @return An initialized get current directory path failed exception
 */
- (instancetype)initWithErrNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFGetCurrentDirectoryPathFailedException.m from [8bd7e18c95] to [60672e8d84].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#import "OFGetCurrentDirectoryPathFailedException.h"
#import "OFString.h"

@implementation OFGetCurrentDirectoryPathFailedException
@synthesize errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithErrNo: (int)errNo
{
	return [[[self alloc] initWithErrNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithErrNo: (int)errNo
{
	self = [super init];

	_errNo = errNo;

	return self;
}






- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Getting the current directory path failed: %@",
	    of_strerror(_errNo)];
}
@end







<
<
<
<
<





|

|










>
>
>
>
>





|


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

#import "OFGetCurrentDirectoryPathFailedException.h"
#import "OFString.h"

@implementation OFGetCurrentDirectoryPathFailedException
@synthesize errNo = _errNo;






+ (instancetype)exceptionWithErrNo: (int)errNo
{
	return [[[self alloc] initWithErrNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithErrNo: (int)errNo
{
	self = [super init];

	_errNo = errNo;

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Getting the current directory path failed: %@",
	    OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFGetOptionFailedException.h from [2d9859c7e8] to [07bdd97238].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) id object;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased get option failed exception.
 *
 * @param object The object for which the option could not be retrieved
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased get option failed exception
 */
+ (instancetype)exceptionWithObject: (id)object
			      errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated get option failed exception.
 *
 * @param object The object for which the option could not be retrieved
 * @param errNo The errno of the error that occurred
 * @return An initialized get option failed exception
 */
- (instancetype)initWithObject: (id)object
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<







|
<

|










>
>



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
@property (readonly, nonatomic) id object;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased get option failed exception.
 *
 * @param object The object for which the option could not be retrieved
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased get option failed exception
 */
+ (instancetype)exceptionWithObject: (id)object errNo: (int)errNo;


+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated get option failed exception.
 *
 * @param object The object for which the option could not be retrieved
 * @param errNo The errno of the error that occurred
 * @return An initialized get option failed exception
 */
- (instancetype)initWithObject: (id)object
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFGetOptionFailedException.m from [a2104983ac] to [aeeadc602d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize object = _object, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithObject: (id)object
			      errNo: (int)errNo
{
	return [[[self alloc] initWithObject: object
				       errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object
			 errNo: (int)errNo
{
	self = [super init];

	_object = [object retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_object release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Getting an option in an object of type %@ failed: %@",
	    [_object class], of_strerror(_errNo)];
}
@end







|
<

|
<







|
<




















|


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
@synthesize object = _object, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithObject: (id)object errNo: (int)errNo

{
	return [[[self alloc] initWithObject: object errNo: errNo] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object errNo: (int)errNo

{
	self = [super init];

	_object = [object retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_object release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Getting an option in an object of type %@ failed: %@",
	    [_object class], OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFGetWindowsRegistryValueFailedException.h from [09e5521995] to [733c657ffd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  ObjFW/OFGetWindowsRegistryValueFailedException.h
 *
 * @brief An exception indicating that getting a Windows registry value failed.
 */
@interface OFGetWindowsRegistryValueFailedException: OFException
{
	OFWindowsRegistryKey *_registryKey;
	OFString *_Nullable _value;
	DWORD _flags;
	LSTATUS _status;
}

/**
 * @brief The registry key on which getting the value at the key path failed.
 */
@property (readonly, nonatomic) OFWindowsRegistryKey *registryKey;

/**
 * @brief The value which could not be retrieved.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *value;

/**
 * @brief The status returned by RegGetValueEx().
 */
@property (readonly, nonatomic) LSTATUS status;

/**
 * @brief Creates a new, autoreleased get Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which getting the value at the sub
 *		      key path failed
 * @param value The value which could not be retrieved
 * @param status The status returned by RegGetValueEx()
 * @return A new, autoreleased get Windows registry value failed exception
 */
+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
				   value: (nullable OFString *)value
				  status: (LSTATUS)status;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated get Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which getting the value at the sub
 *		      key path failed
 * @param value The value which could not be retrieved
 * @param status The status returned by RegGetValueEx()
 * @return An initialized get Windows registry value failed exception
 */
- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			      value: (nullable OFString *)value
			     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;


@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
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
 *	  ObjFW/OFGetWindowsRegistryValueFailedException.h
 *
 * @brief An exception indicating that getting a Windows registry value failed.
 */
@interface OFGetWindowsRegistryValueFailedException: OFException
{
	OFWindowsRegistryKey *_registryKey;
	OFString *_Nullable _valueName;
	DWORD _flags;
	LSTATUS _status;
}

/**
 * @brief The registry key on which getting the value at the key path failed.
 */
@property (readonly, nonatomic) OFWindowsRegistryKey *registryKey;

/**
 * @brief The name of the value which could not be retrieved.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *valueName;

/**
 * @brief The status returned by RegGetValueEx().
 */
@property (readonly, nonatomic) LSTATUS status;

/**
 * @brief Creates a new, autoreleased get Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which getting the value at the sub
 *		      key path failed
 * @param valueName The name of the value which could not be retrieved
 * @param status The status returned by RegGetValueEx()
 * @return A new, autoreleased get Windows registry value failed exception
 */
+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			       valueName: (nullable OFString *)valueName
				  status: (LSTATUS)status;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated get Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which getting the value at the sub
 *		      key path failed
 * @param valueName The name of the value which could not be retrieved
 * @param status The status returned by RegGetValueEx()
 * @return An initialized get Windows registry value failed exception
 */
- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			  valueName: (nullable OFString *)valueName
			     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFGetWindowsRegistryValueFailedException.m from [1c37f45def] to [57087b05b3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFGetWindowsRegistryValueFailedException.h"

@implementation OFGetWindowsRegistryValueFailedException
@synthesize registryKey = _registryKey, value = _value, status = _status;


+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
				   value: (OFString *)value
				  status: (LSTATUS)status
{
	return [[[self alloc] initWithRegistryKey: registryKey
					    value: value
					   status: status] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			      value: (OFString *)value
			     status: (LSTATUS)status
{
	self = [super init];

	@try {
		_registryKey = [registryKey retain];
		_value = [value copy];
		_status = status;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_registryKey release];
	[_value release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to get value %@: %@",
	    _value, of_windows_status_to_string(_status)];
}
@end

<
<
|


















|
>


|



|



|

|



|






|








>
>
>
>
>




|







|
|


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


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

#import "OFGetWindowsRegistryValueFailedException.h"

@implementation OFGetWindowsRegistryValueFailedException
@synthesize registryKey = _registryKey, valueName = _valueName;
@synthesize status = _status;

+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			       valueName: (OFString *)valueName
				  status: (LSTATUS)status
{
	return [[[self alloc] initWithRegistryKey: registryKey
					valueName: valueName
					   status: status] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			  valueName: (OFString *)valueName
			     status: (LSTATUS)status
{
	self = [super init];

	@try {
		_registryKey = [registryKey retain];
		_valueName = [valueName copy];
		_status = status;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_registryKey release];
	[_valueName release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to get value named %@: %@",
	    _valueName, OFWindowsStatusToString(_status)];
}
@end

Modified src/exceptions/OFHTTPRequestFailedException.h from [781508f774] to [59803d0bef].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFHTTPRequest *request;

/**
 * @brief The response for the failed HTTP request.
 */
@property (readonly, nonatomic) OFHTTPResponse *response;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased HTTP request failed exception.
 *
 * @param request The HTTP request which failed
 * @param response The response for the failed HTTP request
 * @return A new, autoreleased HTTP request failed exception
 */
+ (instancetype)exceptionWithRequest: (OFHTTPRequest *)request
			    response: (OFHTTPResponse *)response;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated HTTP request failed exception.
 *
 * @param request The HTTP request which failed
 * @param response The response for the failed HTTP request
 * @return A new HTTP request failed exception
 */
- (instancetype)initWithRequest: (OFHTTPRequest *)request
		       response: (OFHTTPResponse *)response
    OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<










|











>
>



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
@property (readonly, nonatomic) OFHTTPRequest *request;

/**
 * @brief The response for the failed HTTP request.
 */
@property (readonly, nonatomic) OFHTTPResponse *response;



/**
 * @brief Creates a new, autoreleased HTTP request failed exception.
 *
 * @param request The HTTP request which failed
 * @param response The response for the failed HTTP request
 * @return A new, autoreleased HTTP request failed exception
 */
+ (instancetype)exceptionWithRequest: (OFHTTPRequest *)request
			    response: (OFHTTPResponse *)response;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated HTTP request failed exception.
 *
 * @param request The HTTP request which failed
 * @param response The response for the failed HTTP request
 * @return A new HTTP request failed exception
 */
- (instancetype)initWithRequest: (OFHTTPRequest *)request
		       response: (OFHTTPResponse *)response
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFHTTPRequestFailedException.m from [ef8de3042e] to [02e986bf36].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFString.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"

@implementation OFHTTPRequestFailedException
@synthesize request = _request, response = _response;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithRequest: (OFHTTPRequest *)request
			    response: (OFHTTPResponse *)response
{
	return [[[self alloc] initWithRequest: request
				     response: response] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRequest: (OFHTTPRequest *)request
		       response: (OFHTTPResponse *)response
{
	self = [super init];

	_request = [request retain];
	_response = [response retain];

	return self;
}






- (void)dealloc
{
	[_request release];
	[_response release];

	[super dealloc];
}

- (OFString *)description
{
	const char *method = of_http_request_method_to_string(_request.method);

	return [OFString stringWithFormat:
	    @"An HTTP %s request with URL %@ failed with code %hd!", method,
	    _request.URL, _response.statusCode];
}
@end







<
<
<
<
<







|

|












>
>
>
>
>











|






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
#import "OFString.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"

@implementation OFHTTPRequestFailedException
@synthesize request = _request, response = _response;






+ (instancetype)exceptionWithRequest: (OFHTTPRequest *)request
			    response: (OFHTTPResponse *)response
{
	return [[[self alloc] initWithRequest: request
				     response: response] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithRequest: (OFHTTPRequest *)request
		       response: (OFHTTPResponse *)response
{
	self = [super init];

	_request = [request retain];
	_response = [response retain];

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_request release];
	[_response release];

	[super dealloc];
}

- (OFString *)description
{
	const char *method = OFHTTPRequestMethodName(_request.method);

	return [OFString stringWithFormat:
	    @"An HTTP %s request with URL %@ failed with code %hd!", method,
	    _request.URL, _response.statusCode];
}
@end

Modified src/exceptions/OFHashAlreadyCalculatedException.h from [44dc09adde] to [9fce34d334].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
}

/**
 * @brief The hash which has already been calculated.
 */
@property (readonly, nonatomic) id object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased hash already calculated exception.
 *
 * @param object The hash which has already been calculated
 * @return A new, autoreleased hash already calculated exception
 */
+ (instancetype)exceptionWithObject: (id)object;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated hash already calculated exception.
 *
 * @param object The hash which has already been calculated
 * @return An initialized hash already calculated exception
 */
- (instancetype)initWithObject: (id)object OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<








|








>
>



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
}

/**
 * @brief The hash which has already been calculated.
 */
@property (readonly, nonatomic) id object;



/**
 * @brief Creates a new, autoreleased hash already calculated exception.
 *
 * @param object The hash which has already been calculated
 * @return A new, autoreleased hash already calculated exception
 */
+ (instancetype)exceptionWithObject: (id)object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated hash already calculated exception.
 *
 * @param object The hash which has already been calculated
 * @return An initialized hash already calculated exception
 */
- (instancetype)initWithObject: (id)object OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFHashAlreadyCalculatedException.m from [1c0b901a32] to [cf2d8e44b5].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Added src/exceptions/OFHashNotCalculatedException.h version [768ce1b6b5].



















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFException.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFHashNotCalculatedException OFHashNotCalculatedException.h \
 *	  ObjFW/OFHashNotCalculatedException.h
 *
 * @brief An exception indicating that the hash has not been calculated yet.
 */
@interface OFHashNotCalculatedException: OFException
{
	id _object;
}

/**
 * @brief The hash which has not been calculated yet.
 */
@property (readonly, nonatomic) id object;

/**
 * @brief Creates a new, autoreleased hash not calculated exception.
 *
 * @param object The hash which has not been calculated yet
 * @return A new, autoreleased hash not calculated exception
 */
+ (instancetype)exceptionWithObject: (id)object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated hash not calculated exception.
 *
 * @param object The hash which has not been calculated yet
 * @return An initialized hash not calculated exception
 */
- (instancetype)initWithObject: (id)object OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Added src/exceptions/OFHashNotCalculatedException.m version [3e84cb3642].



























































































































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

#import "OFHashNotCalculatedException.h"
#import "OFString.h"

@implementation OFHashNotCalculatedException
@synthesize object = _object;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithObject: (id)object
{
	return [[[self alloc] initWithObject: object] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object
{
	self = [super init];

	_object = [object retain];

	return self;
}

- (void)dealloc
{
	[_object release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"The hash of type %@ has not been calculated yet!",
	    [_object class]];
}
@end

Modified src/exceptions/OFInitializationFailedException.h from [272cc20780] to [20206324fd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInitializationFailedException.m from [9117449d9c] to [cfa23bb7fe].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInvalidArgumentException.h from [4e3b286133] to [d5e2417590].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInvalidArgumentException.m from [a6206d7da8] to [70957802ed].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInvalidEncodingException.h from [e0b39579e5] to [393547809a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInvalidEncodingException.m from [66ac68069c] to [7e85cac169].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInvalidFormatException.h from [b2bd9775a2] to [e6f46ae68c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInvalidFormatException.m from [c6e814685c] to [f3d13d03ab].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInvalidJSONException.h from [08723a84ca] to [9ac94f6e36].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *string;

/**
 * @brief The line in which parsing the JSON representation failed.
 */
@property (readonly, nonatomic) size_t line;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased invalid JSON exception.
 *
 * @param string The string containing the invalid JSON representation
 * @param line The line in which the parsing error was encountered
 * @return A new, autoreleased invalid JSON exception
 */
+ (instancetype)exceptionWithString: (nullable OFString *)string
			       line: (size_t)line;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated invalid JSON exception.
 *
 * @param string The string containing the invalid JSON representation
 * @param line The line in which the parsing error was encountered
 * @return An initialized invalid JSON exception
 */
- (instancetype)initWithString: (nullable OFString *)string
			  line: (size_t)line OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<










|










>
>



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
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *string;

/**
 * @brief The line in which parsing the JSON representation failed.
 */
@property (readonly, nonatomic) size_t line;



/**
 * @brief Creates a new, autoreleased invalid JSON exception.
 *
 * @param string The string containing the invalid JSON representation
 * @param line The line in which the parsing error was encountered
 * @return A new, autoreleased invalid JSON exception
 */
+ (instancetype)exceptionWithString: (nullable OFString *)string
			       line: (size_t)line;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated invalid JSON exception.
 *
 * @param string The string containing the invalid JSON representation
 * @param line The line in which the parsing error was encountered
 * @return An initialized invalid JSON exception
 */
- (instancetype)initWithString: (nullable OFString *)string
			  line: (size_t)line OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFInvalidJSONException.m from [e80457c265] to [973f5127bd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize string = _string, line = _line;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithString: (OFString *)string
			       line: (size_t)line
{
	return [[[self alloc] initWithString: string
					line: line] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithString: (OFString *)string
			  line: (size_t)line
{
	self = [super init];

	@try {
		_string = [string copy];
		_line = line;
	} @catch (id e) {







|
<

|
<







|
<







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
@synthesize string = _string, line = _line;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithString: (OFString *)string line: (size_t)line

{
	return [[[self alloc] initWithString: string line: line] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithString: (OFString *)string line: (size_t)line

{
	self = [super init];

	@try {
		_string = [string copy];
		_line = line;
	} @catch (id e) {

Modified src/exceptions/OFInvalidServerReplyException.h from [4468aafe74] to [90e12ae8cf].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFInvalidServerReplyException.m from [0345e9b1e9] to [aae10c2dab].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFLinkFailedException.h from [be0b2a5425] to [099d3c2419].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFURL *destinationURL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased link failed exception.
 *
 * @param sourceURL The source for the link
 * @param destinationURL The destination for the link
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased link failed exception
 */
+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
			destinationURL: (OFURL *)destinationURL
				 errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated link failed exception.
 *
 * @param sourceURL The source for the link
 * @param destinationURL The destination for the link
 * @param errNo The errno of the error that occurred
 * @return An initialized link failed exception
 */
- (instancetype)initWithSourceURL: (OFURL*)sourceURL
		   destinationURL: (OFURL *)destinationURL
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@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
@property (readonly, nonatomic) OFURL *destinationURL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased link failed exception.
 *
 * @param sourceURL The source for the link
 * @param destinationURL The destination for the link
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased link failed exception
 */
+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
			destinationURL: (OFURL *)destinationURL
				 errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated link failed exception.
 *
 * @param sourceURL The source for the link
 * @param destinationURL The destination for the link
 * @param errNo The errno of the error that occurred
 * @return An initialized link failed exception
 */
- (instancetype)initWithSourceURL: (OFURL*)sourceURL
		   destinationURL: (OFURL *)destinationURL
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFLinkFailedException.m from [b001d54d98] to [7710ab3fd2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
69
70
71
72
73
74
75
76
77
78

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"Failed to link file %@ to %@: %@",
	    _sourceURL, _destinationURL, of_strerror(_errNo)];
}
@end







|


67
68
69
70
71
72
73
74
75
76

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"Failed to link file %@ to %@: %@",
	    _sourceURL, _destinationURL, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFListenFailedException.h from [a314b9e246] to [32d4375802].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) int backlog;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased listen failed exception.
 *
 * @param socket The socket which failed to listen
 * @param backlog The requested size of the back log
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased listen failed exception
 */
+ (instancetype)exceptionWithSocket: (id)socket
			    backlog: (int)backlog
			      errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated listen failed exception
 *
 * @param socket The socket which failed to listen
 * @param backlog The requested size of the back log
 * @param errNo The errno of the error that occurred
 * @return An initialized listen failed exception
 */
- (instancetype)initWithSocket: (id)socket
		       backlog: (int)backlog
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<












|


|









>
>



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
@property (readonly, nonatomic) int backlog;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased listen failed exception.
 *
 * @param socket The socket which failed to listen
 * @param backlog The requested size of the back log
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased listen failed exception
 */
+ (instancetype)exceptionWithSocket: (id)socket
			    backlog: (int)backlog
			      errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated listen failed exception.
 *
 * @param socket The socket which failed to listen
 * @param backlog The requested size of the back log
 * @param errNo The errno of the error that occurred
 * @return An initialized listen failed exception
 */
- (instancetype)initWithSocket: (id)socket
		       backlog: (int)backlog
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFListenFailedException.m from [b0ecc966ad] to [45e185da77].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
62
63
64
65
66
67
68
69
70
71
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to listen in socket of type %@ with a back log of %d: %@",
	    [_socket class], _backlog, of_strerror(_errNo)];
}
@end







|


60
61
62
63
64
65
66
67
68
69
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to listen in socket of type %@ with a back log of %d: %@",
	    [_socket class], _backlog, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFLoadPluginFailedException.h from [e486958965] to [7c45470186].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFString *path;

/**
 * @brief The error why the plugin could not be loaded, as a string
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *error;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased load plugin failed exception.
 *
 * @param path The path of the plugin which could not be loaded
 * @param error The error why the plugin could not be loaded, as a string
 * @return A new, autoreleased load plugin failed exception
 */
+ (instancetype)exceptionWithPath: (OFString *)path
			    error: (nullable OFString *)error;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated load plugin failed exception.
 *
 * @param path The path of the plugin which could not be loaded
 * @param error The error why the plugin could not be loaded, as a string
 * @return An initialized load plugin failed exception
 */
- (instancetype)initWithPath: (OFString *)path
		       error: (nullable OFString *)error
    OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<










|











>
>



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
@property (readonly, nonatomic) OFString *path;

/**
 * @brief The error why the plugin could not be loaded, as a string
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *error;



/**
 * @brief Creates a new, autoreleased load plugin failed exception.
 *
 * @param path The path of the plugin which could not be loaded
 * @param error The error why the plugin could not be loaded, as a string
 * @return A new, autoreleased load plugin failed exception
 */
+ (instancetype)exceptionWithPath: (OFString *)path
			    error: (nullable OFString *)error;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated load plugin failed exception.
 *
 * @param path The path of the plugin which could not be loaded
 * @param error The error why the plugin could not be loaded, as a string
 * @return An initialized load plugin failed exception
 */
- (instancetype)initWithPath: (OFString *)path
		       error: (nullable OFString *)error
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFLoadPluginFailedException.m from [0471728613] to [73caeab9c3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFLoadPluginFailedException.h"
#import "OFString.h"

@implementation OFLoadPluginFailedException
@synthesize path = _path, error = _error;






+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithPath: (OFString *)path
			    error: (OFString *)error
{
	return [[[self alloc] initWithPath: path
				     error: error] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPath: (OFString *)path
		       error: (OFString *)error
{
	self = [super init];

	@try {
		_path = [path copy];
		_error = [error copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_path release];
	[_error release];

	[super dealloc];

<
<
|




















>
>
>
>
>






<
<
<
<
<
<
<
<
<
<
<
<
|
<













>
>
>
>
>







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


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

#import "OFLoadPluginFailedException.h"
#import "OFString.h"

@implementation OFLoadPluginFailedException
@synthesize path = _path, error = _error;

+ (instancetype)exceptionWithPath: (OFString *)path error: (OFString *)error
{
	return [[[self alloc] initWithPath: path error: error] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}













- (instancetype)initWithPath: (OFString *)path error: (OFString *)error

{
	self = [super init];

	@try {
		_path = [path copy];
		_error = [error copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_path release];
	[_error release];

	[super dealloc];

Modified src/exceptions/OFLockFailedException.h from [2896ed28a8] to [e861f27ee4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * @class OFLockFailedException \
 *	  OFLockFailedException.h ObjFW/OFLockFailedException.h
 *
 * @brief An exception indicating that locking a lock failed.
 */
@interface OFLockFailedException: OFException
{
	id <OFLocking> _lock;
	int _errNo;
}

/**
 * @brief The lock which could not be locked.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id <OFLocking> lock;







|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 * @class OFLockFailedException \
 *	  OFLockFailedException.h ObjFW/OFLockFailedException.h
 *
 * @brief An exception indicating that locking a lock failed.
 */
@interface OFLockFailedException: OFException
{
	id <OFLocking> _Nullable _lock;
	int _errNo;
}

/**
 * @brief The lock which could not be locked.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id <OFLocking> lock;
57
58
59
60
61
62
63
64
65
66
67
68
 *
 * @param lock The lock which could not be locked
 * @param errNo The errno of the error that occurred
 * @return An initialized lock failed exception
 */
- (instancetype)initWithLock: (nullable id <OFLocking>)lock
		       errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END







<
<



55
56
57
58
59
60
61


62
63
64
 *
 * @param lock The lock which could not be locked
 * @param errNo The errno of the error that occurred
 * @return An initialized lock failed exception
 */
- (instancetype)initWithLock: (nullable id <OFLocking>)lock
		       errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFLockFailedException.m from [194a416a76] to [77b32492ca].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#import "OFLockFailedException.h"
#import "OFString.h"

@implementation OFLockFailedException
@synthesize lock = _lock, errNo = _errNo;

+ (instancetype)exceptionWithLock: (id <OFLocking>)lock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithLock: lock
				     errNo: errNo] autorelease];
}

- (instancetype)initWithLock: (id <OFLocking>)lock
		       errNo: (int)errNo
{
	self = [super init];

	_lock = [lock retain];
	_errNo = errNo;

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_lock release];

	[super dealloc];
}

- (OFString *)description
{

	return [OFString stringWithFormat:
	    @"A lock of type %@ could not be locked: %s",
	    [_lock class], strerror(_errNo)];


}
@end







|
<

|
<


|
<









<
<
<
<
<









>
|
|
|
>
>


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

#import "OFLockFailedException.h"
#import "OFString.h"

@implementation OFLockFailedException
@synthesize lock = _lock, errNo = _errNo;

+ (instancetype)exceptionWithLock: (id <OFLocking>)lock errNo: (int)errNo

{
	return [[[self alloc] initWithLock: lock errNo: errNo] autorelease];

}

- (instancetype)initWithLock: (id <OFLocking>)lock errNo: (int)errNo

{
	self = [super init];

	_lock = [lock retain];
	_errNo = errNo;

	return self;
}






- (void)dealloc
{
	[_lock release];

	[super dealloc];
}

- (OFString *)description
{
	if (_lock != nil)
		return [OFString stringWithFormat:
		    @"A lock of type %@ could not be locked: %s",
		    [_lock class], strerror(_errNo)];
	else
		return @"A lock could not be locked!";
}
@end

Modified src/exceptions/OFMalformedXMLException.h from [918b47a4d1] to [3b999908e6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFMalformedXMLException.m from [3254a4bf70] to [f5ceb7c800].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Deleted src/exceptions/OFMemoryNotPartOfObjectException.h version [ec71f02702].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFException.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFMemoryNotPartOfObjectException \
 *	  OFMemoryNotPartOfObjectException.h \
 *	  ObjFW/OFMemoryNotPartOfObjectException.h
 *
 * @brief An exception indicating the given memory is not part of the object.
 */
@interface OFMemoryNotPartOfObjectException: OFException
{
	void *_Nullable _pointer;
	id _object;
}

/**
 * @brief A pointer to the memory which is not part of the object.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) void *pointer;

/**
 * @brief The object which the memory is not part of.
 */
@property (readonly, nonatomic) id object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased memory not part of object exception.
 *
 * @param pointer A pointer to the memory that is not part of the object
 * @param object The object which the memory is not part of
 * @return A new, autoreleased memory not part of object exception
 */
+ (instancetype)exceptionWithPointer: (nullable void *)pointer
			      object: (id)object;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated memory not part of object exception.
 *
 * @param pointer A pointer to the memory that is not part of the object
 * @param object The object which the memory is not part of
 * @return An initialized memory not part of object exception
 */
- (instancetype)initWithPointer: (nullable void *)pointer
			 object: (id)object OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































































































Deleted src/exceptions/OFMemoryNotPartOfObjectException.m version [58ab407fc3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFMemoryNotPartOfObjectException.h"
#import "OFString.h"

@implementation OFMemoryNotPartOfObjectException
@synthesize pointer = _pointer, object = _object;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithPointer: (void *)pointer
			      object: (id)object
{
	return [[[self alloc] initWithPointer: pointer
				       object: object] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPointer: (void *)pointer
			 object: (id)object
{
	self = [super init];

	_pointer = pointer;
	_object = [object retain];

	return self;
}

- (void)dealloc
{
	[_object release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Deallocation or reallocation of memory not allocated as part of "
	    @"object of type %@ was attempted! It is also possible that there "
	    @"was an attempt to free the same memory twice.",
	    [_object class]];
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































































































Modified src/exceptions/OFMoveItemFailedException.h from [3c8aaf82f5] to [c4c3e18fa9].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFURL *destinationURL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased move item failed exception.
 *
 * @param sourceURL The original URL
 * @param destinationURL The new URL
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased move item failed exception
 */
+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
			destinationURL: (OFURL *)destinationURL
				 errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated move item failed exception.
 *
 * @param sourceURL The original URL
 * @param destinationURL The new URL
 * @param errNo The errno of the error that occurred
 * @return An initialized move item failed exception
 */
- (instancetype)initWithSourceURL: (OFURL *)sourceURL
		   destinationURL: (OFURL *)destinationURL
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@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
@property (readonly, nonatomic) OFURL *destinationURL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased move item failed exception.
 *
 * @param sourceURL The original URL
 * @param destinationURL The new URL
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased move item failed exception
 */
+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
			destinationURL: (OFURL *)destinationURL
				 errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated move item failed exception.
 *
 * @param sourceURL The original URL
 * @param destinationURL The new URL
 * @param errNo The errno of the error that occurred
 * @return An initialized move item failed exception
 */
- (instancetype)initWithSourceURL: (OFURL *)sourceURL
		   destinationURL: (OFURL *)destinationURL
			    errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFMoveItemFailedException.m from [d8cf092187] to [4ea07cc8dd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
			destinationURL: (OFURL *)destinationURL
				  errNo: (int)errNo
{
	return [[[self alloc] initWithSourceURL: sourceURL
				 destinationURL: destinationURL
					  errNo: errNo] autorelease];
}

- (instancetype)init







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL
			destinationURL: (OFURL *)destinationURL
				 errNo: (int)errNo
{
	return [[[self alloc] initWithSourceURL: sourceURL
				 destinationURL: destinationURL
					  errNo: errNo] autorelease];
}

- (instancetype)init
70
71
72
73
74
75
76
77
78
79
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to move item at %@ to %@: %@",
	    _sourceURL, _destinationURL, of_strerror(_errNo)];
}
@end







|


68
69
70
71
72
73
74
75
76
77
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to move item at %@ to %@: %@",
	    _sourceURL, _destinationURL, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFNotImplementedException.h from [c94de3583e] to [891dc14bbc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 * @brief The selector which is not or not fully implemented.
 */
@property (readonly, nonatomic) SEL selector;

/**
 * @brief The object which does not (fully) implement the selector.
 */
@property (readonly, nonatomic) id object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased not implemented exception.
 *
 * @param selector The selector which is not or not fully implemented
 * @param object The object which does not (fully) implement the selector
 * @return A new, autoreleased not implemented exception
 */
+ (instancetype)exceptionWithSelector: (SEL)selector
			       object: (nullable id)object;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated not implemented exception.
 *
 * @param selector The selector which is not or not fully implemented
 * @param object The object which does not (fully) implement the selector
 * @return An initialized not implemented exception
 */
- (instancetype)initWithSelector: (SEL)selector
			  object: (nullable id)object OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







|
<
<











|










>
>



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
 * @brief The selector which is not or not fully implemented.
 */
@property (readonly, nonatomic) SEL selector;

/**
 * @brief The object which does not (fully) implement the selector.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id object;



/**
 * @brief Creates a new, autoreleased not implemented exception.
 *
 * @param selector The selector which is not or not fully implemented
 * @param object The object which does not (fully) implement the selector
 * @return A new, autoreleased not implemented exception
 */
+ (instancetype)exceptionWithSelector: (SEL)selector
			       object: (nullable id)object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated not implemented exception.
 *
 * @param selector The selector which is not or not fully implemented
 * @param object The object which does not (fully) implement the selector
 * @return An initialized not implemented exception
 */
- (instancetype)initWithSelector: (SEL)selector
			  object: (nullable id)object OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFNotImplementedException.m from [febe4bbb90] to [c04e46d7c8].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize selector = _selector, object = _object;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithSelector: (SEL)selector
			       object: (id)object
{
	return [[[self alloc] initWithSelector: selector
					object: object] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSelector: (SEL)selector
			  object: (id)object
{
	self = [super init];

	_selector = selector;
	_object = [object retain];

	return self;







|
<










|
<







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
@synthesize selector = _selector, object = _object;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithSelector: (SEL)selector object: (id)object

{
	return [[[self alloc] initWithSelector: selector
					object: object] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSelector: (SEL)selector object: (id)object

{
	self = [super init];

	_selector = selector;
	_object = [object retain];

	return self;

Modified src/exceptions/OFNotOpenException.h from [ae04d94402] to [45f02f6f12].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
}

/**
 * @brief The object which is not open, connected or bound.
 */
@property (readonly, nonatomic) id object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased not open exception.
 *
 * @param object The object which is not open, connected or bound
 * @return A new, autoreleased not open exception
 */
+ (instancetype)exceptionWithObject: (id)object;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated not open exception.
 *
 * @param object The object which is not open, connected or bound
 * @return An initialized not open exception
 */
- (instancetype)initWithObject: (id)object OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<








|








>
>



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 object which is not open, connected or bound.
 */
@property (readonly, nonatomic) id object;



/**
 * @brief Creates a new, autoreleased not open exception.
 *
 * @param object The object which is not open, connected or bound
 * @return A new, autoreleased not open exception
 */
+ (instancetype)exceptionWithObject: (id)object;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated not open exception.
 *
 * @param object The object which is not open, connected or bound
 * @return An initialized not open exception
 */
- (instancetype)initWithObject: (id)object OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFNotOpenException.m from [45d81233dc] to [b0a200e662].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFObserveFailedException.h from [705baea80a] to [6c7bf7885c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFKernelEventObserver *observer;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased observe failed exception.
 *
 * @param observer The observer which failed to observe
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased observe failed exception
 */
+ (instancetype)exceptionWithObserver: (OFKernelEventObserver *)observer
				errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated observe failed exception.
 *
 * @param observer The observer which failed to observe
 * @param errNo The errno of the error that occurred
 * @return An initialized observe failed exception
 */
- (instancetype)initWithObserver: (OFKernelEventObserver *)observer
			   errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<










|










>
>



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
@property (readonly, nonatomic) OFKernelEventObserver *observer;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased observe failed exception.
 *
 * @param observer The observer which failed to observe
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased observe failed exception
 */
+ (instancetype)exceptionWithObserver: (OFKernelEventObserver *)observer
				errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated observe failed exception.
 *
 * @param observer The observer which failed to observe
 * @param errNo The errno of the error that occurred
 * @return An initialized observe failed exception
 */
- (instancetype)initWithObserver: (OFKernelEventObserver *)observer
			   errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFObserveFailedException.m from [0373f3dc3e] to [1a09e82eb7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
64
65
66
67
68
69
70
71
72
73
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"An observer of class %@ failed to observe: %@",
	    _observer.class, of_strerror(_errNo)];
}
@end







|


62
63
64
65
66
67
68
69
70
71
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"An observer of class %@ failed to observe: %@",
	    _observer.class, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFOpenItemFailedException.h from [34fbd4fcc7] to [bf32fbec46].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *mode;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased open item failed exception.
 *
 * @param URL The URL of the item which could not be opened
 * @param mode A string with the mode in which the item should have been opened
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased open item failed exception







<
<







49
50
51
52
53
54
55


56
57
58
59
60
61
62
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *mode;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased open item failed exception.
 *
 * @param URL The URL of the item which could not be opened
 * @param mode A string with the mode in which the item should have been opened
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased open item failed exception
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased open item failed exception
 */
+ (instancetype)exceptionWithPath: (OFString *)path
			     mode: (nullable OFString *)mode
			    errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated open item failed exception.
 *
 * @param URL The URL of the item which could not be opened
 * @param mode A string with the mode in which the item should have been opened
 * @param errNo The errno of the error that occurred







|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased open item failed exception
 */
+ (instancetype)exceptionWithPath: (OFString *)path
			     mode: (nullable OFString *)mode
			    errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated open item failed exception.
 *
 * @param URL The URL of the item which could not be opened
 * @param mode A string with the mode in which the item should have been opened
 * @param errNo The errno of the error that occurred
102
103
104
105
106
107
108


109
110
111
 * @param mode A string with the mode in which the item should have been opened
 * @param errNo The errno of the error that occurred
 * @return An initialized open item failed exception
 */
- (instancetype)initWithPath: (OFString *)path
			mode: (nullable OFString *)mode
		       errNo: (int)errNo;


@end

OF_ASSUME_NONNULL_END







>
>



98
99
100
101
102
103
104
105
106
107
108
109
 * @param mode A string with the mode in which the item should have been opened
 * @param errNo The errno of the error that occurred
 * @return An initialized open item failed exception
 */
- (instancetype)initWithPath: (OFString *)path
			mode: (nullable OFString *)mode
		       errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFOpenItemFailedException.m from [765498651a] to [2b4ec7a8b3].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFOpenItemFailedException.h"
#import "OFString.h"
#import "OFURL.h"

@implementation OFOpenItemFailedException
@synthesize URL = _URL, path = _path, mode = _mode, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL
			    mode: (OFString *)mode
			   errNo: (int)errNo
{
	return [[[self alloc] initWithURL: URL
				     mode: mode
				    errNo: errNo] autorelease];
}

+ (instancetype)exceptionWithPath: (OFString *)path
			     mode: (OFString *)mode
			    errNo: (int)errNo
{
	return [[[self alloc] initWithPath: path
				      mode: mode
				     errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURL: (OFURL *)URL
		       mode: (OFString *)mode
		      errNo: (int)errNo
{
	self = [super init];







<
<
<
<
<


















|

|







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
#import "OFOpenItemFailedException.h"
#import "OFString.h"
#import "OFURL.h"

@implementation OFOpenItemFailedException
@synthesize URL = _URL, path = _path, mode = _mode, errNo = _errNo;






+ (instancetype)exceptionWithURL: (OFURL *)URL
			    mode: (OFString *)mode
			   errNo: (int)errNo
{
	return [[[self alloc] initWithURL: URL
				     mode: mode
				    errNo: errNo] autorelease];
}

+ (instancetype)exceptionWithPath: (OFString *)path
			     mode: (OFString *)mode
			    errNo: (int)errNo
{
	return [[[self alloc] initWithPath: path
				      mode: mode
				     errNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithURL: (OFURL *)URL
		       mode: (OFString *)mode
		      errNo: (int)errNo
{
	self = [super init];
83
84
85
86
87
88
89





90
91
92
93
94
95
96
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_URL release];
	[_path release];
	[_mode release];








>
>
>
>
>







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_URL release];
	[_path release];
	[_mode release];

105
106
107
108
109
110
111
112
113
114
115
116
117
		item = _URL;
	else if (_path != nil)
		item = _path;

	if (_mode != nil)
		return [OFString stringWithFormat:
		    @"Failed to open item %@ with mode %@: %@",
		    item, _mode, of_strerror(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"Failed to open item %@: %@", item, of_strerror(_errNo)];
}
@end







|


|


103
104
105
106
107
108
109
110
111
112
113
114
115
		item = _URL;
	else if (_path != nil)
		item = _path;

	if (_mode != nil)
		return [OFString stringWithFormat:
		    @"Failed to open item %@ with mode %@: %@",
		    item, _mode, OFStrError(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"Failed to open item %@: %@", item, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFOpenWindowsRegistryKeyFailedException.h from [154d53c4c1] to [5fb8a73f7a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
+ (instancetype)
    exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			path: (OFString *)path
		     options: (DWORD)options
     securityAndAccessRights: (REGSAM)securityAndAccessRights
		      status: (LSTATUS)status;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated open Windows registry key failed
 *	  exception.
 *
 * @param registryKey The registry key on which opening the subkey failed
 * @param path The path for the subkey that could not be opened
 * @param options The options for the subkey that could not be opened
 * @param securityAndAccessRights The security and access rights for the sub
 *				  key that could not be opened
 * @param status The status returned by RegOpenKeyEx()
 * @return An initialized open Windows registry key failed exception
 */
- (instancetype)
	initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
		       path: (OFString *)path
		    options: (DWORD)options
    securityAndAccessRights: (REGSAM)securityAndAccessRights
		     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







|



















>
>



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
+ (instancetype)
    exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			path: (OFString *)path
		     options: (DWORD)options
     securityAndAccessRights: (REGSAM)securityAndAccessRights
		      status: (LSTATUS)status;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated open Windows registry key failed
 *	  exception.
 *
 * @param registryKey The registry key on which opening the subkey failed
 * @param path The path for the subkey that could not be opened
 * @param options The options for the subkey that could not be opened
 * @param securityAndAccessRights The security and access rights for the sub
 *				  key that could not be opened
 * @param status The status returned by RegOpenKeyEx()
 * @return An initialized open Windows registry key failed exception
 */
- (instancetype)
	initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
		       path: (OFString *)path
		    options: (DWORD)options
    securityAndAccessRights: (REGSAM)securityAndAccessRights
		     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFOpenWindowsRegistryKeyFailedException.m from [5a3399041c] to [52118b22c0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
	return [[[self alloc] initWithRegistryKey: registryKey
					     path: path
					  options: options
			  securityAndAccessRights: securityAndAccessRights
					   status: status] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)
	initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
		       path: (OFString *)path
		    options: (DWORD)options
    securityAndAccessRights: (REGSAM)securityAndAccessRights







|

|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
	return [[[self alloc] initWithRegistryKey: registryKey
					     path: path
					  options: options
			  securityAndAccessRights: securityAndAccessRights
					   status: status] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)
	initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
		       path: (OFString *)path
		    options: (DWORD)options
    securityAndAccessRights: (REGSAM)securityAndAccessRights
61
62
63
64
65
66
67





68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_registryKey release];
	[_path release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to open subkey at path %@: %@",
	    _path, of_windows_status_to_string(_status)];
}
@end







>
>
>
>
>













|


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
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_registryKey release];
	[_path release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to open subkey at path %@: %@",
	    _path, OFWindowsStatusToString(_status)];
}
@end

Modified src/exceptions/OFOutOfMemoryException.h from [f5d3bcfd5b] to [a0abc1f6de].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFOutOfMemoryException.m from [5abff47452] to [f0195e5a54].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFOutOfRangeException.h from [f0db0a5bed] to [ad1a11af88].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFOutOfRangeException.m from [54a6768506] to [42dae12fbe].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFReadFailedException.h from [10c62e7402] to [a4ca6ea7be].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFReadFailedException.m from [0000f46d4a] to [0a38835e23].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25

26
27




28
29
30

#import "OFReadFailedException.h"
#import "OFString.h"

@implementation OFReadFailedException
- (OFString *)description
{

	return [OFString stringWithFormat:
	    @"Failed to read %zu bytes from an object of type %@: %@",




	    _requestedLength, [_object class], of_strerror(_errNo)];
}
@end







>
|
|
>
>
>
>
|


17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

#import "OFReadFailedException.h"
#import "OFString.h"

@implementation OFReadFailedException
- (OFString *)description
{
	if (_errNo != 0)
		return [OFString stringWithFormat:
		    @"Failed to read %zu bytes from an object of type %@: %@",
		    _requestedLength, [_object class], OFStrError(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"Failed to read %zu bytes from an object of type %@",
		    _requestedLength, [_object class]];
}
@end

Modified src/exceptions/OFReadOrWriteFailedException.h from [9ae34bfd6a] to [17403f4ccf].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFReadOrWriteFailedException.m from [8ec2463652] to [02ef4afd0b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
61
62
63
64
65
66
67

68
69
70





71
72
73
	[_object release];

	[super dealloc];
}

- (OFString *)description
{

	return [OFString stringWithFormat:
	    @"Failed to read or write %zu bytes from / to an object of type "
	    @"%@: %@",





	    _requestedLength, [_object class], of_strerror(_errNo)];
}
@end







>
|
|
|
>
>
>
>
>
|


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
	[_object release];

	[super dealloc];
}

- (OFString *)description
{
	if (_errNo != 0)
		return [OFString stringWithFormat:
		    @"Failed to read or write %zu bytes from / to an object of "
		    @"type %@: %@",
		    _requestedLength, [_object class], OFStrError(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"Failed to read or write %zu bytes from / to an object of "
		    @"type %@",
		    _requestedLength, [_object class]];
}
@end

Modified src/exceptions/OFRemoveItemFailedException.h from [2f7e82f67e] to [c9bfaec2ab].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased remove failed exception.
 *
 * @param URL The URL of the item which could not be removed
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased remove item failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL
			   errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated remove failed exception.
 *
 * @param URL The URL of the item which could not be removed
 * @param errNo The errno of the error that occurred
 * @return An initialized remove item failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<







|
<

|










>
>



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
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased remove failed exception.
 *
 * @param URL The URL of the item which could not be removed
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased remove item failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo;


+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated remove failed exception.
 *
 * @param URL The URL of the item which could not be removed
 * @param errNo The errno of the error that occurred
 * @return An initialized remove item failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFRemoveItemFailedException.m from [5f5d30d1f4] to [be28f37327].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize URL = _URL, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL
			   errNo: (int)errNo
{
	return [[[self alloc] initWithURL: URL
				    errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

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

	@try {
		_URL = [URL copy];
		_errNo = errNo;
	} @catch (id e) {







|
<

|
<







|
<







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
@synthesize URL = _URL, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo

{
	return [[[self alloc] initWithURL: URL errNo: errNo] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURL: (OFURL *)URL errNo: (int)errNo

{
	self = [super init];

	@try {
		_URL = [URL copy];
		_errNo = errNo;
	} @catch (id e) {
63
64
65
66
67
68
69
70
71
72

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to remove item at URL %@: %@", _URL, of_strerror(_errNo)];
}
@end







|


58
59
60
61
62
63
64
65
66
67

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to remove item at URL %@: %@", _URL, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFResolveHostFailedException.h from [214de79488] to [26d03aec04].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  OFResolveHostFailedException.h ObjFW/OFResolveHostFailedException.h
 *
 * @brief An exception indicating that resolving a host failed.
 */
@interface OFResolveHostFailedException: OFException
{
	OFString *_host;
	of_socket_address_family_t _addressFamily;
	of_dns_resolver_error_t _error;
}

/**
 * @brief The host which could not be resolved.
 */
@property (readonly, nonatomic) OFString *host;

/**
 * @brief The address family for which the host could not be resolved.
 */
@property (readonly, nonatomic) of_socket_address_family_t addressFamily;

/**
 * @brief The error from the resolver.
 */
@property (readonly, nonatomic) of_dns_resolver_error_t error;

/**
 * @brief Creates a new, autoreleased resolve host failed exception.
 *
 * @param host The host which could not be resolved
 * @param addressFamily The address family for which the host could not be
 *			resolved
 * @param error The error from the resolver
 * @return A new, autoreleased address translation failed exception
 */
+ (instancetype)exceptionWithHost: (OFString *)host
		    addressFamily: (of_socket_address_family_t)addressFamily

			    error: (of_dns_resolver_error_t)error;


/**
 * @brief Initializes an already allocated resolve host failed exception.
 *
 * @param host The host which could not be resolved
 * @param addressFamily The address family for which the host could not be
 *			resolved
 * @param error The error from the resolver
 * @return An initialized address translation failed exception
 */
- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (of_socket_address_family_t)addressFamily

		       error: (of_dns_resolver_error_t)error;

@end

OF_ASSUME_NONNULL_END







|
|










|


|

|







|



|
>
|
>







|



|
>
|
>



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
 *	  OFResolveHostFailedException.h ObjFW/OFResolveHostFailedException.h
 *
 * @brief An exception indicating that resolving a host failed.
 */
@interface OFResolveHostFailedException: OFException
{
	OFString *_host;
	OFSocketAddressFamily _addressFamily;
	OFDNSResolverErrorCode _errorCode;
}

/**
 * @brief The host which could not be resolved.
 */
@property (readonly, nonatomic) OFString *host;

/**
 * @brief The address family for which the host could not be resolved.
 */
@property (readonly, nonatomic) OFSocketAddressFamily addressFamily;

/**
 * @brief The error code from the resolver.
 */
@property (readonly, nonatomic) OFDNSResolverErrorCode errorCode;

/**
 * @brief Creates a new, autoreleased resolve host failed exception.
 *
 * @param host The host which could not be resolved
 * @param addressFamily The address family for which the host could not be
 *			resolved
 * @param errorCode The error code from the resolver
 * @return A new, autoreleased address translation failed exception
 */
+ (instancetype)exceptionWithHost: (OFString *)host
		    addressFamily: (OFSocketAddressFamily)addressFamily
			errorCode: (OFDNSResolverErrorCode)errorCode;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated resolve host failed exception.
 *
 * @param host The host which could not be resolved
 * @param addressFamily The address family for which the host could not be
 *			resolved
 * @param errorCode The error code from the resolver
 * @return An initialized address translation failed exception
 */
- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (OFSocketAddressFamily)addressFamily
		   errorCode: (OFDNSResolverErrorCode)errorCode;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFResolveHostFailedException.m from [9ab8b6e961] to [a81a110c2f].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFResolveHostFailedException.h"
#import "OFDNSQueryFailedException.h"
#import "OFString.h"

@implementation OFResolveHostFailedException
@synthesize host = _host, addressFamily = _addressFamily, error = _error;


+ (instancetype)exceptionWithHost: (OFString *)host
		    addressFamily: (of_socket_address_family_t)addressFamily
			    error: (of_dns_resolver_error_t)error
{
	return [[[self alloc] initWithHost: host
			     addressFamily: addressFamily
				     error: error] autorelease];





}

- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (of_socket_address_family_t)addressFamily
		       error: (of_dns_resolver_error_t)error
{
	self = [super init];

	@try {
		_host = [host copy];
		_addressFamily = addressFamily;
		_error = error;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_host release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"The host %@ could not be resolved: %@",
	    _host, of_dns_resolver_error_to_string(_error)];
}
@end

<
<
|




















|
>


|
|



|
>
>
>
>
>



|
|






|







>
>
>
>
>












|


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


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

#import "OFResolveHostFailedException.h"
#import "OFDNSQueryFailedException.h"
#import "OFString.h"

@implementation OFResolveHostFailedException
@synthesize host = _host, addressFamily = _addressFamily;
@synthesize errorCode = _errorCode;

+ (instancetype)exceptionWithHost: (OFString *)host
		    addressFamily: (OFSocketAddressFamily)addressFamily
			errorCode: (OFDNSResolverErrorCode)errorCode
{
	return [[[self alloc] initWithHost: host
			     addressFamily: addressFamily
				 errorCode: errorCode] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithHost: (OFString *)host
	       addressFamily: (OFSocketAddressFamily)addressFamily
		   errorCode: (OFDNSResolverErrorCode)errorCode
{
	self = [super init];

	@try {
		_host = [host copy];
		_addressFamily = addressFamily;
		_errorCode = errorCode;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_host release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"The host %@ could not be resolved: %@",
	    _host, OFDNSResolverErrorCodeDescription(_errorCode)];
}
@end

Modified src/exceptions/OFRetrieveItemAttributesFailedException.h from [07675a3c0a] to [2269d1ffef].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased retrieve item attributes failed exception.
 *
 * @param URL The URL of the item whose attributes could not be retrieved
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased retrieve item attributes failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL
			   errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated retrieve item attributes failed
 *	  exception.
 *
 * @param URL The URL of the item whose attributes could not be retrieved
 * @param errNo The errno of the error that occurred
 * @return An initialized retrieve item attributes failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<







|
<

|











>
>



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
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased retrieve item attributes failed exception.
 *
 * @param URL The URL of the item whose attributes could not be retrieved
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased retrieve item attributes failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo;


+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated retrieve item attributes failed
 *	  exception.
 *
 * @param URL The URL of the item whose attributes could not be retrieved
 * @param errNo The errno of the error that occurred
 * @return An initialized retrieve item attributes failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFRetrieveItemAttributesFailedException.m from [cd9a66d72c] to [8b9c2b49f4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize URL = _URL, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL
			   errNo: (int)errNo
{
	return [[[self alloc] initWithURL: URL
				    errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

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

	@try {
		_URL = [URL copy];
		_errNo = errNo;
	} @catch (id e) {







|
<

|
<







|
<







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
@synthesize URL = _URL, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo

{
	return [[[self alloc] initWithURL: URL errNo: errNo] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURL: (OFURL *)URL errNo: (int)errNo

{
	self = [super init];

	@try {
		_URL = [URL copy];
		_errNo = errNo;
	} @catch (id e) {
64
65
66
67
68
69
70
71
72
73
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to retrieve attributes for item %@: %@",
	    _URL, of_strerror(_errNo)];
}
@end







|


59
60
61
62
63
64
65
66
67
68
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to retrieve attributes for item %@: %@",
	    _URL, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFSandboxActivationFailedException.h from [4b78a21d95] to [3961f69a33].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFException.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSandbox;

/**
 * @class OFSandboxActivationFailedException \
 *	  OFSandboxActivationFailedException.h \
 *	  ObjFW/OFSandboxActivationFailedException.h
 *
 * @brief An exception indicating that sandboxing the process failed.
 */
@interface OFSandboxActivationFailedException: OFException
{
	OFSandbox *_sandbox;
	int _errNo;
}

/**
 * @brief The sandbox which could not be activated.
 */
@property (readonly, nonatomic) OFSandbox *sandbox;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased sandboxing failed exception.
 *
 * @param sandbox The sandbox which could not be activated
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased sandboxing failed exception
 */
+ (instancetype)exceptionWithSandbox: (OFSandbox *)sandbox
			       errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated sandboxing failed exception.
 *
 * @param sandbox The sandbox which could not be activated
 * @param errNo The errno of the error that occurred
 * @return An initialized sandboxing failed exception
 */
- (instancetype)initWithSandbox: (OFSandbox *)sandbox
			  errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

<
<
|



















<
<
<
<
<
<
<






<
<
<

<
<
<
<



<
<
<
<
<
<
<
<
|
<
<

<
<
<
<
<
<
<
<





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


 * 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 "OFException.h"

OF_ASSUME_NONNULL_BEGIN

@class OFSandbox;








@interface OFSandboxActivationFailedException: OFException
{
	OFSandbox *_sandbox;
	int _errNo;
}




@property (readonly, nonatomic) OFSandbox *sandbox;




@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;








+ (instancetype)exceptionWithSandbox: (OFSandbox *)sandbox errNo: (int)errNo;


- (instancetype)init OF_UNAVAILABLE;








- (instancetype)initWithSandbox: (OFSandbox *)sandbox
			  errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFSandboxActivationFailedException.m from [5e9eec7605] to [6ace25dc28].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize sandbox = _sandbox, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithSandbox: (OFSandbox *)sandbox
			       errNo: (int)errNo
{
	return [[[self alloc] initWithSandbox: sandbox
					errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSandbox: (OFSandbox *)sandbox
			  errNo: (int)errNo
{
	self = [super init];

	_sandbox = [sandbox retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_sandbox release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"The sandbox could not be applied: %@", of_strerror(_errNo)];
}
@end







|
<










|
<



















|


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
@synthesize sandbox = _sandbox, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithSandbox: (OFSandbox *)sandbox errNo: (int)errNo

{
	return [[[self alloc] initWithSandbox: sandbox
					errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithSandbox: (OFSandbox *)sandbox errNo: (int)errNo

{
	self = [super init];

	_sandbox = [sandbox retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_sandbox release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"The sandbox could not be applied: %@", OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFSeekFailedException.h from [9b32035ace] to [f6a3b56229].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  OFSeekFailedException.h ObjFW/OFSeekFailedException.h
 *
 * @brief An exception indicating that seeking in a stream failed.
 */
@interface OFSeekFailedException: OFException
{
	OFSeekableStream *_stream;
	of_offset_t _offset;
	int _whence, _errNo;
}

/**
 * @brief The stream for which seeking failed.
 */
@property (readonly, nonatomic) OFSeekableStream *stream;

/**
 * @brief The offset to which seeking failed.
 */
@property (readonly, nonatomic) of_offset_t offset;

/**
 * @brief To what the offset is relative.
 */
@property (readonly, nonatomic) int whence;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased seek failed exception.
 *
 * @param stream The stream for which seeking failed
 * @param offset The offset to which seeking failed
 * @param whence To what the offset is relative
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased seek failed exception
 */
+ (instancetype)exceptionWithStream: (OFSeekableStream *)stream
			     offset: (of_offset_t)offset
			     whence: (int)whence
			      errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated seek failed exception.
 *
 * @param stream The stream for which seeking failed
 * @param offset The offset to which seeking failed
 * @param whence To what the offset is relative
 * @param errNo The errno of the error that occurred
 * @return An initialized seek failed exception
 */
- (instancetype)initWithStream: (OFSeekableStream *)stream
			offset: (of_offset_t)offset
			whence: (int)whence
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







|











|











<
<










|



|











|


>
>



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
 *	  OFSeekFailedException.h ObjFW/OFSeekFailedException.h
 *
 * @brief An exception indicating that seeking in a stream failed.
 */
@interface OFSeekFailedException: OFException
{
	OFSeekableStream *_stream;
	OFFileOffset _offset;
	int _whence, _errNo;
}

/**
 * @brief The stream for which seeking failed.
 */
@property (readonly, nonatomic) OFSeekableStream *stream;

/**
 * @brief The offset to which seeking failed.
 */
@property (readonly, nonatomic) OFFileOffset offset;

/**
 * @brief To what the offset is relative.
 */
@property (readonly, nonatomic) int whence;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased seek failed exception.
 *
 * @param stream The stream for which seeking failed
 * @param offset The offset to which seeking failed
 * @param whence To what the offset is relative
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased seek failed exception
 */
+ (instancetype)exceptionWithStream: (OFSeekableStream *)stream
			     offset: (OFFileOffset)offset
			     whence: (int)whence
			      errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated seek failed exception.
 *
 * @param stream The stream for which seeking failed
 * @param offset The offset to which seeking failed
 * @param whence To what the offset is relative
 * @param errNo The errno of the error that occurred
 * @return An initialized seek failed exception
 */
- (instancetype)initWithStream: (OFSeekableStream *)stream
			offset: (OFFileOffset)offset
			whence: (int)whence
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFSeekFailedException.m from [223dd15b2f] to [8afbb45d54].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithStream: (OFSeekableStream *)stream
			     offset: (of_offset_t)offset
			     whence: (int)whence
			      errNo: (int)errNo
{
	return [[[self alloc] initWithStream: stream
				      offset: offset
				      whence: whence
				       errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OFSeekableStream *)stream
			offset: (of_offset_t)offset
			whence: (int)whence
			 errNo: (int)errNo
{
	self = [super init];

	_stream = [stream retain];
	_offset = offset;







|















|







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

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithStream: (OFSeekableStream *)stream
			     offset: (OFFileOffset)offset
			     whence: (int)whence
			      errNo: (int)errNo
{
	return [[[self alloc] initWithStream: stream
				      offset: offset
				      whence: whence
				       errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OFSeekableStream *)stream
			offset: (OFFileOffset)offset
			whence: (int)whence
			 errNo: (int)errNo
{
	self = [super init];

	_stream = [stream retain];
	_offset = offset;
68
69
70
71
72
73
74
75
76
77
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Seeking failed in stream of type %@: %@",
	    _stream.class, of_strerror(_errNo)];
}
@end







|


66
67
68
69
70
71
72
73
74
75
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Seeking failed in stream of type %@: %@",
	    _stream.class, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFSetItemAttributesFailedException.h from [f9744801d9] to [40e626240d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  ObjFW/OFSetItemAttributesFailedException.h
 *
 * @brief An exception indicating an item's attributes could not be set.
 */
@interface OFSetItemAttributesFailedException: OFException
{
	OFURL *_URL;
	of_file_attributes_t _attributes;
	of_file_attribute_key_t _failedAttribute;
	int _errNo;
}

/**
 * @brief The URL of the item whose attributes could not be set.
 */
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

/**
 * @brief The attributes that should have been set.
 */
@property (readonly, nonatomic) of_file_attributes_t attributes;

/**
 * @brief The first attribute that could not be set.
 */
@property (readonly, nonatomic) of_file_attribute_key_t failedAttribute;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased set item attributes failed exception.
 *
 * @param URL The URL of the item whose attributes could not be set
 * @param attributes The attributes that should have been set for the specified
 *		     item.
 * @param failedAttribute The first attribute that could not be set
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased set item attributes failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL
		      attributes: (of_file_attributes_t)attributes
		 failedAttribute: (of_file_attribute_key_t)failedAttribute
			   errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated set item attributes failed exception.
 *
 * @param URL The URL of the item whose attributes could not be set
 * @param attributes The attributes that should have been set for the specified
 *		     item.
 * @param failedAttribute The first attribute that could not be set
 * @param errNo The errno of the error that occurred
 * @return An initialized set item attributes failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		 attributes: (of_file_attributes_t)attributes
	    failedAttribute: (of_file_attribute_key_t)failedAttribute
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@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


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
 *	  ObjFW/OFSetItemAttributesFailedException.h
 *
 * @brief An exception indicating an item's attributes could not be set.
 */
@interface OFSetItemAttributesFailedException: OFException
{
	OFURL *_URL;
	OFFileAttributes _attributes;
	OFFileAttributeKey _failedAttribute;
	int _errNo;
}

/**
 * @brief The URL of the item whose attributes could not be set.
 */
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

/**
 * @brief The attributes that should have been set.
 */
@property (readonly, nonatomic) OFFileAttributes attributes;

/**
 * @brief The first attribute that could not be set.
 */
@property (readonly, nonatomic) OFFileAttributeKey failedAttribute;



/**
 * @brief Creates a new, autoreleased set item attributes failed exception.
 *
 * @param URL The URL of the item whose attributes could not be set
 * @param attributes The attributes that should have been set for the specified
 *		     item.
 * @param failedAttribute The first attribute that could not be set
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased set item attributes failed exception
 */
+ (instancetype)exceptionWithURL: (OFURL *)URL
		      attributes: (OFFileAttributes)attributes
		 failedAttribute: (OFFileAttributeKey)failedAttribute
			   errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated set item attributes failed exception.
 *
 * @param URL The URL of the item whose attributes could not be set
 * @param attributes The attributes that should have been set for the specified
 *		     item.
 * @param failedAttribute The first attribute that could not be set
 * @param errNo The errno of the error that occurred
 * @return An initialized set item attributes failed exception
 */
- (instancetype)initWithURL: (OFURL *)URL
		 attributes: (OFFileAttributes)attributes
	    failedAttribute: (OFFileAttributeKey)failedAttribute
		      errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFSetItemAttributesFailedException.m from [780f6f2739] to [c7f09ee0c0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL
		      attributes: (of_file_attributes_t)attributes
		 failedAttribute: (of_file_attribute_key_t)failedAttribute
			   errNo: (int)errNo
{
	return [[[self alloc] initWithURL: URL
			       attributes: attributes
			  failedAttribute: failedAttribute
				    errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURL: (OFURL *)URL
		 attributes: (of_file_attributes_t)attributes
	    failedAttribute: (of_file_attribute_key_t)failedAttribute
		      errNo: (int)errNo
{
	self = [super init];

	@try {
		_URL = [URL copy];
		_attributes = [attributes copy];







|
|














|
|







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

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL
		      attributes: (OFFileAttributes)attributes
		 failedAttribute: (OFFileAttributeKey)failedAttribute
			   errNo: (int)errNo
{
	return [[[self alloc] initWithURL: URL
			       attributes: attributes
			  failedAttribute: failedAttribute
				    errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURL: (OFURL *)URL
		 attributes: (OFFileAttributes)attributes
	    failedAttribute: (OFFileAttributeKey)failedAttribute
		      errNo: (int)errNo
{
	self = [super init];

	@try {
		_URL = [URL copy];
		_attributes = [attributes copy];
75
76
77
78
79
80
81
82
83
84
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to set attribute %@ for item %@: %@",
	    _failedAttribute, _URL, of_strerror(_errNo)];
}
@end







|


73
74
75
76
77
78
79
80
81
82
	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to set attribute %@ for item %@: %@",
	    _failedAttribute, _URL, OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFSetOptionFailedException.h from [3ad19fbb9d] to [fd8e9dd895].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) id object;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased set option failed exception.
 *
 * @param object The object for which the option could not be set
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased set option failed exception
 */
+ (instancetype)exceptionWithObject: (id)object
			      errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated set option failed exception.
 *
 * @param object The object for which the option could not be set
 * @param errNo The errno of the error that occurred
 * @return An initialized set option failed exception
 */
- (instancetype)initWithObject: (id)object
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<







|
<

|










>
>



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
@property (readonly, nonatomic) id object;

/**
 * @brief The errno of the error that occurred.
 */
@property (readonly, nonatomic) int errNo;



/**
 * @brief Creates a new, autoreleased set option failed exception.
 *
 * @param object The object for which the option could not be set
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased set option failed exception
 */
+ (instancetype)exceptionWithObject: (id)object errNo: (int)errNo;


+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated set option failed exception.
 *
 * @param object The object for which the option could not be set
 * @param errNo The errno of the error that occurred
 * @return An initialized set option failed exception
 */
- (instancetype)initWithObject: (id)object
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFSetOptionFailedException.m from [1fbb6fdc4f] to [8f19f7dfbc].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize object = _object, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithObject: (id)object
			      errNo: (int)errNo
{
	return [[[self alloc] initWithObject: object
				       errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object
			 errNo: (int)errNo
{
	self = [super init];

	_object = [object retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_object release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Setting an option in an object of type %@ failed: %@",
	    [_object class], of_strerror(_errNo)];
}
@end







|
<

|
<







|
<




















|


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
@synthesize object = _object, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithObject: (id)object errNo: (int)errNo

{
	return [[[self alloc] initWithObject: object errNo: errNo] autorelease];

}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object errNo: (int)errNo

{
	self = [super init];

	_object = [object retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_object release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Setting an option in an object of type %@ failed: %@",
	    [_object class], OFStrError(_errNo)];
}
@end

Modified src/exceptions/OFSetWindowsRegistryValueFailedException.h from [d07cbbec63] to [881eab26fa].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  ObjFW/OFSetWindowsRegistryValueFailedException.h
 *
 * @brief An exception indicating that setting a Windows registry value failed.
 */
@interface OFSetWindowsRegistryValueFailedException: OFException
{
	OFWindowsRegistryKey *_registryKey;
	OFString *_Nullable _value;
	OFData *_Nullable _data;
	DWORD _type;
	LSTATUS _status;
}

/**
 * @brief The registry key on which setting the value failed.
 */
@property (readonly, nonatomic) OFWindowsRegistryKey *registryKey;

/**
 * @brief The value which could not be set.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *value;

/**
 * @brief The data to which the value could not be set.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFData *data;

/**







|











|

|







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
 *	  ObjFW/OFSetWindowsRegistryValueFailedException.h
 *
 * @brief An exception indicating that setting a Windows registry value failed.
 */
@interface OFSetWindowsRegistryValueFailedException: OFException
{
	OFWindowsRegistryKey *_registryKey;
	OFString *_Nullable _valueName;
	OFData *_Nullable _data;
	DWORD _type;
	LSTATUS _status;
}

/**
 * @brief The registry key on which setting the value failed.
 */
@property (readonly, nonatomic) OFWindowsRegistryKey *registryKey;

/**
 * @brief The name of the value which could not be set.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *valueName;

/**
 * @brief The data to which the value could not be set.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFData *data;

/**
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
@property (readonly, nonatomic) LSTATUS status;

/**
 * @brief Creates a new, autoreleased set Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which setting the value failed
 * @param value The value which could not be set
 * @param data The data to which the value could not be set
 * @param type The type for the value that could not be set
 * @param status The status returned by RegSetValueEx()
 * @return A new, autoreleased set Windows registry value failed exception
 */
+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
				   value: (nullable OFString *)value
				    data: (nullable OFData *)data
				    type: (DWORD)type
				  status: (LSTATUS)status;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated set Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which setting the value failed
 * @param value The value which could not be set
 * @param data The data to which the value could not be set
 * @param type The type for the value that could not be set
 * @param status The status returned by RegSetValueEx()
 * @return An initialized set Windows registry value failed exception
 */
- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			      value: (nullable OFString *)value
			       data: (nullable OFData *)data
			       type: (DWORD)type
			     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END







|






|











|






|






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
@property (readonly, nonatomic) LSTATUS status;

/**
 * @brief Creates a new, autoreleased set Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which setting the value failed
 * @param valueName The name of the value which could not be set
 * @param data The data to which the value could not be set
 * @param type The type for the value that could not be set
 * @param status The status returned by RegSetValueEx()
 * @return A new, autoreleased set Windows registry value failed exception
 */
+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			       valueName: (nullable OFString *)valueName
				    data: (nullable OFData *)data
				    type: (DWORD)type
				  status: (LSTATUS)status;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated set Windows registry value failed
 *	  exception.
 *
 * @param registryKey The registry key on which setting the value failed
 * @param valueName The name of the value which could not be set
 * @param data The data to which the value could not be set
 * @param type The type for the value that could not be set
 * @param status The status returned by RegSetValueEx()
 * @return An initialized set Windows registry value failed exception
 */
- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			  valueName: (nullable OFString *)valueName
			       data: (nullable OFData *)data
			       type: (DWORD)type
			     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFSetWindowsRegistryValueFailedException.m from [07e1fe5617] to [e6d28eef78].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFSetWindowsRegistryValueFailedException.h"

#import "OFData.h"

@implementation OFSetWindowsRegistryValueFailedException
@synthesize registryKey = _registryKey, value = _value, data = _data;
@synthesize type = _type, status = _status;

+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
				   value: (OFString *)value
				    data: (OFData *)data
				    type: (DWORD)type
				  status: (LSTATUS)status
{
	return [[[self alloc] initWithRegistryKey: registryKey
					    value: value
					     data: data
					     type: type
					   status: status] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			      value: (OFString *)value
			       data: (OFData *)data
			       type: (DWORD)type
			     status: (LSTATUS)status
{
	self = [super init];

	@try {
		_registryKey = [registryKey retain];
		_value = [value copy];
		_data = [data copy];
		_type = type;
		_status = status;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_registryKey release];
	[_value release];
	[_data release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to set value %@ of type %u: %@",
	    _value, _type, of_windows_status_to_string(_status)];
}
@end

<
<
|




















|



|





|











|








|














|








|
|


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


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

#import "OFSetWindowsRegistryValueFailedException.h"

#import "OFData.h"

@implementation OFSetWindowsRegistryValueFailedException
@synthesize registryKey = _registryKey, valueName = _valueName, data = _data;
@synthesize type = _type, status = _status;

+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			       valueName: (OFString *)valueName
				    data: (OFData *)data
				    type: (DWORD)type
				  status: (LSTATUS)status
{
	return [[[self alloc] initWithRegistryKey: registryKey
					valueName: valueName
					     data: data
					     type: type
					   status: status] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			  valueName: (OFString *)valueName
			       data: (OFData *)data
			       type: (DWORD)type
			     status: (LSTATUS)status
{
	self = [super init];

	@try {
		_registryKey = [registryKey retain];
		_valueName = [valueName copy];
		_data = [data copy];
		_type = type;
		_status = status;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_registryKey release];
	[_valueName release];
	[_data release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to set value named %@ of type %u: %@",
	    _valueName, _type, OFWindowsStatusToString(_status)];
}
@end

Modified src/exceptions/OFStillLockedException.h from [129c6beace] to [9e844f7b24].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * @class OFStillLockedException \
 *	  OFStillLockedException.h ObjFW/OFStillLockedException.h
 *
 * @brief An exception indicating that a lock is still locked.
 */
@interface OFStillLockedException: OFException
{
	id <OFLocking> _lock;
}

/**
 * @brief The lock which is still locked.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id <OFLocking> lock;








|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 * @class OFStillLockedException \
 *	  OFStillLockedException.h ObjFW/OFStillLockedException.h
 *
 * @brief An exception indicating that a lock is still locked.
 */
@interface OFStillLockedException: OFException
{
	id <OFLocking> _Nullable _lock;
}

/**
 * @brief The lock which is still locked.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id <OFLocking> lock;

Modified src/exceptions/OFStillLockedException.m from [befdc7b2dc] to [3bdcf688e2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Added src/exceptions/OFTLSHandshakeFailedException.h version [624dc0f97d].

























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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 "OFException.h"

#ifndef OF_HAVE_SOCKETS
# error No sockets available!
#endif

#import "OFTLSStream.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __cplusplus
extern "C" {
#endif
extern int _OFTLSHandshakeFailedException_reference;
#ifdef __cplusplus
}
#endif

/**
 * @class OFTLSHandshakeFailedException \
 *	  OFTLSHandshakeFailedException.h ObjFW/OFTLSHandshakeFailedException.h
 *
 * @brief An exception indicating that a TLS handshake.
 */
@interface OFTLSHandshakeFailedException: OFException
{
	OFTLSStream *_stream;
	OFString *_Nullable _host;
	OFTLSStreamErrorCode _errorCode;
}

/**
 * @brief The TLS stream which failed the handshake.
 */
@property (readonly, nonatomic) OFTLSStream *stream;

/**
 * @brief The host for the handshake.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;

/**
 * @brief The error code from the TLS stream.
 */
@property (readonly, nonatomic) OFTLSStreamErrorCode errorCode;

/**
 * @brief Creates a new, autoreleased TLS handshake failed exception.
 *
 * @param stream The TLS stream which failed the handshake
 * @param host The host for the handshake
 * @param errorCode The error code from the TLS stream
 * @return A new, autoreleased TLS handshake failed exception
 */
+ (instancetype)exceptionWithStream: (OFTLSStream *)stream
			       host: (nullable OFString *)host
			  errorCode: (OFTLSStreamErrorCode)errorCode;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated TLS handshake failed exception.
 *
 * @param stream The TLS stream which failed the handshake
 * @param host The host for the handshake
 * @param errorCode The error code from the TLS stream
 * @return An initialized TLS handshake failed exception
 */
- (instancetype)initWithStream: (OFTLSStream *)stream
			  host: (nullable OFString *)host
		     errorCode: (OFTLSStreamErrorCode)errorCode
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Added src/exceptions/OFTLSHandshakeFailedException.m version [9f825e73f8].







































































































































































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

#import "OFTLSHandshakeFailedException.h"
#import "OFString.h"

int _OFTLSHandshakeFailedException_reference;

@implementation OFTLSHandshakeFailedException
@synthesize stream = _stream, host = _host, errorCode = _errorCode;

+ (instancetype)exceptionWithStream: (OFTLSStream *)stream
			       host: (OFString *)host
			  errorCode: (OFTLSStreamErrorCode)errorCode
{
	return [[[self alloc] initWithStream: stream
					host: host
				   errorCode: errorCode] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithStream: (OFTLSStream *)stream
			  host: (OFString *)host
		     errorCode: (OFTLSStreamErrorCode)errorCode
{
	self = [super init];

	@try {
		_stream = [stream retain];
		_host = [host copy];
		_errorCode = errorCode;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_stream release];
	[_host release];

	[super dealloc];
}

- (OFString *)description
{
	if (_host != nil)
		return [OFString stringWithFormat:
		    @"TLS handshake in class %@ with host %@ failed: %@",
		    _stream.class, _host,
		    OFTLSStreamErrorCodeDescription(_errorCode)];
	else
		return [OFString stringWithFormat:
		    @"TLS handshake in class %@ failed: %@",
		    _stream.class, OFTLSStreamErrorCodeDescription(_errorCode)];
}
@end

Modified src/exceptions/OFThreadJoinFailedException.h from [b527bd48d4] to [8bf49777b7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 * @class OFThreadJoinFailedException \
 *	  OFThreadJoinFailedException.h ObjFW/OFThreadJoinFailedException.h
 *
 * @brief An exception indicating that joining a thread failed.
 */
@interface OFThreadJoinFailedException: OFException
{
	OFThread *_thread;
	int _errNo;
}

/**
 * @brief The thread which could not be joined.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread;







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 * @class OFThreadJoinFailedException \
 *	  OFThreadJoinFailedException.h ObjFW/OFThreadJoinFailedException.h
 *
 * @brief An exception indicating that joining a thread failed.
 */
@interface OFThreadJoinFailedException: OFException
{
	OFThread *_Nullable _thread;
	int _errNo;
}

/**
 * @brief The thread which could not be joined.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread;
52
53
54
55
56
57
58


59
60
61
62
63
64
65
 *
 * @param thread The thread which could not be joined
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased thread join failed exception
 */
+ (instancetype)exceptionWithThread: (nullable OFThread *)thread
			      errNo: (int)errNo;



/**
 * @brief Initializes an already allocated thread join failed exception.
 *
 * @param thread The thread which could not be joined
 * @param errNo The errno of the error that occurred
 * @return An initialized thread join failed exception







>
>







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 *
 * @param thread The thread which could not be joined
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased thread join failed exception
 */
+ (instancetype)exceptionWithThread: (nullable OFThread *)thread
			      errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated thread join failed exception.
 *
 * @param thread The thread which could not be joined
 * @param errNo The errno of the error that occurred
 * @return An initialized thread join failed exception

Modified src/exceptions/OFThreadJoinFailedException.m from [a63740fb47] to [3ceaee16ab].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
22
23
24
25
26
27
28
29
30
31
32

33



34
35
36
37
38
39
40
41
42
43
44
#import "OFThreadJoinFailedException.h"
#import "OFString.h"
#import "OFThread.h"

@implementation OFThreadJoinFailedException
@synthesize thread = _thread, errNo = _errNo;

+ (instancetype)exceptionWithThread: (OFThread *)thread
			      errNo: (int)errNo
{
	return [[[self alloc] initWithThread: thread

				       errNo: errNo] autorelease];



}

- (instancetype)initWithThread: (OFThread *)thread
			 errNo: (int)errNo
{
	self = [super init];

	_thread = [thread retain];
	_errNo = errNo;

	return self;







|
<

|
>
|
>
>
>


|
<







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
#import "OFThreadJoinFailedException.h"
#import "OFString.h"
#import "OFThread.h"

@implementation OFThreadJoinFailedException
@synthesize thread = _thread, errNo = _errNo;

+ (instancetype)exceptionWithThread: (OFThread *)thread errNo: (int)errNo

{
	return [[[self alloc] initWithThread: thread errNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithThread: (OFThread *)thread errNo: (int)errNo

{
	self = [super init];

	_thread = [thread retain];
	_errNo = errNo;

	return self;

Modified src/exceptions/OFThreadStartFailedException.h from [3e69c81f5f] to [d864107627].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 * @class OFThreadStartFailedException \
 *	  OFThreadStartFailedException.h ObjFW/OFThreadStartFailedException.h
 *
 * @brief An exception indicating that starting a thread failed.
 */
@interface OFThreadStartFailedException: OFException
{
	OFThread *_thread;
	int _errNo;
}

/**
 * @brief The thread which could not be started.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread;







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 * @class OFThreadStartFailedException \
 *	  OFThreadStartFailedException.h ObjFW/OFThreadStartFailedException.h
 *
 * @brief An exception indicating that starting a thread failed.
 */
@interface OFThreadStartFailedException: OFException
{
	OFThread *_Nullable _thread;
	int _errNo;
}

/**
 * @brief The thread which could not be started.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread;
52
53
54
55
56
57
58


59
60
61
62
63
64
65
 *
 * @param thread The thread which could not be started
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased thread start failed exception
 */
+ (instancetype)exceptionWithThread: (nullable OFThread *)thread
			      errNo: (int)errNo;



/**
 * @brief Initializes an already allocated thread start failed exception.
 *
 * @param thread The thread which could not be started
 * @param errNo The errno of the error that occurred
 * @return An initialized thread start failed exception







>
>







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 *
 * @param thread The thread which could not be started
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased thread start failed exception
 */
+ (instancetype)exceptionWithThread: (nullable OFThread *)thread
			      errNo: (int)errNo;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated thread start failed exception.
 *
 * @param thread The thread which could not be started
 * @param errNo The errno of the error that occurred
 * @return An initialized thread start failed exception

Modified src/exceptions/OFThreadStartFailedException.m from [1c0a578d89] to [4df0dddce1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
22
23
24
25
26
27
28
29
30
31
32

33



34
35
36
37
38
39
40
41
42
43
44
#import "OFThreadStartFailedException.h"
#import "OFString.h"
#import "OFThread.h"

@implementation OFThreadStartFailedException
@synthesize thread = _thread, errNo = _errNo;

+ (instancetype)exceptionWithThread: (OFThread *)thread
			      errNo: (int)errNo
{
	return [[[self alloc] initWithThread: thread

				       errNo: errNo] autorelease];



}

- (instancetype)initWithThread: (OFThread *)thread
			 errNo: (int)errNo
{
	self = [super init];

	_thread = [thread retain];
	_errNo = errNo;

	return self;







|
<

|
>
|
>
>
>


|
<







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
#import "OFThreadStartFailedException.h"
#import "OFString.h"
#import "OFThread.h"

@implementation OFThreadStartFailedException
@synthesize thread = _thread, errNo = _errNo;

+ (instancetype)exceptionWithThread: (OFThread *)thread errNo: (int)errNo

{
	return [[[self alloc] initWithThread: thread errNo: errNo] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithThread: (OFThread *)thread errNo: (int)errNo

{
	self = [super init];

	_thread = [thread retain];
	_errNo = errNo;

	return self;

Modified src/exceptions/OFThreadStillRunningException.h from [bb48ef30be] to [2982187d56].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 * @class OFThreadStillRunningException \
 *	  OFThreadStillRunningException.h ObjFW/OFThreadStillRunningException.h
 *
 * @brief An exception indicating that a thread is still running.
 */
@interface OFThreadStillRunningException: OFException
{
	OFThread *_thread;
}

/**
 * @brief The thread which is still running.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread;








|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 * @class OFThreadStillRunningException \
 *	  OFThreadStillRunningException.h ObjFW/OFThreadStillRunningException.h
 *
 * @brief An exception indicating that a thread is still running.
 */
@interface OFThreadStillRunningException: OFException
{
	OFThread *_Nullable _thread;
}

/**
 * @brief The thread which is still running.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFThread *thread;

Modified src/exceptions/OFThreadStillRunningException.m from [408dabc9f4] to [08b6a2fa91].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFTruncatedDataException.h from [d9329704f6] to [8a558e22e7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFTruncatedDataException.m from [3c7848233b] to [d3465afc55].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFUnboundNamespaceException.h from [8c7398ae7e] to [cc4424a80d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

/**
 * @brief The unbound namespace.
 */
#ifndef __cplusplus
@property (readonly, nonatomic) OFString *namespace;
#else
@property (readonly, nonatomic, getter=namespace) OFString *namespace_;
#endif

/**
 * @brief The element in which the namespace was not bound.
 */
@property (readonly, nonatomic) OFXMLElement *element;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased unbound namespace exception.
 *
 * @param namespace_ The namespace which is unbound
 * @param element The element in which the namespace was not bound
 * @return A new, autoreleased unbound namespace exception
 */
+ (instancetype)exceptionWithNamespace: (OFString *)namespace_
			       element: (OFXMLElement *)element;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated unbound namespace exception.
 *
 * @param namespace_ The namespace which is unbound
 * @param element The element in which the namespace was not bound
 * @return An initialized unbound namespace exception
 */
- (instancetype)initWithNamespace: (OFString *)namespace_
			  element: (OFXMLElement *)element
    OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







|







<
<



|



|


|




|



|


>
>



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

/**
 * @brief The unbound namespace.
 */
#ifndef __cplusplus
@property (readonly, nonatomic) OFString *namespace;
#else
@property (readonly, nonatomic, getter=namespace) OFString *nameSpace;
#endif

/**
 * @brief The element in which the namespace was not bound.
 */
@property (readonly, nonatomic) OFXMLElement *element;



/**
 * @brief Creates a new, autoreleased unbound namespace exception.
 *
 * @param nameSpace The namespace which is unbound
 * @param element The element in which the namespace was not bound
 * @return A new, autoreleased unbound namespace exception
 */
+ (instancetype)exceptionWithNamespace: (OFString *)nameSpace
			       element: (OFXMLElement *)element;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated unbound namespace exception.
 *
 * @param nameSpace The namespace which is unbound
 * @param element The element in which the namespace was not bound
 * @return An initialized unbound namespace exception
 */
- (instancetype)initWithNamespace: (OFString *)nameSpace
			  element: (OFXMLElement *)element
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFUnboundNamespaceException.m from [0746a32c6a] to [8d26c2cfa6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFUnboundPrefixException.h from [e2787d1963] to [6d7bca5c41].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@property (readonly, nonatomic) OFString *prefix;

/**
 * @brief The parser which encountered the unbound prefix.
 */
@property (readonly, nonatomic) OFXMLParser *parser;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased unbound prefix exception.
 *
 * @param prefix The prefix which is unbound
 * @param parser The parser which encountered the unbound prefix
 * @return A new, autoreleased unbound prefix exception
 */
+ (instancetype)exceptionWithPrefix: (OFString *)prefix
			     parser: (OFXMLParser *)parser;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated unbound prefix exception.
 *
 * @param prefix The prefix which is unbound
 * @param parser The parser which encountered the unbound prefix
 * @return An initialized unbound prefix exception
 */
- (instancetype)initWithPrefix: (OFString *)prefix
			parser: (OFXMLParser *)parser OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<










|










>
>



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
@property (readonly, nonatomic) OFString *prefix;

/**
 * @brief The parser which encountered the unbound prefix.
 */
@property (readonly, nonatomic) OFXMLParser *parser;



/**
 * @brief Creates a new, autoreleased unbound prefix exception.
 *
 * @param prefix The prefix which is unbound
 * @param parser The parser which encountered the unbound prefix
 * @return A new, autoreleased unbound prefix exception
 */
+ (instancetype)exceptionWithPrefix: (OFString *)prefix
			     parser: (OFXMLParser *)parser;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated unbound prefix exception.
 *
 * @param prefix The prefix which is unbound
 * @param parser The parser which encountered the unbound prefix
 * @return An initialized unbound prefix exception
 */
- (instancetype)initWithPrefix: (OFString *)prefix
			parser: (OFXMLParser *)parser OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFUnboundPrefixException.m from [0c71a73c4b] to [09d3302162].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPrefix: (OFString *)prefix
			parser: (OFXMLParser *)parser
{
	self = [super init];

	@try {
		_prefix = [prefix copy];
		_parser = [parser retain];
	} @catch (id e) {







|
<







35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithPrefix: (OFString *)prefix parser: (OFXMLParser *)parser

{
	self = [super init];

	@try {
		_prefix = [prefix copy];
		_parser = [parser retain];
	} @catch (id e) {

Modified src/exceptions/OFUndefinedKeyException.h from [3f22266364] to [1d0c472b52].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *
 * @brief An exception indicating that a key is undefined (e.g. for Key Value
 *	  Coding).
 */
@interface OFUndefinedKeyException: OFException
{
	id _object;
	OFString *_key;
	id _Nullable _value;
}

/**
 * @brief The object on which the key is undefined.
 */
@property (readonly, nonatomic) id object;

/**
 * @brief The key which is undefined.
 */
@property (readonly, nonatomic) OFString *key;

/**
 * @brief The value for the undefined key
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id value;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased undefined key exception.
 *
 * @param object The object on which the key is undefined
 * @param key The key which is undefined
 *
 * @return A new, autoreleased undefined key exception
 */
+ (instancetype)exceptionWithObject: (id)object
				key: (OFString *)key;

/**
 * @brief Creates a new, autoreleased undefined key exception.
 *
 * @param object The object on which the key is undefined
 * @param key The key which is undefined
 * @param value The value for the undefined key
 *
 * @return A new, autoreleased undefined key exception
 */
+ (instancetype)exceptionWithObject: (id)object
				key: (OFString *)key
			      value: (nullable id)value;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated undefined key exception.
 *
 * @param object The object on which the key is undefined
 * @param key The key which is undefined
 *
 * @return An initialized undefined key exception
 */
- (instancetype)initWithObject: (id)object
			   key: (OFString *)key;

/**
 * @brief Initializes an already allocated undefined key exception.
 *
 * @param object The object on which the key is undefined
 * @param key The key which is undefined
 * @param value The value for the undefined key
 *
 * @return An initialized undefined key exception
 */
- (instancetype)initWithObject: (id)object
			   key: (OFString *)key
			 value: (nullable id)value OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







|











|






<
<








|
<











|


|









|
<











|

>
>



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
 *
 * @brief An exception indicating that a key is undefined (e.g. for Key Value
 *	  Coding).
 */
@interface OFUndefinedKeyException: OFException
{
	id _object;
	OFString *_Nullable _key;
	id _Nullable _value;
}

/**
 * @brief The object on which the key is undefined.
 */
@property (readonly, nonatomic) id object;

/**
 * @brief The key which is undefined.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *key;

/**
 * @brief The value for the undefined key
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id value;



/**
 * @brief Creates a new, autoreleased undefined key exception.
 *
 * @param object The object on which the key is undefined
 * @param key The key which is undefined
 *
 * @return A new, autoreleased undefined key exception
 */
+ (instancetype)exceptionWithObject: (id)object key: (OFString *)key;


/**
 * @brief Creates a new, autoreleased undefined key exception.
 *
 * @param object The object on which the key is undefined
 * @param key The key which is undefined
 * @param value The value for the undefined key
 *
 * @return A new, autoreleased undefined key exception
 */
+ (instancetype)exceptionWithObject: (id)object
				key: (nullable OFString *)key
			      value: (nullable id)value;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated undefined key exception.
 *
 * @param object The object on which the key is undefined
 * @param key The key which is undefined
 *
 * @return An initialized undefined key exception
 */
- (instancetype)initWithObject: (id)object key: (OFString *)key;


/**
 * @brief Initializes an already allocated undefined key exception.
 *
 * @param object The object on which the key is undefined
 * @param key The key which is undefined
 * @param value The value for the undefined key
 *
 * @return An initialized undefined key exception
 */
- (instancetype)initWithObject: (id)object
			   key: (nullable OFString *)key
			 value: (nullable id)value OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFUndefinedKeyException.m from [150023b635] to [fa0cc877cd].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize object = _object, key = _key, value = _value;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithObject: (id)object
				key: (OFString *)key
{
	return [[[self alloc] initWithObject: object
					 key: key] autorelease];
}

+ (instancetype)exceptionWithObject: (id)object
				key: (OFString *)key
			      value: (id)value
{
	return [[[self alloc] initWithObject: object
					 key: key
				       value: value] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object
			   key: (OFString *)key
{
	return [self initWithObject: object
				key: key
			      value: nil];
}

- (instancetype)initWithObject: (id)object
			   key: (OFString *)key
			 value: (id)value
{
	self = [super init];

	@try {
		_object = [object retain];
		_key = [key copy];
		_value = [value retain];







|
<

|
<
















|
<

|
<
<


|
<
<







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
@synthesize object = _object, key = _key, value = _value;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithObject: (id)object key: (OFString *)key

{
	return [[[self alloc] initWithObject: object key: key] autorelease];

}

+ (instancetype)exceptionWithObject: (id)object
				key: (OFString *)key
			      value: (id)value
{
	return [[[self alloc] initWithObject: object
					 key: key
				       value: value] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithObject: (id)object key: (OFString *)key

{
	return [self initWithObject: object key: key value: nil];


}

- (instancetype)initWithObject: (id)object key: (OFString *)key value: (id)value


{
	self = [super init];

	@try {
		_object = [object retain];
		_key = [key copy];
		_value = [value retain];

Modified src/exceptions/OFUnknownXMLEntityException.h from [cbe0c1933d] to [fdd37fe4f2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56


57
58
59
 * @brief Creates a new, autoreleased unknown XML entity exception.
 *
 * @param entityName The name of the unknown XML entity
 * @return A new, autoreleased unknown XML entity exception
 */
+ (instancetype)exceptionWithEntityName: (OFString *)entityName;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated unknown XML entity exception.
 *
 * @param entityName The name of the unknown XML entity
 * @return An initialized unknown XML entity exception
 */
- (instancetype)initWithEntityName: (OFString *)entityName
    OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







|









>
>



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
 * @brief Creates a new, autoreleased unknown XML entity exception.
 *
 * @param entityName The name of the unknown XML entity
 * @return A new, autoreleased unknown XML entity exception
 */
+ (instancetype)exceptionWithEntityName: (OFString *)entityName;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated unknown XML entity exception.
 *
 * @param entityName The name of the unknown XML entity
 * @return An initialized unknown XML entity exception
 */
- (instancetype)initWithEntityName: (OFString *)entityName
    OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFUnknownXMLEntityException.m from [0ddad3269c] to [87056195a2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@synthesize entityName = _entityName;

+ (instancetype)exceptionWithEntityName: (OFString *)entityName
{
	return [[[self alloc] initWithEntityName: entityName] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithEntityName: (OFString *)entityName
{
	self = [super init];

	@try {
		_entityName = [entityName copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}






- (void)dealloc
{
	[_entityName release];

	[super dealloc];
}







|

|















>
>
>
>
>







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
@synthesize entityName = _entityName;

+ (instancetype)exceptionWithEntityName: (OFString *)entityName
{
	return [[[self alloc] initWithEntityName: entityName] autorelease];
}

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)initWithEntityName: (OFString *)entityName
{
	self = [super init];

	@try {
		_entityName = [entityName copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_entityName release];

	[super dealloc];
}

Modified src/exceptions/OFUnlockFailedException.h from [23e77d50e9] to [ab29b208b2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * @class OFUnlockFailedException \
 *	  OFUnlockFailedException.h ObjFW/OFUnlockFailedException.h
 *
 * @brief An exception indicating that unlocking a lock failed.
 */
@interface OFUnlockFailedException: OFException
{
	id <OFLocking> _lock;
	int _errNo;
}

/**
 * @brief The lock which could not be unlocked.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id <OFLocking> lock;







|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 * @class OFUnlockFailedException \
 *	  OFUnlockFailedException.h ObjFW/OFUnlockFailedException.h
 *
 * @brief An exception indicating that unlocking a lock failed.
 */
@interface OFUnlockFailedException: OFException
{
	id <OFLocking> _Nullable _lock;
	int _errNo;
}

/**
 * @brief The lock which could not be unlocked.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id <OFLocking> lock;
57
58
59
60
61
62
63
64
65
66
67
68
 *
 * @param lock The lock which could not be unlocked
 * @param errNo The errno of the error that occurred
 * @return An initialized unlock failed exception
 */
- (instancetype)initWithLock: (nullable id <OFLocking>)lock
		       errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END







<
<



55
56
57
58
59
60
61


62
63
64
 *
 * @param lock The lock which could not be unlocked
 * @param errNo The errno of the error that occurred
 * @return An initialized unlock failed exception
 */
- (instancetype)initWithLock: (nullable id <OFLocking>)lock
		       errNo: (int)errNo OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFUnlockFailedException.m from [f8ba9a8de1] to [68e65cd613].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

#import "OFUnlockFailedException.h"
#import "OFString.h"

@implementation OFUnlockFailedException
@synthesize lock = _lock, errNo = _errNo;

+ (instancetype)exceptionWithLock: (id <OFLocking>)lock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithLock: lock
				     errNo: errNo] autorelease];
}

- (instancetype)initWithLock: (id <OFLocking>)lock
		       errNo: (int)errNo
{
	self = [super init];

	_lock = [lock retain];
	_errNo = errNo;

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_lock release];

	[super dealloc];
}

- (OFString *)description
{

	return [OFString stringWithFormat:
	    @"A lock of type %@ could not be unlocked: %s",
	    [_lock class], strerror(_errNo)];


}
@end







|
<

|
<


|
<









<
<
<
<
<









>
|
|
|
>
>


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

#import "OFUnlockFailedException.h"
#import "OFString.h"

@implementation OFUnlockFailedException
@synthesize lock = _lock, errNo = _errNo;

+ (instancetype)exceptionWithLock: (id <OFLocking>)lock errNo: (int)errNo

{
	return [[[self alloc] initWithLock: lock errNo: errNo] autorelease];

}

- (instancetype)initWithLock: (id <OFLocking>)lock errNo: (int)errNo

{
	self = [super init];

	_lock = [lock retain];
	_errNo = errNo;

	return self;
}






- (void)dealloc
{
	[_lock release];

	[super dealloc];
}

- (OFString *)description
{
	if (_lock != nil)
		return [OFString stringWithFormat:
		    @"A lock of type %@ could not be unlocked: %s",
		    [_lock class], strerror(_errNo)];
	else
		return @"A lock could not be unlocked!";
}
@end

Modified src/exceptions/OFUnsupportedProtocolException.h from [0d4f7d0a94] to [8223e3796e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 *	  ObjFW/OFUnsupportedProtocolException.h
 *
 * @brief An exception indicating that the protocol specified by the URL is not
 *	  supported.
 */
@interface OFUnsupportedProtocolException: OFException
{
	OFURL *_URL;
}

/**
 * @brief The URL whose protocol is unsupported.
 */
@property (readonly, nonatomic) OFURL *URL;

/**
 * @brief Creates a new, autoreleased unsupported protocol exception.
 *
 * @param URL The URL whose protocol is unsupported
 * @return A new, autoreleased unsupported protocol exception
 */
+ (instancetype)exceptionWithURL: (OFURL*)URL;

/**
 * @brief Initializes an already allocated unsupported protocol exception
 *
 * @param URL The URL whose protocol is unsupported
 * @return An initialized unsupported protocol exception
 */
- (instancetype)initWithURL: (OFURL*)URL OF_DESIGNATED_INITIALIZER;
@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
57
 *	  ObjFW/OFUnsupportedProtocolException.h
 *
 * @brief An exception indicating that the protocol specified by the URL is not
 *	  supported.
 */
@interface OFUnsupportedProtocolException: OFException
{
	OFURL *_Nullable _URL;
}

/**
 * @brief The URL whose protocol is unsupported.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFURL *URL;

/**
 * @brief Creates a new, autoreleased unsupported protocol exception.
 *
 * @param URL The URL whose protocol is unsupported
 * @return A new, autoreleased unsupported protocol exception
 */
+ (instancetype)exceptionWithURL: (nullable OFURL*)URL;

/**
 * @brief Initializes an already allocated unsupported protocol exception
 *
 * @param URL The URL whose protocol is unsupported
 * @return An initialized unsupported protocol exception
 */
- (instancetype)initWithURL: (nullable OFURL*)URL OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFUnsupportedProtocolException.m from [a55564c11d] to [ba097adb5e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFUnsupportedVersionException.h from [f8d8b879c7] to [fd604be220].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
}

/**
 * @brief The version which is unsupported.
 */
@property (readonly, nonatomic) OFString *version;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased unsupported version exception.
 *
 * @param version The version which is unsupported
 * @return A new, autoreleased unsupported version exception
 */
+ (instancetype)exceptionWithVersion: (OFString *)version;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated unsupported protocol exception.
 *
 * @param version The version which is unsupported
 * @return An initialized unsupported version exception
 */
- (instancetype)initWithVersion: (OFString *)version OF_DESIGNATED_INITIALIZER;


@end

OF_ASSUME_NONNULL_END







<
<








|








>
>



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
}

/**
 * @brief The version which is unsupported.
 */
@property (readonly, nonatomic) OFString *version;



/**
 * @brief Creates a new, autoreleased unsupported version exception.
 *
 * @param version The version which is unsupported
 * @return A new, autoreleased unsupported version exception
 */
+ (instancetype)exceptionWithVersion: (OFString *)version;

+ (instancetype)exception OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated unsupported protocol exception.
 *
 * @param version The version which is unsupported
 * @return An initialized unsupported version exception
 */
- (instancetype)initWithVersion: (OFString *)version OF_DESIGNATED_INITIALIZER;

- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFUnsupportedVersionException.m from [3987a818e5] to [ae8969b353].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/exceptions/OFWriteFailedException.h from [052da38254] to [52a0c0096f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
 * @brief The number of bytes already written before the write failed.
 *
 * This can be used to make sure that a retry does not write data already
 * written before.
 */
@property (readonly, nonatomic) size_t bytesWritten;

+ (instancetype)exceptionWithObject: (id)object
		    requestedLength: (size_t)requestedLength
			      errNo: (int)errNo OF_UNAVAILABLE;

/**
 * @brief Creates a new, autoreleased write failed exception.
 *
 * @param object The object from which reading or to which writing failed
 * @param requestedLength The requested length of the data that could not be
 *			  read / written
 * @param bytesWritten The amount of bytes already written before the write
 *		       failed
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased write failed exception
 */
+ (instancetype)exceptionWithObject: (id)object
		    requestedLength: (size_t)requestedLength
		       bytesWritten: (size_t)bytesWritten
			      errNo: (int)errNo;

- (instancetype)initWithObject: (id)object
	       requestedLength: (size_t)requestedLength
			 errNo: (int)errNo OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated write failed exception.
 *
 * @param object The object from which reading or to which writing failed
 * @param requestedLength The requested length of the data that could not be
 *			  read / written
 * @param bytesWritten The amount of bytes already written before the write
 *		       failed
 * @param errNo The errno of the error that occurred
 * @return A new open file failed exception
 */
- (instancetype)initWithObject: (id)object
	       requestedLength: (size_t)requestedLength
		  bytesWritten: (size_t)bytesWritten
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;




@end

OF_ASSUME_NONNULL_END







<
<
<
<
















|
|
|
















>
>
>
>



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
 * @brief The number of bytes already written before the write failed.
 *
 * This can be used to make sure that a retry does not write data already
 * written before.
 */
@property (readonly, nonatomic) size_t bytesWritten;





/**
 * @brief Creates a new, autoreleased write failed exception.
 *
 * @param object The object from which reading or to which writing failed
 * @param requestedLength The requested length of the data that could not be
 *			  read / written
 * @param bytesWritten The amount of bytes already written before the write
 *		       failed
 * @param errNo The errno of the error that occurred
 * @return A new, autoreleased write failed exception
 */
+ (instancetype)exceptionWithObject: (id)object
		    requestedLength: (size_t)requestedLength
		       bytesWritten: (size_t)bytesWritten
			      errNo: (int)errNo;

+ (instancetype)exceptionWithObject: (id)object
		    requestedLength: (size_t)requestedLength
			      errNo: (int)errNo OF_UNAVAILABLE;

/**
 * @brief Initializes an already allocated write failed exception.
 *
 * @param object The object from which reading or to which writing failed
 * @param requestedLength The requested length of the data that could not be
 *			  read / written
 * @param bytesWritten The amount of bytes already written before the write
 *		       failed
 * @param errNo The errno of the error that occurred
 * @return A new open file failed exception
 */
- (instancetype)initWithObject: (id)object
	       requestedLength: (size_t)requestedLength
		  bytesWritten: (size_t)bytesWritten
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;

- (instancetype)initWithObject: (id)object
	       requestedLength: (size_t)requestedLength
			 errNo: (int)errNo OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFWriteFailedException.m from [5aba14e7b3] to [1eca55cb4e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
62
63
64
65
66
67
68

69
70
71
72
73





74
75
	_bytesWritten = bytesWritten;

	return self;
}

- (OFString *)description
{

	return [OFString stringWithFormat:
	    @"Failed to write %zu bytes (after %zu bytes written) to an "
	    @"object of type %@: %@",
	    _requestedLength, _bytesWritten, [_object class],
	    of_strerror(_errNo)];





}
@end







>
|
|
|
|
|
>
>
>
>
>


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
	_bytesWritten = bytesWritten;

	return self;
}

- (OFString *)description
{
	if (_errNo != 0)
		return [OFString stringWithFormat:
		    @"Failed to write %zu bytes (after %zu bytes written) to "
		    @"an object of type %@: %@",
		    _requestedLength, _bytesWritten, [_object class],
		    OFStrError(_errNo)];
	else
		return [OFString stringWithFormat:
		    @"Failed to write %zu bytes (after %zu bytes written) to "
		    @"an object of type %@",
		    _requestedLength, _bytesWritten, [_object class]];
}
@end

Modified src/forwarding/apple-forwarding-arm.S from [bb35b86b46] to [638359a9cd].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.globl _of_forward
.globl _of_forward_stret

.section __TEXT, __objc_methname, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __DATA, __objc_selrefs, literal_pointers, no_dead_strip
sel_forwardingTargetForSelector_:
	.long str_forwardingTargetForSelector_

.section __DATA, __objc_imageinfo, regular, no_dead_strip
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
.arm
.align 2
_of_forward:
	stmfd	sp!, {r0-r4, lr}
	vstmdb	sp!, {d0-d7}

	ldr	r4, sel_forwardingTargetForSelector_$indirect_L0
L0:
	ldr	r4, [pc, r4]


<
<
|















|
|















|







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


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

.globl _OFForward
.globl _OFForward_stret

.section __TEXT, __objc_methname, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __DATA, __objc_selrefs, literal_pointers, no_dead_strip
sel_forwardingTargetForSelector_:
	.long str_forwardingTargetForSelector_

.section __DATA, __objc_imageinfo, regular, no_dead_strip
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
.arm
.align 2
_OFForward:
	stmfd	sp!, {r0-r4, lr}
	vstmdb	sp!, {d0-d7}

	ldr	r4, sel_forwardingTargetForSelector_$indirect_L0
L0:
	ldr	r4, [pc, r4]

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
	ldmfd	sp!, {r1-r4, lr}

	b	_objc_msgSend

0:
	vldmia	sp!, {d0-d7}
	ldmfd	sp!, {r0-r4, lr}
	b	_of_method_not_found

.data_region
sel_forwardingTargetForSelector_$indirect_L0:
	.long sel_forwardingTargetForSelector_-(L0+8)
.end_data_region

.align 2
_of_forward_stret:
	stmfd	sp!, {r0-r4, lr}
	vstmdb	sp!, {d0-d7}

	ldr	r4, sel_forwardingTargetForSelector_$indirect_L1
L1:
	ldr	r4, [pc, r4]








|







|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
	ldmfd	sp!, {r1-r4, lr}

	b	_objc_msgSend

0:
	vldmia	sp!, {d0-d7}
	ldmfd	sp!, {r0-r4, lr}
	b	_OFMethodNotFound

.data_region
sel_forwardingTargetForSelector_$indirect_L0:
	.long sel_forwardingTargetForSelector_-(L0+8)
.end_data_region

.align 2
_OFForward_stret:
	stmfd	sp!, {r0-r4, lr}
	vstmdb	sp!, {d0-d7}

	ldr	r4, sel_forwardingTargetForSelector_$indirect_L1
L1:
	ldr	r4, [pc, r4]

114
115
116
117
118
119
120
121
122
123
124
125
126
	ldmfd	sp!, {r2-r4, lr}

	b	_objc_msgSend_stret

0:
	vldmia	sp!, {d0-d7}
	ldmfd	sp!, {r0-r4, lr}
	b	_of_method_not_found_stret

.data_region
sel_forwardingTargetForSelector_$indirect_L1:
	.long sel_forwardingTargetForSelector_-(L1+8)
.end_data_region







|





112
113
114
115
116
117
118
119
120
121
122
123
124
	ldmfd	sp!, {r2-r4, lr}

	b	_objc_msgSend_stret

0:
	vldmia	sp!, {d0-d7}
	ldmfd	sp!, {r0-r4, lr}
	b	_OFMethodNotFound_stret

.data_region
sel_forwardingTargetForSelector_$indirect_L1:
	.long sel_forwardingTargetForSelector_-(L1+8)
.end_data_region

Modified src/forwarding/apple-forwarding-arm64.S from [b601a8acbc] to [d2adf305f5].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.globl _of_forward
.globl _of_forward_stret

.section __TEXT, __objc_methname, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __DATA, __objc_selrefs, literal_pointers, no_dead_strip
sel_forwardingTargetForSelector_:
	.quad str_forwardingTargetForSelector_

.section __DATA, __objc_imageinfo, regular, no_dead_strip
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
.align 2
_of_forward:
_of_forward_stret:
	stp	fp, lr, [sp, #-208]!
	mov	fp, sp
	sub	sp, sp, #208

	/* Save all arguments, x8 and x19 */
	stp	x0, x1, [sp]
	stp	x2, x3, [sp, #16]

<
<
|















|
|














|
|







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


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

.globl _OFForward
.globl _OFForward_stret

.section __TEXT, __objc_methname, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __DATA, __objc_selrefs, literal_pointers, no_dead_strip
sel_forwardingTargetForSelector_:
	.quad str_forwardingTargetForSelector_

.section __DATA, __objc_imageinfo, regular, no_dead_strip
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
.align 2
_OFForward:
_OFForward_stret:
	stp	fp, lr, [sp, #-208]!
	mov	fp, sp
	sub	sp, sp, #208

	/* Save all arguments, x8 and x19 */
	stp	x0, x1, [sp]
	stp	x2, x3, [sp, #16]
93
94
95
96
97
98
99
100
0:
	ldp	x0, x1, [sp]
	ldr	x19, [sp, #72]

	mov	sp, fp
	ldp	fp, lr, [sp], #208

	b	_of_method_not_found







|
91
92
93
94
95
96
97
98
0:
	ldp	x0, x1, [sp]
	ldr	x19, [sp, #72]

	mov	sp, fp
	ldp	fp, lr, [sp], #208

	b	_OFMethodNotFound

Modified src/forwarding/apple-forwarding-i386.S from [288bef5c12] to [11f716a457].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.intel_syntax noprefix

.globl _of_forward
.globl _of_forward_stret

.section __TEXT, __cstring, cstring_literals
Lstr_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __OBJC, __message_refs, literal_pointers, no_dead_strip
Lsel_forwardingTargetForSelector_:
	.long Lstr_forwardingTargetForSelector_

.section __OBJC, __image_info
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
_of_forward:
	push	ebp
	mov	ebp, esp

	push	ebx
	sub	esp, 20

	call	get_eip
0:

	mov	eax, [ebp+8]
	mov	[esp], eax
	call	_object_getClass

	mov	[esp], eax
	.att_syntax	/* Next line is broken in Intel syntax */
	movl	Lsel_forwardingTargetForSelector_-0b(%ebx), %eax
	.intel_syntax noprefix
	mov	[esp+4], eax
	call	_class_respondsToSelector

	test	eax, eax
	jz	0f

	mov	eax, [ebp+8]
	mov	[esp], eax
	.att_syntax	/* Next line is broken in Intel syntax */
	movl	Lsel_forwardingTargetForSelector_-0b(%ebx), %eax
	.intel_syntax noprefix
	mov	[esp+4], eax
	mov	eax, [ebp+12]
	mov	[esp+8], eax
	call	_objc_msgSend

	test	eax, eax
	jz	0f
	cmp	eax, [ebp+8]
	je	0f

	mov	[ebp+8], eax

	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	_objc_msgSend

0:
	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	_of_method_not_found

_of_forward_stret:
	push	ebp
	mov	ebp, esp

	push	ebx
	sub	esp, 20

	call	get_eip
0:

	mov	eax, [ebp+12]
	mov	[esp], eax
	call	_object_getClass

	mov	[esp], eax
	.att_syntax	/* Next line is broken in Intel syntax */
	movl	Lsel_forwardingTargetForSelector_-0b(%ebx), %eax
	.intel_syntax noprefix
	mov	[esp+4], eax
	call	_class_respondsToSelector

	test	eax, eax
	jz	0f

	mov	eax, [ebp+12]
	mov	[esp], eax
	.att_syntax	/* Next line is broken in Intel syntax */
	movl	Lsel_forwardingTargetForSelector_-0b(%ebx), %eax




	.intel_syntax noprefix
	mov	[esp+4], eax
	mov	eax, [ebp+16]
	mov	[esp+8], eax
	call	_objc_msgSend

	test	eax, eax
	jz	0f
	cmp	eax, [ebp+12]
	je	0f

	mov	[ebp+12], eax

	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	_objc_msgSend_stret

0:
	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	_of_method_not_found_stret

get_eip:
	mov	ebx, [esp]
	ret

<
<
|















<
<
|
|


|



|
|





|
|
|

|
|




|
|


|
<
|
<
|


|


|
|
<
|
|
|
|
<


|

|


|

|
|
|




|
|
|

|

|
|
|

|
|




|
|


|
<
|
<
|


|


|
|
<
|
>
>
>
>
<
<
<
<
<

|

|


|

|
|
|




|
|
|

|


|

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


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



.globl _OFForward
.globl _OFForward_stret

.section __TEXT, __cstring, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __OBJC, __message_refs, literal_pointers, no_dead_strip
sel_forwardingTargetForSelector_:
	.long str_forwardingTargetForSelector_

.section __OBJC, __image_info
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
_OFForward:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	subl	$20, %esp

	call	get_eip
0:

	movl	8(%ebp), %eax
	movl	%eax, (%esp)
	call	_object_getClass

	movl	%eax, (%esp)

	movl	sel_forwardingTargetForSelector_-0b(%ebx), %eax

	movl	%eax, 4(%esp)
	call	_class_respondsToSelector

	testl	%eax, %eax
	jz	0f

	movl	8(%ebp), %eax
	movl	%eax, (%esp)

	movl	sel_forwardingTargetForSelector_-0b(%ebx), %eax
	movl	%eax, 4(%esp)
	movl	12(%ebp), %eax
	movl	%eax, 8(%esp)

	call	_objc_msgSend

	testl	%eax, %eax
	jz	0f
	cmpl	8(%ebp), %eax
	je	0f

	movl	%eax, 8(%ebp)

	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	_objc_msgSend

0:
	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	_OFMethodNotFound

_OFForward_stret:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	subl	$20, %esp

	call	get_eip
0:

	movl	12(%ebp), %eax
	movl	%eax, (%esp)
	call	_object_getClass

	movl	%eax, (%esp)

	movl	sel_forwardingTargetForSelector_-0b(%ebx), %eax

	movl	%eax, 4(%esp)
	call	_class_respondsToSelector

	testl	%eax, %eax
	jz	0f

	movl	12(%ebp), %eax
	movl	%eax, (%esp)

	movl	sel_forwardingTargetForSelector_-0b(%ebx), %eax
	movl	%eax, 4(%esp)
	movl	16(%ebp), %eax
	movl	%eax, 8(%esp)
	call	_objc_msgSend






	testl	%eax, %eax
	jz	0f
	cmpl	12(%ebp), %eax
	je	0f

	movl	%eax, 12(%ebp)

	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	_objc_msgSend_stret

0:
	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	_OFMethodNotFound_stret

get_eip:
	movl	(%esp), %ebx
	ret

Modified src/forwarding/apple-forwarding-powerpc.S from [f6d7016a9d] to [8f7b914664].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.globl _of_forward
.globl _of_forward_stret

.section __TEXT, __cstring, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __OBJC, __message_refs
sel_forwardingTargetForSelector_:
	.long str_forwardingTargetForSelector_

.section __OBJC, __image_info
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
_of_forward:
	mflr	r0
	stw	r0, 8(r1)
	stwu	r1, -192(r1)

	/*
	 * Save all arguments and r13.
	 *

<
<
|















|
|













|







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


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

.globl _OFForward
.globl _OFForward_stret

.section __TEXT, __cstring, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __OBJC, __message_refs
sel_forwardingTargetForSelector_:
	.long str_forwardingTargetForSelector_

.section __OBJC, __image_info
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
_OFForward:
	mflr	r0
	stw	r0, 8(r1)
	stwu	r1, -192(r1)

	/*
	 * Save all arguments and r13.
	 *
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
	lwz	r3, 216(r1)
	lwz	r4, 220(r1)

	addi	r1, r1, 192
	lwz	r0, 8(r1)
	mtlr	r0

	b	_of_method_not_found

_of_forward_stret:
	mflr	r0
	stw	r0, 8(r1)
	stwu	r1, -184(r1)

	/*
	 * Save all arguments and r13.
	 *







|

|







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
	lwz	r3, 216(r1)
	lwz	r4, 220(r1)

	addi	r1, r1, 192
	lwz	r0, 8(r1)
	mtlr	r0

	b	_OFMethodNotFound

_OFForward_stret:
	mflr	r0
	stw	r0, 8(r1)
	stwu	r1, -184(r1)

	/*
	 * Save all arguments and r13.
	 *
234
235
236
237
238
239
240
241
	lwz	r4, 212(r1)
	lwz	r5, 216(r1)

	addi	r1, r1, 184
	lwz	r0, 8(r1)
	mtlr	r0

	b	_of_method_not_found_stret







|
232
233
234
235
236
237
238
239
	lwz	r4, 212(r1)
	lwz	r5, 216(r1)

	addi	r1, r1, 184
	lwz	r0, 8(r1)
	mtlr	r0

	b	_OFMethodNotFound_stret

Modified src/forwarding/apple-forwarding-x86_64.S from [5b8fcf7945] to [1324ab08b0].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.intel_syntax noprefix

.globl _of_forward
.globl _of_forward_stret

.section __TEXT, __objc_methname, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __DATA, __objc_selrefs, literal_pointers, no_dead_strip
sel_forwardingTargetForSelector_:
	.quad str_forwardingTargetForSelector_

.section __DATA, __objc_imageinfo, regular, no_dead_strip
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
_of_forward:
	push	rbp
	mov	rbp, rsp

	/* Save all arguments */
	sub	rsp, 0xC0	/* 16-byte alignment */
	mov	[rbp-0x08], rax
	mov	[rbp-0x10], rdi
	mov	[rbp-0x18], rsi
	mov	[rbp-0x20], rdx
	mov	[rbp-0x28], rcx
	mov	[rbp-0x30], r8
	mov	[rbp-0x38], r9
	movaps	[rbp-0x50], xmm0
	movaps	[rbp-0x60], xmm1
	movaps	[rbp-0x70], xmm2
	movaps	[rbp-0x80], xmm3
	movaps	[rbp-0x90], xmm4
	movaps	[rbp-0xA0], xmm5
	movaps	[rbp-0xB0], xmm6
	movaps	[rbp-0xC0], xmm7

	call	_object_getClass

	mov	rdi, rax
	mov	rsi, [rip+sel_forwardingTargetForSelector_]
	call	_class_respondsToSelector

	test	rax, rax
	jz	0f

	mov	rdi, [rbp-0x10]
	mov	rsi, [rip+sel_forwardingTargetForSelector_]
	mov	rdx, [rbp-0x18]
	call	_objc_msgSend

	test	rax, rax
	jz	0f
	cmp	rax, [rbp-0x10]
	je	0f

	mov	rdi, rax

	/* Restore all arguments, except %rdi */
	movaps	xmm7, [rbp-0xC0]
	movaps	xmm6, [rbp-0xB0]
	movaps	xmm5, [rbp-0xA0]
	movaps	xmm4, [rbp-0x90]
	movaps	xmm3, [rbp-0x80]
	movaps	xmm2, [rbp-0x70]
	movaps	xmm1, [rbp-0x60]
	movaps	xmm0, [rbp-0x50]
	mov	r9,   [rbp-0x38]
	mov	r8,   [rbp-0x30]
	mov	rcx,  [rbp-0x28]
	mov	rdx,  [rbp-0x20]
	mov	rsi,  [rbp-0x18]
	mov	rax,  [rbp-0x08]

	mov	rsp, rbp
	pop	rbp

	jmp	_objc_msgSend

0:
	mov	rdi, [rbp-0x10]
	mov	rsi, [rbp-0x18]

	mov	rsp, rbp
	pop	rbp

	jmp	_of_method_not_found

_of_forward_stret:
	push	rbp
	mov	rbp, rsp

	/* Save all arguments */
	sub	rsp, 0xC0	/* 16-byte alignment */
	mov	[rbp-0x08], rax
	mov	[rbp-0x10], rdi
	mov	[rbp-0x18], rsi
	mov	[rbp-0x20], rdx
	mov	[rbp-0x28], rcx
	mov	[rbp-0x30], r8
	mov	[rbp-0x38], r9
	movaps	[rbp-0x50], xmm0
	movaps	[rbp-0x60], xmm1
	movaps	[rbp-0x70], xmm2
	movaps	[rbp-0x80], xmm3
	movaps	[rbp-0x90], xmm4
	movaps	[rbp-0xA0], xmm5
	movaps	[rbp-0xB0], xmm6
	movaps	[rbp-0xC0], xmm7

	mov	rdi, rsi
	call	_object_getClass

	mov	rdi, rax
	mov	rsi, [rip+sel_forwardingTargetForSelector_]
	call	_class_respondsToSelector
	test	rax, rax
	jz	0f

	mov	rdi, [rbp-0x18]
	mov	rsi, [rip+sel_forwardingTargetForSelector_]
	mov	rdx, [rbp-0x20]
	call	_objc_msgSend

	test	rax, rax
	jz	0f
	cmp	rax, [rbp-0x18]
	je	0f

	mov	rsi, rax

	/* Restore all arguments, except %rsi */
	movaps	xmm7, [rbp-0xC0]
	movaps	xmm6, [rbp-0xB0]
	movaps	xmm5, [rbp-0xA0]
	movaps	xmm4, [rbp-0x90]
	movaps	xmm3, [rbp-0x80]
	movaps	xmm2, [rbp-0x70]
	movaps	xmm1, [rbp-0x60]
	movaps	xmm0, [rbp-0x50]
	mov	r9,   [rbp-0x38]
	mov	r8,   [rbp-0x30]
	mov	rcx,  [rbp-0x28]
	mov	rdx,  [rbp-0x20]
	mov	rdi,  [rbp-0x10]
	mov	rax,  [rbp-0x08]

	mov	rsp, rbp
	pop	rbp

	jmp	_objc_msgSend_stret

0:
	mov	rdi, [rbp-0x10]
	mov	rsi, [rbp-0x18]
	mov	rdx, [rbp-0x20]

	mov	rsp, rbp
	pop	rbp

	jmp	_of_method_not_found_stret

<
<
|















<
<
|
|













|
|
|


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



|
|


|


|
|
|


|

|


|


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

|
|




|
|

|
|

|

|
|
|


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

|


|
|

|


|
|
|


|

|


|


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

|
|




|
|
|

|
|

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


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



.globl _OFForward
.globl _OFForward_stret

.section __TEXT, __objc_methname, cstring_literals
str_forwardingTargetForSelector_:
	.asciz "forwardingTargetForSelector:"

.section __DATA, __objc_selrefs, literal_pointers, no_dead_strip
sel_forwardingTargetForSelector_:
	.quad str_forwardingTargetForSelector_

.section __DATA, __objc_imageinfo, regular, no_dead_strip
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
_OFForward:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0xC0, %rsp	/* 16-byte alignment */
	movq	%rax, -0x8(%rbp)
	movq	%rdi, -0x10(%rbp)
	movq	%rsi, -0x18(%rbp)
	movq	%rdx, -0x20(%rbp)
	movq	%rcx, -0x28(%rbp)
	movq	%r8, -0x30(%rbp)
	movq	%r9, -0x38(%rbp)
	movaps	%xmm0, -0x50(%rbp)
	movaps	%xmm1, -0x60(%rbp)
	movaps	%xmm2, -0x70(%rbp)
	movaps	%xmm3, -0x80(%rbp)
	movaps	%xmm4, -0x90(%rbp)
	movaps	%xmm5, -0xA0(%rbp)
	movaps	%xmm6, -0xB0(%rbp)
	movaps	%xmm7, -0xC0(%rbp)

	call	_object_getClass

	movq	%rax, %rdi
	movq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	_class_respondsToSelector

	testq	%rax, %rax
	jz	0f

	movq	-0x10(%rbp), %rdi
	movq	sel_forwardingTargetForSelector_(%rip), %rsi
	movq	-0x18(%rbp), %rdx
	call	_objc_msgSend

	testq	%rax, %rax
	jz	0f
	cmpq	-0x10(%rbp), %rax
	je	0f

	movq	%rax, %rdi

	/* Restore all arguments, except %rdi */
	movaps	-0xC0(%rbp), %xmm7
	movaps	-0xB0(%rbp), %xmm6
	movaps	-0xA0(%rbp), %xmm5
	movaps	-0x90(%rbp), %xmm4
	movaps	-0x80(%rbp), %xmm3
	movaps	-0x70(%rbp), %xmm2
	movaps	-0x60(%rbp), %xmm1
	movaps	-0x50(%rbp), %xmm0
	movq	-0x38(%rbp), %r9
	movq	-0x30(%rbp), %r8
	movq	-0x28(%rbp), %rcx
	movq	-0x20(%rbp), %rdx
	movq	-0x18(%rbp), %rsi
	movq	-0x8(%rbp), %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_objc_msgSend

0:
	movq	-0x10(%rbp), %rdi
	movq	-0x18(%rbp), %rsi

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_OFMethodNotFound

_OFForward_stret:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0xC0, %rsp	/* 16-byte alignment */
	movq	%rax, -0x8(%rbp)
	movq	%rdi, -0x10(%rbp)
	movq	%rsi, -0x18(%rbp)
	movq	%rdx, -0x20(%rbp)
	movq	%rcx, -0x28(%rbp)
	movq	%r8, -0x30(%rbp)
	movq	%r9, -0x38(%rbp)
	movaps	%xmm0, -0x50(%rbp)
	movaps	%xmm1, -0x60(%rbp)
	movaps	%xmm2, -0x70(%rbp)
	movaps	%xmm3, -0x80(%rbp)
	movaps	%xmm4, -0x90(%rbp)
	movaps	%xmm5, -0xA0(%rbp)
	movaps	%xmm6, -0xB0(%rbp)
	movaps	%xmm7, -0xC0(%rbp)

	movq	%rsi, %rdi
	call	_object_getClass

	movq	%rax, %rdi
	movq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	_class_respondsToSelector
	testq	%rax, %rax
	jz	0f

	movq	-0x18(%rbp), %rdi
	movq	sel_forwardingTargetForSelector_(%rip), %rsi
	movq	-0x20(%rbp), %rdx
	call	_objc_msgSend

	testq	%rax, %rax
	jz	0f
	cmpq	-0x18(%rbp), %rax
	je	0f

	movq	%rax, %rsi

	/* Restore all arguments, except %rsi */
	movaps	-0xC0(%rbp), %xmm7
	movaps	-0xB0(%rbp), %xmm6
	movaps	-0xA0(%rbp), %xmm5
	movaps	-0x90(%rbp), %xmm4
	movaps	-0x80(%rbp), %xmm3
	movaps	-0x70(%rbp), %xmm2
	movaps	-0x60(%rbp), %xmm1
	movaps	-0x50(%rbp), %xmm0
	movq	-0x38(%rbp), %r9
	movq	-0x30(%rbp), %r8
	movq	-0x28(%rbp), %rcx
	movq	-0x20(%rbp), %rdx
	movq	-0x10(%rbp), %rdi
	movq	-0x8(%rbp), %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_objc_msgSend_stret

0:
	movq	-0x10(%rbp), %rdi
	movq	-0x18(%rbp), %rsi
	movq	-0x20(%rbp), %rdx

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_OFMethodNotFound_stret

Modified src/forwarding/forwarding-arm-elf.S from [19985726e9] to [ebb3f926fa].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

#include "platform.h"

#ifdef HAVE_VFP2
.fpu vfp
#endif

.globl of_forward
.globl of_forward_stret

.section .text
of_forward:
#ifdef HAVE_VFP2
	vstmdb	sp!, {d0-d7}
#endif
	stmfd	sp!, {r0-r4, lr}

	ldr	r4, sel_forwardingTargetForSelector_$indirect_.L0
.L0:







|
|


|







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

#include "platform.h"

#ifdef HAVE_VFP2
.fpu vfp
#endif

.globl OFForward
.globl OFForward_stret

.section .text
OFForward:
#ifdef HAVE_VFP2
	vstmdb	sp!, {d0-d7}
#endif
	stmfd	sp!, {r0-r4, lr}

	ldr	r4, sel_forwardingTargetForSelector_$indirect_.L0
.L0:
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
	bx	r12

0:
	ldmfd	sp!, {r0-r4, lr}
#ifdef HAVE_VFP2
	vldmia	sp!, {d0-d7}
#endif
	b	of_method_not_found(PLT)
.type of_forward, %function
.size of_forward, .-of_forward

of_forward_stret:
#ifdef HAVE_VFP2
	vstmdb	sp!, {d0-d7}
#endif
	stmfd	sp!, {r0-r4, lr}

	ldr	r4, sel_forwardingTargetForSelector_$indirect_.L1
.L1:







|
|
|

|







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
	bx	r12

0:
	ldmfd	sp!, {r0-r4, lr}
#ifdef HAVE_VFP2
	vldmia	sp!, {d0-d7}
#endif
	b	OFMethodNotFound(PLT)
.type OFForward, %function
.size OFForward, .-OFForward

OFForward_stret:
#ifdef HAVE_VFP2
	vstmdb	sp!, {d0-d7}
#endif
	stmfd	sp!, {r0-r4, lr}

	ldr	r4, sel_forwardingTargetForSelector_$indirect_.L1
.L1:
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
	bx	r12

0:
	ldmfd	sp!, {r0-r4, lr}
#ifdef HAVE_VFP2
	vldmia	sp!, {d0-d7}
#endif
	b	of_method_not_found_stret(PLT)
.type of_forward_stret, %function
.size of_forward_stret, .-of_forward_stret

init:
	ldr	r0, module$indirect_.L2
.L2:
	add	r0, pc
	b	__objc_exec_class(PLT)








|
|
|







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
	bx	r12

0:
	ldmfd	sp!, {r0-r4, lr}
#ifdef HAVE_VFP2
	vldmia	sp!, {d0-d7}
#endif
	b	OFMethodNotFound_stret(PLT)
.type OFForward_stret, %function
.size OFForward_stret, .-OFForward_stret

init:
	ldr	r0, module$indirect_.L2
.L2:
	add	r0, pc
	b	__objc_exec_class(PLT)

Modified src/forwarding/forwarding-arm64-elf.S from [d178718011] to [81775de57c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.globl of_forward
.globl of_forward_stret

.section .text
of_forward:
of_forward_stret:
	stp	fp, lr, [sp, #-208]!
	mov	fp, sp
	sub	sp, sp, #208

	/* Save all arguments, x8 and x19 */
	stp	x0, x1, [sp]
	stp	x2, x3, [sp, #16]

<
<
|

















|
|


|
|







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

#include "config.h"

#include "platform.h"

.globl OFForward
.globl OFForward_stret

.section .text
OFForward:
OFForward_stret:
	stp	fp, lr, [sp, #-208]!
	mov	fp, sp
	sub	sp, sp, #208

	/* Save all arguments, x8 and x19 */
	stp	x0, x1, [sp]
	stp	x2, x3, [sp, #16]
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
0:
	ldp	x0, x1, [sp]
	ldr	x19, [sp, #72]

	mov	sp, fp
	ldp	fp, lr, [sp], #208

	b	of_method_not_found
.type of_forward, %function
.size of_forward, .-of_forward
.type of_forward_stret, %function
.size of_forward_stret, .-of_forward_stret

init:
	adrp	x0, module
	add	x0, x0, :lo12:module
	b	__objc_exec_class

.section .init_array, "aw", %init_array







|
|
|
|
|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
0:
	ldp	x0, x1, [sp]
	ldr	x19, [sp, #72]

	mov	sp, fp
	ldp	fp, lr, [sp], #208

	b	OFMethodNotFound
.type OFForward, %function
.size OFForward, .-OFForward
.type OFForward_stret, %function
.size OFForward_stret, .-OFForward_stret

init:
	adrp	x0, module
	add	x0, x0, :lo12:module
	b	__objc_exec_class

.section .init_array, "aw", %init_array

Modified src/forwarding/forwarding-mips-elf.S from [c8fc1f4693] to [e03d5d5039].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.globl of_forward
.globl of_forward_stret

#ifdef OF_PIC
.macro j_pic symbol
	lw	$t9, %call16(\symbol)($gp)
	jr	$t9
.endm
.macro jal_pic symbol
	lw	$t9, %call16(\symbol)($gp)
	jalr	$t9
.endm
#else
.macro j_pic symbol
	j	\symbol
.endm
.macro jal_pic symbol
	jal	\symbol
.endm
#endif

.section .text
of_forward:
#ifdef OF_PIC
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9
#endif

	addiu	$sp, $sp, -96

<
<
|

















|
|




















|







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


 * 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 "platform.h"

.globl OFForward
.globl OFForward_stret

#ifdef OF_PIC
.macro j_pic symbol
	lw	$t9, %call16(\symbol)($gp)
	jr	$t9
.endm
.macro jal_pic symbol
	lw	$t9, %call16(\symbol)($gp)
	jalr	$t9
.endm
#else
.macro j_pic symbol
	j	\symbol
.endm
.macro jal_pic symbol
	jal	\symbol
.endm
#endif

.section .text
OFForward:
#ifdef OF_PIC
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9
#endif

	addiu	$sp, $sp, -96
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
	lw	$s1, 24($sp)
	lw	$s0, 20($sp)
	lw	$ra, 16($sp)

	addiu	$sp, $sp, 96

	j_pic	of_method_not_found
.type of_forward, %function
.size of_forward, .-of_forward

of_forward_stret:
#ifdef OF_PIC
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9
#endif

	addiu	$sp, $sp, -96







|
|

|







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
	lw	$s1, 24($sp)
	lw	$s0, 20($sp)
	lw	$ra, 16($sp)

	addiu	$sp, $sp, 96

	j_pic	of_method_not_found
.type OFForward, %function
.size OFForward, .-OFForward

OFForward_stret:
#ifdef OF_PIC
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9
#endif

	addiu	$sp, $sp, -96
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
	lw	$s1, 24($sp)
	lw	$s0, 20($sp)
	lw	$ra, 16($sp)

	addiu	$sp, $sp, 96

	j_pic	of_method_not_found_stret
.type of_forward_stret, %function
.size of_forward_stret, .-of_forward_stret

init:
#ifdef OF_PIC
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9








|
|







282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
	lw	$s1, 24($sp)
	lw	$s0, 20($sp)
	lw	$ra, 16($sp)

	addiu	$sp, $sp, 96

	j_pic	of_method_not_found_stret
.type OFForward_stret, %function
.size OFForward_stret, .-OFForward_stret

init:
#ifdef OF_PIC
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9

Modified src/forwarding/forwarding-powerpc-elf.S from [765ed4e505] to [65258e67de].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.globl of_forward
.globl of_forward_stret

.section .text
of_forward:
	stwu	%r1, -112(%r1)
	mflr	%r0
	stw	%r0, 116(%r1)

	stw	%r30, 104(%r1)

	bl	0f
0:
	mflr	%r30
	addis	%r30, %r30, .Lbiased_got2-0b@ha
	addi	%r30, %r30, .Lbiased_got2-0b@l


	/* Save all arguments */
	stw	%r3, 8(%r1)
	stw	%r4, 12(%r1)
	stw	%r5, 16(%r1)
	stw	%r6, 20(%r1)
	stw	%r7, 24(%r1)

<
<
|

















|
|


|



>







>







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


 * 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 "platform.h"

.globl OFForward
.globl OFForward_stret

.section .text
OFForward:
	stwu	%r1, -112(%r1)
	mflr	%r0
	stw	%r0, 116(%r1)
#ifdef OF_PIC
	stw	%r30, 104(%r1)

	bl	0f
0:
	mflr	%r30
	addis	%r30, %r30, .Lbiased_got2-0b@ha
	addi	%r30, %r30, .Lbiased_got2-0b@l
#endif

	/* Save all arguments */
	stw	%r3, 8(%r1)
	stw	%r4, 12(%r1)
	stw	%r5, 16(%r1)
	stw	%r6, 20(%r1)
	stw	%r7, 24(%r1)
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
	stfd	%f3, 56(%r1)
	stfd	%f4, 64(%r1)
	stfd	%f5, 72(%r1)
	stfd	%f6, 80(%r1)
	stfd	%f7, 88(%r1)
	stfd	%f8, 96(%r1)


	bl	object_getClass+0x8000@plt

	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
	bl	class_respondsToSelector+0x8000@plt








	cmpwi	%r3, 0
	beq-	0f

	lwz	%r3, 8(%r1)

	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
	bl	objc_msg_lookup+0x8000@plt





	mtctr	%r3

	lwz	%r3, 8(%r1)

	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)




	lwz	%r5, 12(%r1)
	bctrl

	cmpwi	%r3, 0
	beq-	0f
	lwz	%r4, 8(%r1)
	cmpw	%r3, %r4
	beq-	0f

	stw	%r3, 8(%r1)

	lwz	%r4, 12(%r1)

	bl	objc_msg_lookup+0x8000@plt



	mtctr	%r3

	/* Restore all arguments */
	lwz	%r3, 8(%r1)
	lwz	%r4, 12(%r1)
	lwz	%r5, 16(%r1)
	lwz	%r6, 20(%r1)







>




>
>
>
>
>
>
>





>


>
>
>
>
>



>

>
>
>
>












>

>
>
>







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
	stfd	%f3, 56(%r1)
	stfd	%f4, 64(%r1)
	stfd	%f5, 72(%r1)
	stfd	%f6, 80(%r1)
	stfd	%f7, 88(%r1)
	stfd	%f8, 96(%r1)

#ifdef OF_PIC
	bl	object_getClass+0x8000@plt

	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
	bl	class_respondsToSelector+0x8000@plt
#else
	bl	object_getClass

	lis	%r4, sel_forwardingTargetForSelector_@ha
	la	%r4, sel_forwardingTargetForSelector_@l(%r4)
	bl	class_respondsToSelector
#endif

	cmpwi	%r3, 0
	beq-	0f

	lwz	%r3, 8(%r1)
#ifdef OF_PIC
	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
	bl	objc_msg_lookup+0x8000@plt
#else
	lis	%r4, sel_forwardingTargetForSelector_@ha
	la	%r4, sel_forwardingTargetForSelector_@l(%r4)
	bl	objc_msg_lookup
#endif
	mtctr	%r3

	lwz	%r3, 8(%r1)
#ifdef OF_PIC
	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
#else
	lis	%r4, sel_forwardingTargetForSelector_@ha
	la	%r4, sel_forwardingTargetForSelector_@l(%r4)
#endif
	lwz	%r5, 12(%r1)
	bctrl

	cmpwi	%r3, 0
	beq-	0f
	lwz	%r4, 8(%r1)
	cmpw	%r3, %r4
	beq-	0f

	stw	%r3, 8(%r1)

	lwz	%r4, 12(%r1)
#ifdef OF_PIC
	bl	objc_msg_lookup+0x8000@plt
#else
	bl	objc_msg_lookup
#endif
	mtctr	%r3

	/* Restore all arguments */
	lwz	%r3, 8(%r1)
	lwz	%r4, 12(%r1)
	lwz	%r5, 16(%r1)
	lwz	%r6, 20(%r1)
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
	lfd	%f3, 56(%r1)
	lfd	%f4, 64(%r1)
	lfd	%f5, 72(%r1)
	lfd	%f6, 80(%r1)
	lfd	%f7, 88(%r1)
	lfd	%f8, 96(%r1)


	lwz	%r30, 104(%r1)

	lwz	%r0, 116(%r1)
	mtlr	%r0
	addi	%r1, %r1, 112
	bctr

0:
	lwz	%r3, 8(%r1)
	lwz	%r4, 12(%r1)


	lwz	%r0, .Lgot_of_method_not_found-.Lbiased_got2(%r30)
	mtctr	%r0

	lwz	%r30, 104(%r1)


	lwz	%r0, 116(%r1)
	mtlr	%r0
	addi	%r1, %r1, 112


	bctr



.type of_forward, @function
.size of_forward, .-of_forward

of_forward_stret:
	stwu	%r1, -112(%r1)
	mflr	%r0
	stw	%r0, 116(%r1)

	stw	%r30, 104(%r1)

	bl	0f
0:
	mflr	%r30
	addis	%r30, %r30, .Lbiased_got2-0b@ha
	addi	%r30, %r30, .Lbiased_got2-0b@l


	/* Save all arguments */
	stw	%r3, 8(%r1)
	stw	%r4, 12(%r1)
	stw	%r5, 16(%r1)
	stw	%r6, 20(%r1)
	stw	%r7, 24(%r1)







>

>








>
>
|

<

>
>




>

>
>
>
|
|

|



>







>







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
	lfd	%f3, 56(%r1)
	lfd	%f4, 64(%r1)
	lfd	%f5, 72(%r1)
	lfd	%f6, 80(%r1)
	lfd	%f7, 88(%r1)
	lfd	%f8, 96(%r1)

#ifdef OF_PIC
	lwz	%r30, 104(%r1)
#endif
	lwz	%r0, 116(%r1)
	mtlr	%r0
	addi	%r1, %r1, 112
	bctr

0:
	lwz	%r3, 8(%r1)
	lwz	%r4, 12(%r1)

#ifdef OF_PIC
	lwz	%r0, .Lgot_OFMethodNotFound-.Lbiased_got2(%r30)
	mtctr	%r0

	lwz	%r30, 104(%r1)
#endif

	lwz	%r0, 116(%r1)
	mtlr	%r0
	addi	%r1, %r1, 112

#ifdef OF_PIC
	bctr
#else
	b	OFMethodNotFound
#endif
.type OFForward, @function
.size OFForward, .-OFForward

OFForward_stret:
	stwu	%r1, -112(%r1)
	mflr	%r0
	stw	%r0, 116(%r1)
#ifdef OF_PIC
	stw	%r30, 104(%r1)

	bl	0f
0:
	mflr	%r30
	addis	%r30, %r30, .Lbiased_got2-0b@ha
	addi	%r30, %r30, .Lbiased_got2-0b@l
#endif

	/* Save all arguments */
	stw	%r3, 8(%r1)
	stw	%r4, 12(%r1)
	stw	%r5, 16(%r1)
	stw	%r6, 20(%r1)
	stw	%r7, 24(%r1)
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
	stfd	%f4, 64(%r1)
	stfd	%f5, 72(%r1)
	stfd	%f6, 80(%r1)
	stfd	%f7, 88(%r1)
	stfd	%f8, 96(%r1)

	mr	%r3, %r4

	bl	object_getClass+0x800@plt

	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
	bl	class_respondsToSelector+0x8000@plt








	cmpwi	%r3, 0
	beq-	0f

	lwz	%r3, 12(%r1)

	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
	bl	objc_msg_lookup+0x8000@plt





	mtctr	%r3

	lwz	%r3, 12(%r1)

	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)




	lwz	%r5, 16(%r1)
	bctrl

	cmpwi	%r3, 0
	beq-	0f
	lwz	%r4, 12(%r1)
	cmpw	%r3, %r4
	beq-	0f

	stw	%r3, 12(%r1)

	lwz	%r4, 16(%r1)

	bl	objc_msg_lookup_stret+0x8000@plt



	mtctr	%r3

	/* Restore all arguments */
	lwz	%r3, 8(%r1)
	lwz	%r4, 12(%r1)
	lwz	%r5, 16(%r1)
	lwz	%r6, 20(%r1)







>
|



>
>
>
>
>
>
>





>


>
>
>
>
>



>

>
>
>
>












>

>
>
>







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
	stfd	%f4, 64(%r1)
	stfd	%f5, 72(%r1)
	stfd	%f6, 80(%r1)
	stfd	%f7, 88(%r1)
	stfd	%f8, 96(%r1)

	mr	%r3, %r4
#ifdef OF_PIC
	bl	object_getClass+0x8000@plt

	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
	bl	class_respondsToSelector+0x8000@plt
#else
	bl	object_getClass

	lis	%r4, sel_forwardingTargetForSelector_@ha
	la	%r4, sel_forwardingTargetForSelector_@l(%r4)
	bl	class_respondsToSelector
#endif

	cmpwi	%r3, 0
	beq-	0f

	lwz	%r3, 12(%r1)
#ifdef OF_PIC
	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
	bl	objc_msg_lookup+0x8000@plt
#else
	lis	%r4, sel_forwardingTargetForSelector_@ha
	la	%r4, sel_forwardingTargetForSelector_@l(%r4)
	bl	objc_msg_lookup
#endif
	mtctr	%r3

	lwz	%r3, 12(%r1)
#ifdef OF_PIC
	lwz	%r4, .Lgot_sel_forwardingTargetForSelector_-.Lbiased_got2(%r30)
#else
	lis	%r4, sel_forwardingTargetForSelector_@ha
	la	%r4, sel_forwardingTargetForSelector_@l(%r4)
#endif
	lwz	%r5, 16(%r1)
	bctrl

	cmpwi	%r3, 0
	beq-	0f
	lwz	%r4, 12(%r1)
	cmpw	%r3, %r4
	beq-	0f

	stw	%r3, 12(%r1)

	lwz	%r4, 16(%r1)
#ifdef OF_PIC
	bl	objc_msg_lookup_stret+0x8000@plt
#else
	bl	objc_msg_lookup_stret
#endif
	mtctr	%r3

	/* Restore all arguments */
	lwz	%r3, 8(%r1)
	lwz	%r4, 12(%r1)
	lwz	%r5, 16(%r1)
	lwz	%r6, 20(%r1)
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
	lfd	%f3, 56(%r1)
	lfd	%f4, 64(%r1)
	lfd	%f5, 72(%r1)
	lfd	%f6, 80(%r1)
	lfd	%f7, 88(%r1)
	lfd	%f8, 96(%r1)




	lwz	%r0, 116(%r1)
	mtlr	%r0
	addi	%r1, %r1, 112
	bctr

0:
	lwz	%r3, 8(%r1)
	lwz	%r4, 12(%r1)
	lwz	%r5, 16(%r1)


	lwz	%r0, .Lgot_of_method_not_found_stret-.Lbiased_got2(%r30)
	mtctr	%r0

	lwz	%r30, 104(%r1)


	lwz	%r0, 116(%r1)
	mtlr	%r0
	addi	%r1, %r1, 112


	bctr



.type of_forward_stret, @function
.size of_forward_stret, .-of_forward_stret

init:
	stwu	%r1, -16(%r1)
	mflr	%r0
	stw	%r0, 20(%r1)

	stw	%r30, 8(%r1)

	bl	0f
0:
	mflr	%r30
	addis	%r30, %r30, .Lbiased_got2-0b@ha
	addi	%r30, %r30, .Lbiased_got2-0b@l

	lwz	%r3, .Lgot_module-.Lbiased_got2(%r30)
	bl	__objc_exec_class+0x8000@plt

	lwz	%r30, 8(%r1)






	lwz	%r0, 20(%r1)
	addi	%r1, %r1, 16
	mtlr	%r0
	blr

.section .ctors, "aw", @progbits
	.long init







>
>
>









>
>
|

<

>
>




>

>
>
>
|
|





>












>
>
>
>
>
>







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
	lfd	%f3, 56(%r1)
	lfd	%f4, 64(%r1)
	lfd	%f5, 72(%r1)
	lfd	%f6, 80(%r1)
	lfd	%f7, 88(%r1)
	lfd	%f8, 96(%r1)

#ifdef OF_PIC
	lwz	%r30, 104(%r1)
#endif
	lwz	%r0, 116(%r1)
	mtlr	%r0
	addi	%r1, %r1, 112
	bctr

0:
	lwz	%r3, 8(%r1)
	lwz	%r4, 12(%r1)
	lwz	%r5, 16(%r1)

#ifdef OF_PIC
	lwz	%r0, .Lgot_OFMethodNotFound_stret-.Lbiased_got2(%r30)
	mtctr	%r0

	lwz	%r30, 104(%r1)
#endif

	lwz	%r0, 116(%r1)
	mtlr	%r0
	addi	%r1, %r1, 112

#ifdef OF_PIC
	bctr
#else
	b	OFMethodNotFound_stret
#endif
.type OFForward_stret, @function
.size OFForward_stret, .-OFForward_stret

init:
	stwu	%r1, -16(%r1)
	mflr	%r0
	stw	%r0, 20(%r1)
#ifdef OF_PIC
	stw	%r30, 8(%r1)

	bl	0f
0:
	mflr	%r30
	addis	%r30, %r30, .Lbiased_got2-0b@ha
	addi	%r30, %r30, .Lbiased_got2-0b@l

	lwz	%r3, .Lgot_module-.Lbiased_got2(%r30)
	bl	__objc_exec_class+0x8000@plt

	lwz	%r30, 8(%r1)
#else
	lis	%r3, module@ha
	la	%r3, module@l(%r3)
	bl	__objc_exec_class
#endif

	lwz	%r0, 20(%r1)
	addi	%r1, %r1, 16
	mtlr	%r0
	blr

.section .ctors, "aw", @progbits
	.long init
266
267
268
269
270
271
272

273
274
275
276
277
278
279
280
281
282

283
284
285
286
	.long 0, sel_forwardingTargetForSelector_
	.short 0, 0
	.long 0
	.long 0
module:
	.long 8, 16, 0, symtab


.section .got2, "aw"
.Lbiased_got2 = .+0x8000
.Lgot_module:
	.long module
.Lgot_sel_forwardingTargetForSelector_:
	.long sel_forwardingTargetForSelector_
.Lgot_of_method_not_found:
	.long of_method_not_found
.Lgot_of_method_not_found_stret:
	.long of_method_not_found_stret


#ifdef OF_LINUX
.section .note.GNU-stack, "", @progbits
#endif







>






|
|
|
|
>




340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
	.long 0, sel_forwardingTargetForSelector_
	.short 0, 0
	.long 0
	.long 0
module:
	.long 8, 16, 0, symtab

#ifdef OF_PIC
.section .got2, "aw"
.Lbiased_got2 = .+0x8000
.Lgot_module:
	.long module
.Lgot_sel_forwardingTargetForSelector_:
	.long sel_forwardingTargetForSelector_
.Lgot_OFMethodNotFound:
	.long OFMethodNotFound
.Lgot_OFMethodNotFound_stret:
	.long OFMethodNotFound_stret
#endif

#ifdef OF_LINUX
.section .note.GNU-stack, "", @progbits
#endif

Modified src/forwarding/forwarding-sparc-elf.S from [8a7dcec5aa] to [8ec3e64b89].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.globl of_forward
.globl of_forward_stret

.section .text
of_forward:
	save	%sp, -96, %sp

#ifdef OF_PIC
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %l7
	call	add_pc
	 add	%l7, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %l7
#endif

<
<
|

















|
|


|







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


 * 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 "platform.h"

.globl OFForward
.globl OFForward_stret

.section .text
OFForward:
	save	%sp, -96, %sp

#ifdef OF_PIC
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %l7
	call	add_pc
	 add	%l7, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %l7
#endif
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
	call	objc_msg_lookup
	 mov	%i1, %o1

	jmpl	%o0, %g0
	 restore

0:
	call	of_method_not_found
	 restore
.type of_forward, %function
.size of_forward, .-of_forward

of_forward_stret:
	save	%sp, -96, %sp

#ifdef OF_PIC
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %l7
	call	add_pc
	 add	%l7, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %l7
#endif







|

|
|

|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
	call	objc_msg_lookup
	 mov	%i1, %o1

	jmpl	%o0, %g0
	 restore

0:
	call	OFMethodNotFound
	 restore
.type OFForward, %function
.size OFForward, .-OFForward

OFForward_stret:
	save	%sp, -96, %sp

#ifdef OF_PIC
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %l7
	call	add_pc
	 add	%l7, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %l7
#endif
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
	call	objc_msg_lookup
	 mov	%i2, %o1

	jmpl	%o0, %g0
	 restore

0:
	call	of_method_not_found_stret
	 restore
.type of_forward_stret, %function
.size of_forward_stret, .-of_forward_stret

init:
	save	%sp, -96, %sp

#ifdef OF_PIC
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %l7
	call	add_pc







|

|
|







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
	call	objc_msg_lookup
	 mov	%i2, %o1

	jmpl	%o0, %g0
	 restore

0:
	call	OFMethodNotFound_stret
	 restore
.type OFForward_stret, %function
.size OFForward_stret, .-OFForward_stret

init:
	save	%sp, -96, %sp

#ifdef OF_PIC
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %l7
	call	add_pc

Modified src/forwarding/forwarding-sparc64-elf.S from [af08d696f6] to [5ae72e6aac].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.globl of_forward
.globl of_forward_stret

#define BIAS 2047

.section .text
of_forward:
	save	%sp, -304, %sp

	/*
	 * Save all floating point registers as they can be used for parameter
	 * passing.
	 */
	std	%f0, [%sp + BIAS + 176]

<
<
|

















|
|




|







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


 * 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 "platform.h"

.globl OFForward
.globl OFForward_stret

#define BIAS 2047

.section .text
OFForward:
	save	%sp, -304, %sp

	/*
	 * Save all floating point registers as they can be used for parameter
	 * passing.
	 */
	std	%f0, [%sp + BIAS + 176]
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
	ldd	[%sp + BIAS + 288], %f28
	ldd	[%sp + BIAS + 296], %f30

	jmpl	%o0, %g0
	 restore

0:
	call	of_method_not_found
	 restore
.type of_forward, %function
.size of_forward, .-of_forward

of_forward_stret:
	save	%sp, -304, %sp

	/*
	 * Save all floating point registers as they can be used for parameter
	 * passing.
	 */
	std	%f0, [%sp + BIAS + 176]







|

|
|

|







107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
	ldd	[%sp + BIAS + 288], %f28
	ldd	[%sp + BIAS + 296], %f30

	jmpl	%o0, %g0
	 restore

0:
	call	OFMethodNotFound
	 restore
.type OFForward, %function
.size OFForward, .-OFForward

OFForward_stret:
	save	%sp, -304, %sp

	/*
	 * Save all floating point registers as they can be used for parameter
	 * passing.
	 */
	std	%f0, [%sp + BIAS + 176]
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
	ldd	[%sp + BIAS + 288], %f28
	ldd	[%sp + BIAS + 296], %f30

	jmpl	%o0, %g0
	 restore

0:
	call	of_method_not_found_stret
	 restore
.type of_forward_stret, %function
.size of_forward_stret, .-of_forward_stret

init:
	save	%sp, -176, %sp

	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %l7
	call	add_pc
	 add	%l7, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %l7







|

|
|







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
	ldd	[%sp + BIAS + 288], %f28
	ldd	[%sp + BIAS + 296], %f30

	jmpl	%o0, %g0
	 restore

0:
	call	OFMethodNotFound_stret
	 restore
.type OFForward_stret, %function
.size OFForward_stret, .-OFForward_stret

init:
	save	%sp, -176, %sp

	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %l7
	call	add_pc
	 add	%l7, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %l7

Modified src/forwarding/forwarding-x86-elf.S from [294c10a12e] to [a55d3f40d7].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.intel_syntax noprefix

.globl of_forward
.globl of_forward_stret

.section .text
of_forward:
	push	ebp
	mov	ebp, esp

	push	ebx
	sub	esp, 20

	call	get_eip
	add	ebx, offset _GLOBAL_OFFSET_TABLE_

	mov	eax, [ebp+8]
	mov	[esp], eax
	call	object_getClass@PLT

	mov	[esp], eax
	lea	eax, [ebx+sel_forwardingTargetForSelector_@GOTOFF]
	mov	[esp+4], eax
	call	class_respondsToSelector@PLT

	test	eax, eax
	jz	short 0f

	mov	eax, [ebp+8]
	mov	[esp], eax
	lea	eax, [ebx+sel_forwardingTargetForSelector_@GOTOFF]
	mov	[esp+4], eax
	call	objc_msg_lookup@PLT

	mov	edx, [ebp+8]
	mov	[esp], edx
	lea	edx, [ebx+sel_forwardingTargetForSelector_@GOTOFF]




	mov	[esp+4], edx
	mov	edx, [ebp+12]
	mov	[esp+8], edx
	call	eax

	test	eax, eax
	jz	short 0f
	cmp	eax, [ebp+8]
	je	short 0f

	mov	[ebp+8], eax
	mov	[esp], eax
	mov	eax, [ebp+12]
	mov	[esp+4], eax
	call	objc_msg_lookup@PLT

	add	esp, 20
	pop	ebx
	pop	ebp



	jmp	eax

0:
	lea	eax, [ebx+of_method_not_found@GOTOFF]

	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	eax
.type of_forward, %function
.size of_forward, .-of_forward

of_forward_stret:
	push	ebp
	mov	ebp, esp

	push	ebx

	sub	esp, 20

	call	get_eip
	add	ebx, offset _GLOBAL_OFFSET_TABLE_

	mov	eax, [ebp+12]
	mov	[esp], eax
	call	object_getClass@PLT

	mov	[esp], eax
	lea	eax, [ebx+sel_forwardingTargetForSelector_@GOTOFF]
	mov	[esp+4], eax
	call	class_respondsToSelector@PLT

	test	eax, eax
	jz	short 0f

	mov	eax, [ebp+12]
	mov	[esp], eax
	lea	eax, [ebx+sel_forwardingTargetForSelector_@GOTOFF]
	mov	[esp+4], eax
	call	objc_msg_lookup@PLT

	mov	edx, [ebp+12]
	mov	[esp], edx
	lea	edx, [ebx+sel_forwardingTargetForSelector_@GOTOFF]
	mov	[esp+4], edx
	mov	edx, [ebp+16]
	mov	[esp+8], edx
	call	eax

	test	eax, eax
	jz	short 0f
	cmp	eax, [ebp+12]
	je	short 0f

	mov	[ebp+12], eax
	mov	[esp], eax
	mov	eax, [ebp+16]

	mov	[esp+4], eax


	call	objc_msg_lookup_stret@PLT

	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	eax

0:
	lea	eax, [ebx+of_method_not_found_stret@GOTOFF]

	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	eax
.type of_forward_stret, %function
.size of_forward_stret, .-of_forward_stret

init:
	push	ebp
	mov	ebp, esp

	push	ebx
	sub	esp, 4

	call	get_eip
	add	ebx, offset _GLOBAL_OFFSET_TABLE_

	lea	eax, [ebx+module@GOTOFF]
	mov	[esp], eax
	call	__objc_exec_class@PLT

	add	esp, 4
	pop	ebx
	pop	ebp
	ret

get_eip:
	mov	ebx, [esp]
	ret

#ifdef OF_SOLARIS
.section .init_array, "aw"
#else
.section .ctors, "aw", %progbits
#endif

<
<
|

















<
<
|
|


|
|
|

|
|


|

|
|


|
|
|


|
|

|
|
|
|


|
|
|
>
>
>
>
<
<
<
<

|
|
|
|

|
|
|
|


|
|
|
>
>

<
<

|

|
|
|

|
|
|

|
|
|

|
>
<


|

|
|


|
|
|


|
|

|
|
|
|


|
|
|
|
|
|
|

|
|
|
|
<
<
<
|
>
|
>
>


|
|
|

|


|

|
|
|

|
|
|


|
|

|
|


|

|
|


|
|
|



|







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


 * 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 "platform.h"



.globl OFForward
.globl OFForward_stret

.section .text
OFForward:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	subl	$20, %esp

	call	get_eip
	addl	$_GLOBAL_OFFSET_TABLE_, %ebx

	movl	8(%ebp), %eax
	movl	%eax, (%esp)
	call	object_getClass@PLT

	movl	%eax, (%esp)
	leal	sel_forwardingTargetForSelector_@GOTOFF(%ebx), %eax
	movl	%eax, 4(%esp)
	call	class_respondsToSelector@PLT

	testl	%eax, %eax
	jz	0f

	movl	8(%ebp), %eax
	movl	%eax, (%esp)
	leal	sel_forwardingTargetForSelector_@GOTOFF(%ebx), %eax
	movl	%eax, 4(%esp)
	call	objc_msg_lookup@PLT

	movl	8(%ebp), %edx
	movl	%edx, (%esp)
	leal	sel_forwardingTargetForSelector_@GOTOFF(%ebx), %edx
	movl	%edx, 4(%esp)
	movl	12(%ebp), %edx
	movl	%edx, 8(%esp)
	call	*%eax





	testl	%eax, %eax
	jz	0f
	cmpl	8(%ebp), %eax
	je	0f

	movl	%eax, 8(%ebp)
	movl	%eax, (%esp)
	movl	12(%ebp), %eax
	movl	%eax, 4(%esp)
	call	objc_msg_lookup@PLT

	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	*%eax



0:
	leal	OFMethodNotFound@GOTOFF(%ebx), %eax

	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	*%eax
.type OFForward, %function
.size OFForward, .-OFForward

OFForward_stret:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	subl	$20, %esp


	call	get_eip
	addl	$_GLOBAL_OFFSET_TABLE_, %ebx

	movl	12(%ebp), %eax
	movl	%eax, (%esp)
	call	object_getClass@PLT

	movl	%eax, (%esp)
	leal	sel_forwardingTargetForSelector_@GOTOFF(%ebx), %eax
	movl	%eax, 4(%esp)
	call	class_respondsToSelector@PLT

	testl	%eax, %eax
	jz	0f

	movl	12(%ebp), %eax
	movl	%eax, (%esp)
	leal	sel_forwardingTargetForSelector_@GOTOFF(%ebx), %eax
	movl	%eax, 4(%esp)
	call	objc_msg_lookup@PLT

	movl	12(%ebp), %edx
	movl	%edx, (%esp)
	leal	sel_forwardingTargetForSelector_@GOTOFF(%ebx), %edx
	movl	%edx, 4(%esp)
	movl	16(%ebp), %edx
	movl	%edx, 8(%esp)
	call	*%eax

	testl	%eax, %eax
	jz	0f
	cmpl	12(%ebp), %eax
	je	0f




	movl	%eax, 12(%ebp)
	movl	%eax, (%esp)
	movl	16(%ebp), %eax
	movl	%eax, 4(%esp)
	call	objc_msg_lookup_stret@PLT

	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	*%eax

0:
	leal	OFMethodNotFound_stret@GOTOFF(%ebx), %eax

	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	*%eax
.type OFForward_stret, %function
.size OFForward_stret, .-OFForward_stret

init:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	subl	$4, %esp

	call	get_eip
	addl	$_GLOBAL_OFFSET_TABLE_, %ebx

	leal	module@GOTOFF(%ebx), %eax
	movl	%eax, (%esp)
	call	__objc_exec_class@PLT

	addl	$4, %esp
	popl	%ebx
	popl	%ebp
	ret

get_eip:
	movl	(%esp), %ebx
	ret

#ifdef OF_SOLARIS
.section .init_array, "aw"
#else
.section .ctors, "aw", %progbits
#endif

Modified src/forwarding/forwarding-x86-win32.S from [38023e4835] to [7c90db9737].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.intel_syntax noprefix

.globl _of_forward
.globl _of_forward_stret

.section .text
_of_forward:
	push	ebp
	mov	ebp, esp

	push	ebx
	sub	esp, 20

	mov	eax, [ebp+8]
	mov	[esp], eax
	call	_object_getClass

	mov	[esp], eax
	mov	eax, offset sel_forwardingTargetForSelector_
	mov	[esp+4], eax
	call	_class_respondsToSelector

	test	eax, eax
	jz	short 0f

	mov	eax, [ebp+8]
	mov	[esp], eax
	mov	eax, offset sel_forwardingTargetForSelector_
	mov	[esp+4], eax
	call	_objc_msg_lookup

	mov	edx, [ebp+8]
	mov	[esp], edx
	mov	edx, offset sel_forwardingTargetForSelector_
	mov	[esp+4], edx
	mov	edx, [ebp+12]
	mov	[esp+8], edx
	call	eax

	test	eax, eax
	jz	short 0f
	cmp	eax, [ebp+8]
	je	short 0f

	mov	[ebp+8], eax
	mov	[esp], eax
	mov	eax, [ebp+12]
	mov	[esp+4], eax
	call	_objc_msg_lookup

	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	eax

0:
	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	_of_method_not_found





_of_forward_stret:
	push	ebp

	mov	ebp, esp


	push	ebx
	sub	esp, 20

	mov	eax, [ebp+12]
	mov	[esp], eax
	call	_object_getClass

	mov	[esp], eax
	mov	eax, offset sel_forwardingTargetForSelector_
	mov	[esp+4], eax
	call	_class_respondsToSelector

	test	eax, eax
	jz	short 0f

	mov	eax, [ebp+12]
	mov	[esp], eax
	mov	eax, offset sel_forwardingTargetForSelector_
	mov	[esp+4], eax
	call	_objc_msg_lookup

	mov	edx, [ebp+12]
	mov	[esp], edx
	mov	edx, offset sel_forwardingTargetForSelector_
	mov	[esp+4], edx
	mov	edx, [ebp+16]
	mov	[esp+8], edx
	call	eax

	test	eax, eax
	jz	short 0f
	cmp	eax, [ebp+12]
	je	short 0f




	mov	[ebp+12], eax
	mov	[esp], eax
	mov	eax, [ebp+16]
	mov	[esp+4], eax
	call	_objc_msg_lookup_stret

	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	eax

0:
	add	esp, 20
	pop	ebx
	pop	ebp

	jmp	_of_method_not_found_stret





init:
	push	ebp
	mov	ebp, esp

	push	ebx
	sub	esp, 4

	mov	eax, offset module
	mov	[esp], eax
	call	___objc_exec_class

	add	esp, 4
	pop	ebx
	pop	ebp
	ret

.section .ctors, "aw"
	.long init

.section .rodata
str_forwardingTargetForSelector_:

<
<
|















<
<
|
|


|
|
|

|
|

|
|


|
|
|


|
|

|
|
|
|


|
|
|
|
|
|
|

|
|
|
|

|
|
|
|


|
|
|

|


|
|
|

|
>
>
>
>

|
|
>
|
>
|
<
<

|
|


|
|
|


|
|

|
|
|
|


|
|
|
|
|
|
|

|
|
|
|
>
>
>
|
<
<
|
<


|
|
|

|


|
|
|

|
>
>
>
>


|
|

|
|

|
|


|
|
|







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


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



.globl _OFForward
.globl _OFForward_stret

.section .text
_OFForward:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	subl	$20, %esp

	movl	8(%ebp), %eax
	movl	%eax, (%esp)
	call	_object_getClass

	movl	%eax, (%esp)
	movl	$sel_forwardingTargetForSelector_, %eax
	movl	%eax, 4(%esp)
	call	_class_respondsToSelector

	testl	%eax, %eax
	jz	0f

	movl	8(%ebp), %eax
	movl	%eax, (%esp)
	movl	$sel_forwardingTargetForSelector_, %eax
	movl	%eax, 4(%esp)
	call	_objc_msg_lookup

	movl	8(%ebp), %edx
	movl	%edx, (%esp)
	movl	$sel_forwardingTargetForSelector_, %edx
	movl	%edx, 4(%esp)
	movl	12(%ebp), %edx
	movl	%edx, 8(%esp)
	call	*%eax

	testl	%eax, %eax
	jz	0f
	cmpl	8(%ebp), %eax
	je	0f

	movl	%eax, 8(%ebp)
	movl	%eax, (%esp)
	movl	12(%ebp), %eax
	movl	%eax, 4(%esp)
	call	_objc_msg_lookup

	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	*%eax

0:
	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	_OFMethodNotFound
.def _OFForward
.scl 2
.type 32
.endef

_OFForward_stret:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	subl	$20, %esp



	movl	12(%ebp), %eax
	movl	%eax, (%esp)
	call	_object_getClass

	movl	%eax, (%esp)
	movl	$sel_forwardingTargetForSelector_, %eax
	movl	%eax, 4(%esp)
	call	_class_respondsToSelector

	testl	%eax, %eax
	jz	0f

	movl	12(%ebp), %eax
	movl	%eax, (%esp)
	movl	$sel_forwardingTargetForSelector_, %eax
	movl	%eax, 4(%esp)
	call	_objc_msg_lookup

	movl	12(%ebp), %edx
	movl	%edx, (%esp)
	movl	$sel_forwardingTargetForSelector_, %edx
	movl	%edx, 4(%esp)
	movl	16(%ebp), %edx
	movl	%edx, 8(%esp)
	call	*%eax

	testl	%eax, %eax
	jz	0f
	cmpl	12(%ebp), %eax
	je	0f

	movl	%eax, 12(%ebp)
	movl	%eax, (%esp)
	movl	16(%ebp), %eax


	movl	%eax, 4(%esp)

	call	_objc_msg_lookup_stret

	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	*%eax

0:
	addl	$20, %esp
	popl	%ebx
	popl	%ebp

	jmp	_OFMethodNotFound_stret
.def _OFForward_stret
.scl 2
.type 32
.endef

init:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	subl	$4, %esp

	movl	$module, %eax
	movl	%eax, (%esp)
	call	___objc_exec_class

	addl	$4, %esp
	popl	%ebx
	popl	%ebp
	ret

.section .ctors, "aw"
	.long init

.section .rodata
str_forwardingTargetForSelector_:

Modified src/forwarding/forwarding-x86_64-elf.S from [1d1fc641cf] to [9ea0cb41b3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.intel_syntax noprefix

.globl of_forward
.globl of_forward_stret

.section .text
of_forward:
	push	rbp
	mov	rbp, rsp

	/* Save all arguments */
	sub	rsp, 0xC0	/* 16-byte alignment */
	mov	[rbp-0x08], rax
	mov	[rbp-0x10], rdi
	mov	[rbp-0x18], rsi
	mov	[rbp-0x20], rdx
	mov	[rbp-0x28], rcx
	mov	[rbp-0x30], r8
	mov	[rbp-0x38], r9
	movaps	[rbp-0x50], xmm0
	movaps	[rbp-0x60], xmm1
	movaps	[rbp-0x70], xmm2

	movaps	[rbp-0x80], xmm3
	movaps	[rbp-0x90], xmm4
	movaps	[rbp-0xA0], xmm5
	movaps	[rbp-0xB0], xmm6
	movaps	[rbp-0xC0], xmm7

	call	object_getClass@PLT

	mov	rdi, rax
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	call	class_respondsToSelector@PLT

	test	rax, rax
	jz	short 0f

	mov	rdi, [rbp-0x10]
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	call	objc_msg_lookup@PLT

	mov	rdi, [rbp-0x10]
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	mov	rdx, [rbp-0x18]
	call	rax

	test	rax, rax
	jz	short 0f
	cmp	rax, [rbp-0x10]
	je	short 0f

	mov	[rbp-0x10], rax

	mov	rdi, rax
	mov	rsi, [rbp-0x18]
	call	objc_msg_lookup@PLT
	mov	r11, rax

	/* Restore all arguments */
	movaps	xmm7, [rbp-0xC0]
	movaps	xmm6, [rbp-0xB0]
	movaps	xmm5, [rbp-0xA0]
	movaps	xmm4, [rbp-0x90]
	movaps	xmm3, [rbp-0x80]
	movaps	xmm2, [rbp-0x70]
	movaps	xmm1, [rbp-0x60]
	movaps	xmm0, [rbp-0x50]
	mov	r9,   [rbp-0x38]
	mov	r8,   [rbp-0x30]
	mov	rcx,  [rbp-0x28]
	mov	rdx,  [rbp-0x20]
	mov	rsi,  [rbp-0x18]
	mov	rdi,  [rbp-0x10]
	mov	rax,  [rbp-0x08]

	mov	rsp, rbp
	pop	rbp

	jmp	r11

0:
	mov	rdi, [rbp-0x10]
	mov	rsi, [rbp-0x18]

	mov	rsp, rbp
	pop	rbp

	jmp	of_method_not_found@PLT
.type of_forward, %function
.size of_forward, .-of_forward

of_forward_stret:
	push	rbp
	mov	rbp, rsp

	/* Save all arguments */
	sub	rsp, 0xC0	/* 16-byte alignment */
	mov	[rbp-0x08], rax
	mov	[rbp-0x10], rdi
	mov	[rbp-0x18], rsi
	mov	[rbp-0x20], rdx
	mov	[rbp-0x28], rcx
	mov	[rbp-0x30], r8
	mov	[rbp-0x38], r9
	movaps	[rbp-0x50], xmm0
	movaps	[rbp-0x60], xmm1
	movaps	[rbp-0x70], xmm2
	movaps	[rbp-0x80], xmm3
	movaps	[rbp-0x90], xmm4
	movaps	[rbp-0xA0], xmm5
	movaps	[rbp-0xB0], xmm6
	movaps	[rbp-0xC0], xmm7

	mov	rdi, rsi
	call	object_getClass@PLT

	mov	rdi, rax
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	call	class_respondsToSelector@PLT

	test	rax, rax
	jz	short 0f

	mov	rdi, [rbp-0x18]
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	call	objc_msg_lookup@PLT

	mov	rdi, [rbp-0x18]
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	mov	rdx, [rbp-0x20]
	call	rax

	test	rax, rax
	jz	short 0f
	cmp	rax, [rbp-0x18]
	je	short 0f

	mov	[rbp-0x18], rax

	mov	rdi, rax
	mov	rsi, [rbp-0x20]
	call	objc_msg_lookup_stret@PLT
	mov	r11, rax

	/* Restore all arguments */
	movaps	xmm7, [rbp-0xC0]
	movaps	xmm6, [rbp-0xB0]
	movaps	xmm5, [rbp-0xA0]
	movaps	xmm4, [rbp-0x90]
	movaps	xmm3, [rbp-0x80]
	movaps	xmm2, [rbp-0x70]
	movaps	xmm1, [rbp-0x60]
	movaps	xmm0, [rbp-0x50]
	mov	r9,   [rbp-0x38]
	mov	r8,   [rbp-0x30]
	mov	rcx,  [rbp-0x28]
	mov	rdx,  [rbp-0x20]
	mov	rsi,  [rbp-0x18]
	mov	rdi,  [rbp-0x10]
	mov	rax,  [rbp-0x08]




	mov	rsp, rbp
	pop	rbp

	jmp	r11

0:
	mov	rdi, [rbp-0x10]
	mov	rsi, [rbp-0x18]
	mov	rdx, [rbp-0x20]

	mov	rsp, rbp
	pop	rbp

	jmp	of_method_not_found_stret@PLT
.type of_forward_stret, %function
.size of_forward_stret, .-of_forward_stret

init:
	lea	rdi, [rip+module]
	jmp	__objc_exec_class@PLT

#ifdef OF_SOLARIS
.section .init_array, "aw"
#else
.section .ctors, "aw", %progbits
#endif

<
<
|

















<
<
|
|


|
|
|


|
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
<



|
|


|
|

|
|


|
|
|
|

|
|
|
|

|

|
|

|


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

|
|

|


|
|

|
|

|
|
|

|
|
|


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

|


|
|


|
|

|
|


|
|
|
|

|
|
|
|

|

|
|

|


|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
>
>
>

|
|

|


|
|
|

|
|

|
|
|


|







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


 * 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 "platform.h"



.globl OFForward
.globl OFForward_stret

.section .text
OFForward:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0xC0, %rsp	/* 16-byte alignment */
	movq	%rax, -0x8(%rbp)
	movq	%rdi, -0x10(%rbp)
	movq	%rsi, -0x18(%rbp)
	movq	%rdx, -0x20(%rbp)
	movq	%rcx, -0x28(%rbp)
	movq	%r8, -0x30(%rbp)
	movq	%r9, -0x38(%rbp)
	movaps	%xmm0, -0x50(%rbp)
	movaps	%xmm1, -0x60(%rbp)
	movaps	%xmm2, -0x70(%rbp)
	movaps	%xmm3, -0x80(%rbp)
	movaps	%xmm4, -0x90(%rbp)
	movaps	%xmm5, -0xA0(%rbp)
	movaps	%xmm6, -0xB0(%rbp)
	movaps	%xmm7, -0xC0(%rbp)


	call	object_getClass@PLT

	movq	%rax, %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	class_respondsToSelector@PLT

	testq	%rax, %rax
	jz	0f

	movq	-0x10(%rbp), %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	objc_msg_lookup@PLT

	movq	-0x10(%rbp), %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	movq	-0x18(%rbp), %rdx
	call	*%rax

	testq	%rax, %rax
	jz	0f
	cmpq	-0x10(%rbp), %rax
	je	0f

	movq	%rax, -0x10(%rbp)

	movq	%rax, %rdi
	movq	-0x18(%rbp), %rsi
	call	objc_msg_lookup@PLT
	movq	%rax, %r11

	/* Restore all arguments */
	movaps	-0xC0(%rbp), %xmm7
	movaps	-0xB0(%rbp), %xmm6
	movaps	-0xA0(%rbp), %xmm5
	movaps	-0x90(%rbp), %xmm4
	movaps	-0x80(%rbp), %xmm3
	movaps	-0x70(%rbp), %xmm2
	movaps	-0x60(%rbp), %xmm1
	movaps	-0x50(%rbp), %xmm0
	movq	-0x38(%rbp), %r9
	movq	-0x30(%rbp), %r8
	movq	-0x28(%rbp), %rcx
	movq	-0x20(%rbp), %rdx
	movq	-0x18(%rbp), %rsi
	movq	-0x10(%rbp), %rdi
	movq	-0x8(%rbp), %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmpq	*%r11

0:
	movq	-0x10(%rbp), %rdi
	movq	-0x18(%rbp), %rsi

	movq	%rbp, %rsp
	popq	%rbp

	jmp	OFMethodNotFound@PLT
.type OFForward, %function
.size OFForward, .-OFForward

OFForward_stret:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0xC0, %rsp	/* 16-byte alignment */
	movq	%rax, -0x8(%rbp)
	movq	%rdi, -0x10(%rbp)
	movq	%rsi, -0x18(%rbp)
	movq	%rdx, -0x20(%rbp)
	movq	%rcx, -0x28(%rbp)
	movq	%r8, -0x30(%rbp)
	movq	%r9, -0x38(%rbp)
	movaps	%xmm0, -0x50(%rbp)
	movaps	%xmm1, -0x60(%rbp)
	movaps	%xmm2, -0x70(%rbp)
	movaps	%xmm3, -0x80(%rbp)
	movaps	%xmm4, -0x90(%rbp)
	movaps	%xmm5, -0xA0(%rbp)
	movaps	%xmm6, -0xB0(%rbp)
	movaps	%xmm7, -0xC0(%rbp)

	movq	%rsi, %rdi
	call	object_getClass@PLT

	movq	%rax, %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	class_respondsToSelector@PLT

	testq	%rax, %rax
	jz	0f

	movq	-0x18(%rbp), %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	objc_msg_lookup@PLT

	movq	-0x18(%rbp), %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	movq	-0x20(%rbp), %rdx
	call	*%rax

	testq	%rax, %rax
	jz	0f
	cmpq	-0x18(%rbp), %rax
	je	0f

	movq	%rax, -0x18(%rbp)

	movq	%rax, %rdi
	movq	-0x20(%rbp), %rsi
	call	objc_msg_lookup_stret@PLT
	movq	%rax, %r11

	/* Restore all arguments */
	movaps	-0xC0(%rbp), %xmm7
	movaps	-0xB0(%rbp), %xmm6
	movaps	-0xA0(%rbp), %xmm5
	movaps	-0x90(%rbp), %xmm4
	movaps	-0x80(%rbp), %xmm3
	movaps	-0x70(%rbp), %xmm2
	movaps	-0x60(%rbp), %xmm1
	movaps	-0x50(%rbp), %xmm0
	movq	-0x38(%rbp), %r9
	movq	-0x30(%rbp), %r8
	movq	-0x28(%rbp), %rcx
	movq	-0x20(%rbp), %rdx



	movq	-0x18(%rbp), %rsi
	movq	-0x10(%rbp), %rdi
	movq	-0x8(%rbp), %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmpq	*%r11

0:
	movq	-0x10(%rbp), %rdi
	movq	-0x18(%rbp), %rsi
	movq	-0x20(%rbp), %rdx

	movq	%rbp, %rsp
	popq	%rbp

	jmp	OFMethodNotFound_stret@PLT
.type OFForward_stret, %function
.size OFForward_stret, .-OFForward_stret

init:
	leaq	module(%rip), %rdi
	jmp	__objc_exec_class@PLT

#ifdef OF_SOLARIS
.section .init_array, "aw"
#else
.section .ctors, "aw", %progbits
#endif

Modified src/forwarding/forwarding-x86_64-macho.S from [4631e2d50d] to [0e975fcfd2].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.intel_syntax noprefix

.globl _of_forward
.globl _of_forward_stret

.section __TEXT, __text, regular, pure_instructions
_of_forward:
	push	rbp
	mov	rbp, rsp

	/* Save all arguments */
	sub	rsp, 0xC0	/* 16-byte alignment */
	mov	[rbp-0x08], rax
	mov	[rbp-0x10], rdi
	mov	[rbp-0x18], rsi
	mov	[rbp-0x20], rdx
	mov	[rbp-0x28], rcx
	mov	[rbp-0x30], r8
	mov	[rbp-0x38], r9
	movaps	[rbp-0x50], xmm0
	movaps	[rbp-0x60], xmm1
	movaps	[rbp-0x70], xmm2

	movaps	[rbp-0x80], xmm3
	movaps	[rbp-0x90], xmm4
	movaps	[rbp-0xA0], xmm5
	movaps	[rbp-0xB0], xmm6
	movaps	[rbp-0xC0], xmm7

	call	_object_getClass

	mov	rdi, rax
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	call	_class_respondsToSelector

	test	rax, rax
	jz	0f

	mov	rdi, [rbp-0x10]
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	call	_objc_msg_lookup

	mov	rdi, [rbp-0x10]
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	mov	rdx, [rbp-0x18]
	call	rax

	test	rax, rax
	jz	0f
	cmp	rax, [rbp-0x10]
	je	0f

	mov	[rbp-0x10], rax

	mov	rdi, rax
	mov	rsi, [rbp-0x18]
	call	_objc_msg_lookup
	mov	r11, rax

	/* Restore all arguments */
	movaps	xmm7, [rbp-0xC0]
	movaps	xmm6, [rbp-0xB0]
	movaps	xmm5, [rbp-0xA0]
	movaps	xmm4, [rbp-0x90]
	movaps	xmm3, [rbp-0x80]
	movaps	xmm2, [rbp-0x70]
	movaps	xmm1, [rbp-0x60]
	movaps	xmm0, [rbp-0x50]
	mov	r9,   [rbp-0x38]
	mov	r8,   [rbp-0x30]
	mov	rcx,  [rbp-0x28]
	mov	rdx,  [rbp-0x20]
	mov	rsi,  [rbp-0x18]
	mov	rdi,  [rbp-0x10]
	mov	rax,  [rbp-0x08]

	mov	rsp, rbp
	pop	rbp

	jmp	r11

0:
	mov	rdi, [rbp-0x10]
	mov	rsi, [rbp-0x18]

	mov	rsp, rbp
	pop	rbp

	jmp	_of_method_not_found

_of_forward_stret:
	push	rbp
	mov	rbp, rsp

	/* Save all arguments */
	sub	rsp, 0xC0	/* 16-byte alignment */
	mov	[rbp-0x08], rax
	mov	[rbp-0x10], rdi
	mov	[rbp-0x18], rsi
	mov	[rbp-0x20], rdx
	mov	[rbp-0x28], rcx
	mov	[rbp-0x30], r8
	mov	[rbp-0x38], r9
	movaps	[rbp-0x50], xmm0
	movaps	[rbp-0x60], xmm1
	movaps	[rbp-0x70], xmm2
	movaps	[rbp-0x80], xmm3
	movaps	[rbp-0x90], xmm4
	movaps	[rbp-0xA0], xmm5
	movaps	[rbp-0xB0], xmm6
	movaps	[rbp-0xC0], xmm7

	mov	rdi, rsi

	call	_object_getClass

	mov	rdi, rax
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	call	_class_respondsToSelector

	test	rax, rax
	jz	0f

	mov	rdi, [rbp-0x18]
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	call	_objc_msg_lookup

	mov	rdi, [rbp-0x18]
	lea	rsi, [rip+sel_forwardingTargetForSelector_]
	mov	rdx, [rbp-0x20]
	call	rax

	test	rax, rax
	jz	0f
	cmp	rax, [rbp-0x18]
	je	0f



	mov	[rbp-0x18], rax

	mov	rdi, rax
	mov	rsi, [rbp-0x20]
	call	_objc_msg_lookup_stret
	mov	r11, rax

	/* Restore all arguments */
	movaps	xmm7, [rbp-0xC0]
	movaps	xmm6, [rbp-0xB0]
	movaps	xmm5, [rbp-0xA0]
	movaps	xmm4, [rbp-0x90]
	movaps	xmm3, [rbp-0x80]
	movaps	xmm2, [rbp-0x70]
	movaps	xmm1, [rbp-0x60]
	movaps	xmm0, [rbp-0x50]
	mov	r9,   [rbp-0x38]
	mov	r8,   [rbp-0x30]
	mov	rcx,  [rbp-0x28]
	mov	rdx,  [rbp-0x20]
	mov	rsi,  [rbp-0x18]
	mov	rdi,  [rbp-0x10]
	mov	rax,  [rbp-0x08]





	mov	rsp, rbp
	pop	rbp

	jmp	r11

0:
	mov	rdi, [rbp-0x10]
	mov	rsi, [rbp-0x18]
	mov	rdx, [rbp-0x20]

	mov	rsp, rbp
	pop	rbp

	jmp	_of_method_not_found_stret

init:
	lea	rdi, [rip+module]
	jmp	___objc_exec_class

.section __DATA, __mod_init_func, mod_init_funcs
	.quad init

.section __TEXT, __cstring, cstring_literals
str_forwardingTargetForSelector_:

<
<
|

















<
<
|
|


|
|
|


|
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
<



|
|


|


|
|


|
|
|
|

|

|


|

|
|

|


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

|
|

|


|
|

|
|

|

|
|
|


|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
|
>


|
|


|


|
|


|
|
|
|

|

<
<
>
>

|

|
|

|


|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
>
>
>
>

|
|

|


|
|
|

|
|

|


|







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 "platform.h"



.globl _OFForward
.globl _OFForward_stret

.section __TEXT, __text, regular, pure_instructions
_OFForward:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0xC0, %rsp	/* 16-byte alignment */
	movq	%rax, -0x8(%rbp)
	movq	%rdi, -0x10(%rbp)
	movq	%rsi, -0x18(%rbp)
	movq	%rdx, -0x20(%rbp)
	movq	%rcx, -0x28(%rbp)
	movq	%r8, -0x30(%rbp)
	movq	%r9, -0x38(%rbp)
	movaps	%xmm0, -0x50(%rbp)
	movaps	%xmm1, -0x60(%rbp)
	movaps	%xmm2, -0x70(%rbp)
	movaps	%xmm3, -0x80(%rbp)
	movaps	%xmm4, -0x90(%rbp)
	movaps	%xmm5, -0xA0(%rbp)
	movaps	%xmm6, -0xB0(%rbp)
	movaps	%xmm7, -0xC0(%rbp)


	call	_object_getClass

	movq	%rax, %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	_class_respondsToSelector

	testq	%rax, %rax
	jz	0f

	movq	-0x10(%rbp), %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	_objc_msg_lookup

	movq	-0x10(%rbp), %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	movq	-0x18(%rbp), %rdx
	call	*%rax

	testq	%rax, %rax
	jz	0f
	cmpq	-0x10(%rbp), %rax
	je	0f

	movq	%rax, -0x10(%rbp)

	movq	%rax, %rdi
	movq	-0x18(%rbp), %rsi
	call	_objc_msg_lookup
	movq	%rax, %r11

	/* Restore all arguments */
	movaps	-0xC0(%rbp), %xmm7
	movaps	-0xB0(%rbp), %xmm6
	movaps	-0xA0(%rbp), %xmm5
	movaps	-0x90(%rbp), %xmm4
	movaps	-0x80(%rbp), %xmm3
	movaps	-0x70(%rbp), %xmm2
	movaps	-0x60(%rbp), %xmm1
	movaps	-0x50(%rbp), %xmm0
	movq	-0x38(%rbp), %r9
	movq	-0x30(%rbp), %r8
	movq	-0x28(%rbp), %rcx
	movq	-0x20(%rbp), %rdx
	movq	-0x18(%rbp), %rsi
	movq	-0x10(%rbp), %rdi
	movq	-0x8(%rbp), %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmpq	*%r11

0:
	movq	-0x10(%rbp), %rdi
	movq	-0x18(%rbp), %rsi

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_OFMethodNotFound

_OFForward_stret:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0xC0, %rsp	/* 16-byte alignment */
	movq	%rax, -0x8(%rbp)
	movq	%rdi, -0x10(%rbp)
	movq	%rsi, -0x18(%rbp)
	movq	%rdx, -0x20(%rbp)
	movq	%rcx, -0x28(%rbp)
	movq	%r8, -0x30(%rbp)
	movq	%r9, -0x38(%rbp)
	movaps	%xmm0, -0x50(%rbp)
	movaps	%xmm1, -0x60(%rbp)
	movaps	%xmm2, -0x70(%rbp)
	movaps	%xmm3, -0x80(%rbp)
	movaps	%xmm4, -0x90(%rbp)
	movaps	%xmm5, -0xA0(%rbp)
	movaps	%xmm6, -0xB0(%rbp)

	movaps	%xmm7, -0xC0(%rbp)

	movq	%rsi, %rdi
	call	_object_getClass

	movq	%rax, %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	_class_respondsToSelector

	testq	%rax, %rax
	jz	0f

	movq	-0x18(%rbp), %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	call	_objc_msg_lookup

	movq	-0x18(%rbp), %rdi
	leaq	sel_forwardingTargetForSelector_(%rip), %rsi
	movq	-0x20(%rbp), %rdx
	call	*%rax

	testq	%rax, %rax
	jz	0f


	cmpq	-0x18(%rbp), %rax
	je	0f

	movq	%rax, -0x18(%rbp)

	movq	%rax, %rdi
	movq	-0x20(%rbp), %rsi
	call	_objc_msg_lookup_stret
	movq	%rax, %r11

	/* Restore all arguments */
	movaps	-0xC0(%rbp), %xmm7
	movaps	-0xB0(%rbp), %xmm6
	movaps	-0xA0(%rbp), %xmm5
	movaps	-0x90(%rbp), %xmm4
	movaps	-0x80(%rbp), %xmm3
	movaps	-0x70(%rbp), %xmm2
	movaps	-0x60(%rbp), %xmm1
	movaps	-0x50(%rbp), %xmm0
	movq	-0x38(%rbp), %r9
	movq	-0x30(%rbp), %r8
	movq	-0x28(%rbp), %rcx




	movq	-0x20(%rbp), %rdx
	movq	-0x18(%rbp), %rsi
	movq	-0x10(%rbp), %rdi
	movq	-0x8(%rbp), %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmpq	*%r11

0:
	movq	-0x10(%rbp), %rdi
	movq	-0x18(%rbp), %rsi
	movq	-0x20(%rbp), %rdx

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_OFMethodNotFound_stret

init:
	leaq	module(%rip), %rdi
	jmp	___objc_exec_class

.section __DATA, __mod_init_func, mod_init_funcs
	.quad init

.section __TEXT, __cstring, cstring_literals
str_forwardingTargetForSelector_:

Modified src/forwarding/forwarding-x86_64-win64.S from [b1b62864b4] to [74c1db404c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.intel_syntax noprefix

.globl of_forward
.globl of_forward_stret

.section .text
of_forward:
	push	rbp
	mov	rbp, rsp

	/* Save all arguments */
	sub	rsp, 0x90	/* 16-byte alignment */
	mov	[rbp-0x28], rax
	mov	[rbp-0x30], rcx
	mov	[rbp-0x38], rdx
	mov	[rbp-0x40], r8
	mov	[rbp-0x48], r9
	movaps	[rbp-0x60], xmm0
	movaps	[rbp-0x70], xmm1
	movaps	[rbp-0x80], xmm2
	movaps	[rbp-0x90], xmm3

	call	object_getClass

	mov	rcx, rax
	mov	rdx, offset sel_forwardingTargetForSelector_
	call	class_respondsToSelector

	test	rax, rax
	jz	short 0f

	mov	rcx, [rbp-0x30]
	mov	rdx, offset sel_forwardingTargetForSelector_
	call	objc_msg_lookup

	mov	rcx, [rbp-0x30]
	mov	rdx, offset sel_forwardingTargetForSelector_
	mov	r8,  [rbp-0x38]
	call	rax



	test	rax, rax
	jz	short 0f
	cmp	rax, [rbp-0x30]
	je	short 0f

	mov	[rbp-0x30], rax

	mov	rcx, rax
	mov	rdx, [rbp-0x38]
	call	objc_msg_lookup
	mov	r11, rax

	/* Restore all arguments */
	movaps	xmm3, [rbp-0x90]
	movaps	xmm2, [rbp-0x80]
	movaps	xmm1, [rbp-0x70]
	movaps	xmm0, [rbp-0x60]
	mov	r9,   [rbp-0x48]
	mov	r8,   [rbp-0x40]
	mov	rdx,  [rbp-0x38]
	mov	rcx,  [rbp-0x30]
	mov	rax,  [rbp-0x28]


	mov	rsp, rbp
	pop	rbp

	jmp	r11

0:
	mov	rcx, [rbp-0x30]
	mov	rdx, [rbp-0x38]

	mov	rsp, rbp
	pop	rbp

	jmp	of_method_not_found





of_forward_stret:
	push	rbp
	mov	rbp, rsp

	/* Save all arguments */
	sub	rsp, 0x90	/* 16-byte alignment */
	mov	[rbp-0x28], rax
	mov	[rbp-0x30], rcx
	mov	[rbp-0x38], rdx
	mov	[rbp-0x40], r8
	mov	[rbp-0x48], r9
	movaps	[rbp-0x60], xmm0
	movaps	[rbp-0x70], xmm1
	movaps	[rbp-0x80], xmm2
	movaps	[rbp-0x90], xmm3

	mov	rcx, rdx
	call	object_getClass


	mov	rcx, rax
	mov	rdx, offset sel_forwardingTargetForSelector_
	call	class_respondsToSelector

	test	rax, rax
	jz	short 0f

	mov	rcx, [rbp-0x38]
	mov	rdx, offset sel_forwardingTargetForSelector_
	call	objc_msg_lookup

	mov	rcx, [rbp-0x38]
	mov	rdx, offset sel_forwardingTargetForSelector_
	mov	r8,  [rbp-0x40]
	call	rax

	test	rax, rax
	jz	short 0f
	cmp	rax, [rbp-0x38]
	je	short 0f

	mov	[rbp-0x38], rax

	mov	rcx, rax
	mov	rdx, [rbp-0x40]
	call	objc_msg_lookup_stret
	mov	r11, rax

	/* Restore all arguments */
	movaps	xmm3, [rbp-0x90]
	movaps	xmm2, [rbp-0x80]
	movaps	xmm1, [rbp-0x70]
	movaps	xmm0, [rbp-0x60]
	mov	r9,   [rbp-0x48]
	mov	r8,   [rbp-0x40]
	mov	rdx,  [rbp-0x38]
	mov	rcx,  [rbp-0x30]

	mov	rax,  [rbp-0x28]


	mov	rsp, rbp
	pop	rbp

	jmp	r11

0:
	mov	rcx, [rbp-0x30]
	mov	rdx, [rbp-0x38]
	mov	r8,  [rbp-0x40]

	mov	rsp, rbp
	pop	rbp

	jmp	of_method_not_found_stret





init:
	mov	rcx, offset module
	jmp	__objc_exec_class

.section .ctors, "aw"
	.quad init

.section .rodata
str_forwardingTargetForSelector_:

<
<
|















<
<
|
|


|
|
|


|
|
|
|
|
|
|
|
|
|



|
|


|
|

|
|


|
|
|
|
>
>
<
<
|
|
|

|

|
|

|


|
|
|
|
|
|
|
|
<
>

|
|

|


|
|

|
|

|
>
>
>
>

|
|
|


|
|
|
|
|
|
|
|
|
|

|

>
|
<
|


|
|

|
|


|
|
|
|

|
|
|
|

|

|
|

|


|
|
|
|
|
|
|
|
>
|
>
<
<
|

|


|
|
|

|
|

|
>
>
>
>


|







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


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



.globl OFForward
.globl OFForward_stret

.section .text
OFForward:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0x90, %rsp	/* 16-byte alignment */
	movq	%rax, -0x28(%rbp)
	movq	%rcx, -0x30(%rbp)
	movq	%rdx, -0x38(%rbp)
	movq	%r8, -0x40(%rbp)
	movq	%r9, -0x48(%rbp)
	movaps	%xmm0, -0x60(%rbp)
	movaps	%xmm1, -0x70(%rbp)
	movaps	%xmm2, -0x80(%rbp)
	movaps	%xmm3, -0x90(%rbp)

	call	object_getClass

	movq	%rax, %rcx
	leaq	sel_forwardingTargetForSelector_(%rip), %rdx
	call	class_respondsToSelector

	testq	%rax, %rax
	jz	0f

	movq	-0x30(%rbp), %rcx
	leaq	sel_forwardingTargetForSelector_(%rip), %rdx
	call	objc_msg_lookup

	movq	-0x30(%rbp), %rcx
	leaq	sel_forwardingTargetForSelector_(%rip), %rdx
	movq	-0x38(%rbp), %r8
	call	*%rax

	testq	%rax, %rax


	jz	0f
	cmpq	-0x30(%rbp), %rax
	je	0f

	movq	%rax, -0x30(%rbp)

	movq	%rax, %rcx
	movq	-0x38(%rbp), %rdx
	call	objc_msg_lookup
	movq	%rax, %r11

	/* Restore all arguments */
	movaps	-0x90(%rbp), %xmm3
	movaps	-0x80(%rbp), %xmm2
	movaps	-0x70(%rbp), %xmm1
	movaps	-0x60(%rbp), %xmm0
	movq	-0x48(%rbp), %r9
	movq	-0x40(%rbp), %r8
	movq	-0x38(%rbp), %rdx
	movq	-0x30(%rbp), %rcx

	movq	-0x28(%rbp), %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmpq	*%r11

0:
	movq	-0x30(%rbp), %rcx
	movq	-0x38(%rbp), %rdx

	movq	%rbp, %rsp
	popq	%rbp

	jmp	OFMethodNotFound
.def OFForward
.scl 2
.type 32
.endef

OFForward_stret:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0x90, %rsp	/* 16-byte alignment */
	movq	%rax, -0x28(%rbp)
	movq	%rcx, -0x30(%rbp)
	movq	%rdx, -0x38(%rbp)
	movq	%r8, -0x40(%rbp)
	movq	%r9, -0x48(%rbp)
	movaps	%xmm0, -0x60(%rbp)
	movaps	%xmm1, -0x70(%rbp)
	movaps	%xmm2, -0x80(%rbp)
	movaps	%xmm3, -0x90(%rbp)

	movq	%rdx, %rcx
	call	object_getClass

	movq	%rax, %rcx

	leaq	sel_forwardingTargetForSelector_(%rip), %rdx
	call	class_respondsToSelector

	testq	%rax, %rax
	jz	0f

	movq	-0x38(%rbp), %rcx
	leaq	sel_forwardingTargetForSelector_(%rip), %rdx
	call	objc_msg_lookup

	movq	-0x38(%rbp), %rcx
	leaq	sel_forwardingTargetForSelector_(%rip), %rdx
	movq	-0x40(%rbp), %r8
	call	*%rax

	testq	%rax, %rax
	jz	0f
	cmpq	-0x38(%rbp), %rax
	je	0f

	movq	%rax, -0x38(%rbp)

	movq	%rax, %rcx
	movq	-0x40(%rbp), %rdx
	call	objc_msg_lookup_stret
	movq	%rax, %r11

	/* Restore all arguments */
	movaps	-0x90(%rbp), %xmm3
	movaps	-0x80(%rbp), %xmm2
	movaps	-0x70(%rbp), %xmm1
	movaps	-0x60(%rbp), %xmm0
	movq	-0x48(%rbp), %r9
	movq	-0x40(%rbp), %r8
	movq	-0x38(%rbp), %rdx
	movq	-0x30(%rbp), %rcx
	movq	-0x28(%rbp), %rax

	movq	%rbp, %rsp


	popq	%rbp

	jmpq	*%r11

0:
	movq	-0x30(%rbp), %rcx
	movq	-0x38(%rbp), %rdx
	movq	-0x40(%rbp), %r8

	movq	%rbp, %rsp
	popq	%rbp

	jmp	OFMethodNotFound_stret
.def OFForward_stret
.scl 2
.type 32
.endef

init:
	leaq	module(%rip), %rcx
	jmp	__objc_exec_class

.section .ctors, "aw"
	.quad init

.section .rodata
str_forwardingTargetForSelector_:

Modified src/forwarding/forwarding.S from [873a2fafd1] to [0a5e45dea8].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Deleted src/invocation/Makefile version [a7afc8dd3a].

1
2
3
4
5
6
7
8
9
10
11
12
include ../../extra.mk

STATIC_PIC_LIB_NOINST = ${INVOCATION_LIB_A}
STATIC_LIB_NOINST = ${INVOCATION_A}

SRCS = call.S	\
       invoke.m

include ../../buildsys.mk

ASFLAGS += -I../.. -I..
OBJCFLAGS += -I../.. -I.. -I../exceptions -I../runtime
<
<
<
<
<
<
<
<
<
<
<
<
























Deleted src/invocation/apple-call-x86_64.S version [3884ee11f3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "invoke-x86_64.h"

.intel_syntax noprefix

.globl _of_invocation_call

.section __TEXT, __text, regular, pure_instructions
_of_invocation_call:
	push	rbp
	mov	rbp, rsp

	sub	rsp, 16
	and	rsp, -16
	mov	[rbp-8], rdi

	lea	rdx, [rdi+OFFSET_STACK]
	mov	rcx, [rdi+OFFSET_STACK_SIZE]

	test	rcx, 1
	jnz	Lfix_align

Lfill_stack:
	test	rcx, rcx
	jz	Lstack_filled

	dec	rcx
	mov	r11, [rdx+rcx*8]
	push	r11

	jmp	Lfill_stack

Lstack_filled:
	mov	al, [rdi+OFFSET_NUM_SSE_USED]

	movaps	xmm7, [rdi+OFFSET_SSE_INOUT+112]
	movaps	xmm6, [rdi+OFFSET_SSE_INOUT+96]
	movaps	xmm5, [rdi+OFFSET_SSE_INOUT+80]
	movaps	xmm4, [rdi+OFFSET_SSE_INOUT+64]
	movaps	xmm3, [rdi+OFFSET_SSE_INOUT+48]
	movaps	xmm2, [rdi+OFFSET_SSE_INOUT+32]
	movaps	xmm1, [rdi+OFFSET_SSE_INOUT+16]
	movaps	xmm0, [rdi+OFFSET_SSE_INOUT]

	mov	r9,  [rdi+OFFSET_GPR_IN+40]
	mov	r8,  [rdi+OFFSET_GPR_IN+32]
	mov	rcx, [rdi+OFFSET_GPR_IN+24]
	mov	rdx, [rdi+OFFSET_GPR_IN+16]
	mov	rsi, [rdi+OFFSET_GPR_IN+8]

	mov	r11b, [rdi+OFFSET_RETURN_TYPE]
	mov	rdi,  [rdi+OFFSET_GPR_IN]

	cmp	r11b, RETURN_TYPE_STRET
	je	Lcall_send_stret

	cmp	r11b, RETURN_TYPE_JMP
	je	_objc_msgSend

	cmp	r11b, RETURN_TYPE_JMP_STRET
	je	_objc_msgSend_stret

	call	_objc_msgSend

Lafter_send:
	mov	rdi, [rbp-8]
	mov	[rdi+OFFSET_GPR_OUT], rax
	mov	[rdi+OFFSET_GPR_OUT+8], rdx
	movaps	[rdi+OFFSET_SSE_INOUT], xmm0
	movaps	[rdi+OFFSET_SSE_INOUT+16], xmm1

	mov	r11b, [rdi+OFFSET_RETURN_TYPE]

	cmp	r11b, RETURN_TYPE_X87
	je	Lpop_long_double

	cmp	r11b, RETURN_TYPE_COMPLEX_X87
	je	Lpop_complex_long_double

Lreturn:
	mov	rsp, rbp
	pop	rbp

	ret

Lfix_align:
	xor	r11, r11
	push	r11
	jmp	Lfill_stack

Lcall_send_stret:
	call	_objc_msgSend_stret
	jmp	Lafter_send

Lpop_long_double:
	fstp	tbyte ptr [rdi+OFFSET_X87_OUT]
	jmp	Lreturn

Lpop_complex_long_double:
	fstp	tbyte ptr [rdi+OFFSET_X87_OUT]
	fstp	tbyte ptr [rdi+OFFSET_X87_OUT+16]
	jmp	Lreturn
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































































































































Deleted src/invocation/call-x86_64-elf.S version [10e2914d71].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "invoke-x86_64.h"

.intel_syntax noprefix

.globl of_invocation_call

.section .text
of_invocation_call:
	pushq	rbp
	mov	rbp, rsp

	sub	rsp, 16
	and	rsp, -16
	mov	[rbp-8], rdi

	mov	r11b, [rdi+OFFSET_RETURN_TYPE]
	cmp	r11b, RETURN_TYPE_STRET
	je	short .Llookup_stret
	cmp	r11b, RETURN_TYPE_JMP_STRET
	je	short .Llookup_stret

	mov	rsi, [rdi+OFFSET_GPR_IN+8]
	mov	rdi, [rdi+OFFSET_GPR_IN]
	call	objc_msg_lookup@PLT

.Lafter_lookup:
	mov	[rbp-16], rax
	mov	rdi, [rbp-8]

	lea	rdx, [rdi+OFFSET_STACK]
	mov	rcx, [rdi+OFFSET_STACK_SIZE]

	test	rcx, 1
	jnz	short .Lfix_align

.Lfill_stack:
	test	rcx, rcx
	jz	short .Lstack_filled

	dec	rcx
	mov	r11, [rdx+rcx*8]
	push	r11

	jmp	short .Lfill_stack

.Lstack_filled:
	mov	al, [rdi+OFFSET_NUM_SSE_USED]

	movaps	xmm7, [rdi+OFFSET_SSE_INOUT+112]
	movaps	xmm6, [rdi+OFFSET_SSE_INOUT+96]
	movaps	xmm5, [rdi+OFFSET_SSE_INOUT+80]
	movaps	xmm4, [rdi+OFFSET_SSE_INOUT+64]
	movaps	xmm3, [rdi+OFFSET_SSE_INOUT+48]
	movaps	xmm2, [rdi+OFFSET_SSE_INOUT+32]
	movaps	xmm1, [rdi+OFFSET_SSE_INOUT+16]
	movaps	xmm0, [rdi+OFFSET_SSE_INOUT]

	mov	r9,  [rdi+OFFSET_GPR_IN+40]
	mov	r8,  [rdi+OFFSET_GPR_IN+32]
	mov	rcx, [rdi+OFFSET_GPR_IN+24]
	mov	rdx, [rdi+OFFSET_GPR_IN+16]
	mov	rsi, [rdi+OFFSET_GPR_IN+8]

	mov	r11b, [rdi+OFFSET_RETURN_TYPE]
	mov	rdi,  [rdi+OFFSET_GPR_IN]

	cmp	r11b, RETURN_TYPE_JMP
	je	short .Ljmp_into_method
	cmp	r11b, RETURN_TYPE_JMP_STRET
	je	short .Ljmp_into_method

	mov	r11, [rbp-16]
	call	r11

.Lafter_send:
	mov	rdi, [rbp-8]
	mov	[rdi+OFFSET_GPR_OUT], rax
	mov	[rdi+OFFSET_GPR_OUT+8], rdx
	movaps	[rdi+OFFSET_SSE_INOUT], xmm0
	movaps	[rdi+OFFSET_SSE_INOUT+16], xmm1

	mov	r11b, [rdi+OFFSET_RETURN_TYPE]

	cmp	r11b, RETURN_TYPE_X87
	je	short .Lpop_long_double

	cmp	r11b, RETURN_TYPE_COMPLEX_X87
	je	short .Lpop_complex_long_double

.Lreturn:
	mov	rsp, rbp
	pop	rbp

	ret

.Lfix_align:
	xor	r11, r11
	push	r11
	jmp	short .Lfill_stack

.Llookup_stret:
	mov	rsi, [rdi+OFFSET_GPR_IN+16]
	mov	rdi, [rdi+OFFSET_GPR_IN+8]
	call	objc_msg_lookup_stret@PLT

	jmp	short .Lafter_lookup

.Ljmp_into_method:
	mov	r11, [rbp-16]
	jmp	r11

.Lpop_long_double:
	fstp	tbyte ptr [rdi+OFFSET_X87_OUT]
	jmp	short .Lreturn

.Lpop_complex_long_double:
	fstp	tbyte ptr [rdi+OFFSET_X87_OUT]
	fstp	tbyte ptr [rdi+OFFSET_X87_OUT+16]
	jmp	short .Lreturn

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































Deleted src/invocation/call.S version [94426b7ab0].

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, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

#ifdef OF_APPLE_RUNTIME
# ifdef OF_X86_64
#  include "apple-call-x86_64.S"
# endif
#else
# ifdef OF_ELF
#  ifdef OF_X86_64
#   include "call-x86_64-elf.S"
#  endif
# endif
#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































Deleted src/invocation/invoke-x86_64.h version [e748f149fa].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

#define RETURN_TYPE_NORMAL	0
#define RETURN_TYPE_STRET	1
#define RETURN_TYPE_X87		2
#define RETURN_TYPE_COMPLEX_X87	3
#define RETURN_TYPE_JMP		4
#define RETURN_TYPE_JMP_STRET	5

#define NUM_GPR_IN	6
#define NUM_GPR_OUT	2
#define NUM_SSE_INOUT	8
#define NUM_X87_OUT	2

#define OFFSET_GPR_IN		0
#define OFFSET_GPR_OUT		(OFFSET_GPR_IN + NUM_GPR_IN * 8)
#define OFFSET_SSE_INOUT	(OFFSET_GPR_OUT + NUM_GPR_OUT * 8)
#define OFFSET_X87_OUT		(OFFSET_SSE_INOUT + NUM_SSE_INOUT * 16)
#define OFFSET_NUM_SSE_USED	(OFFSET_X87_OUT + NUM_X87_OUT * 16)
#define OFFSET_RETURN_TYPE	(OFFSET_NUM_SSE_USED + 1)
#define OFFSET_STACK_SIZE	(OFFSET_RETURN_TYPE + 7)
#define OFFSET_STACK		(OFFSET_STACK_SIZE + 8)
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































Deleted src/invocation/invoke-x86_64.m version [ab1dc9bdb2].

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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdint.h>
#include <stdlib.h>
#include <xmmintrin.h>

#import "OFInvocation.h"
#import "OFMethodSignature.h"

#import "OFInvalidFormatException.h"
#import "OFOutOfMemoryException.h"

#import "invoke-x86_64.h"

#import "macros.h"

struct call_context {
	uint64_t GPR[NUM_GPR_IN + NUM_GPR_OUT];
	__m128 SSE[NUM_SSE_INOUT];
	long double X87[NUM_X87_OUT];
	uint8_t numSSEUsed;
	uint8_t returnType;
	uint64_t stackSize;
	uint64_t stack[];
};

extern void of_invocation_call(struct call_context *);

static void
pushGPR(struct call_context **context, uint_fast8_t *currentGPR, uint64_t value)
{
	struct call_context *newContext;

	if (*currentGPR < NUM_GPR_IN) {
		(*context)->GPR[(*currentGPR)++] = value;
		return;
	}

	if ((newContext = realloc(*context,
	    sizeof(**context) + ((*context)->stackSize + 1) * 8)) == NULL) {
		free(*context);
		@throw [OFOutOfMemoryException exceptionWithRequestedSize:
		    sizeof(**context) + ((*context)->stackSize + 1) * 8];
	}

	newContext->stack[newContext->stackSize] = value;
	newContext->stackSize++;
	*context = newContext;
}

static void
pushDouble(struct call_context **context, uint_fast8_t *currentSSE,
    double value)
{
	struct call_context *newContext;

	if (*currentSSE < NUM_SSE_INOUT) {
		(*context)->SSE[(*currentSSE)++] = (__m128)_mm_set_sd(value);
		(*context)->numSSEUsed++;
		return;
	}

	if ((newContext = realloc(*context,
	    sizeof(**context) + ((*context)->stackSize + 1) * 8)) == NULL) {
		free(*context);
		@throw [OFOutOfMemoryException exceptionWithRequestedSize:
		    sizeof(**context) + ((*context)->stackSize + 1) * 8];
	}

	memcpy(&newContext->stack[newContext->stackSize], &value, 8);
	newContext->stackSize++;
	*context = newContext;
}

static void
pushQuad(struct call_context **context, uint_fast8_t *currentSSE,
    double low, double high)
{
	size_t stackSize;
	struct call_context *newContext;

	if (*currentSSE + 1 < NUM_SSE_INOUT) {
		(*context)->SSE[(*currentSSE)++] = (__m128)_mm_set_sd(low);
		(*context)->SSE[(*currentSSE)++] = (__m128)_mm_set_sd(high);
		(*context)->numSSEUsed += 2;
		return;
	}

	stackSize = (*context)->stackSize + 2;

	if ((newContext = realloc(*context,
	    sizeof(**context) + stackSize * 8)) == NULL) {
		free(*context);
		@throw [OFOutOfMemoryException exceptionWithRequestedSize:
		    sizeof(**context) + stackSize * 8];
	}

	memset(&newContext->stack[newContext->stackSize], '\0',
	    (stackSize - newContext->stackSize) * 8);
	memcpy(&newContext->stack[stackSize - 2], &low, 8);
	memcpy(&newContext->stack[stackSize - 1], &high, 8);
	newContext->stackSize = stackSize;
	*context = newContext;
}

static void
pushLongDouble(struct call_context **context, long double value)
{
	struct call_context *newContext;

	if ((newContext = realloc(*context,
	    sizeof(**context) + ((*context)->stackSize + 2) * 8)) == NULL) {
		free(*context);
		@throw [OFOutOfMemoryException exceptionWithRequestedSize:
		    sizeof(**context) + ((*context)->stackSize + 2) * 8];
	}

	memcpy(&newContext->stack[newContext->stackSize], &value, 16);
	newContext->stackSize += 2;
	*context = newContext;
}

static void
pushLongDoublePair(struct call_context **context, long double value[2])
{
	size_t stackSize;
	struct call_context *newContext;

	stackSize = OF_ROUND_UP_POW2(2UL, (*context)->stackSize) + 4;

	if ((newContext = realloc(*context,
	    sizeof(**context) + stackSize * 8)) == NULL) {
		free(*context);
		@throw [OFOutOfMemoryException exceptionWithRequestedSize:
		    sizeof(**context) + stackSize * 8];
	}

	memset(&newContext->stack[newContext->stackSize], '\0',
	    (stackSize - newContext->stackSize) * 8);
	memcpy(&newContext->stack[stackSize - 4], value, 32);
	newContext->stackSize = stackSize;
	*context = newContext;
}

#if defined(__SIZEOF_INT128__) && !defined(__clang__)
static void
pushInt128(struct call_context **context, uint_fast8_t *currentGPR,
    uint64_t value[2])
{
	size_t stackSize;
	struct call_context *newContext;

	if (*currentGPR + 1 < NUM_GPR_IN) {
		(*context)->GPR[(*currentGPR)++] = value[0];
		(*context)->GPR[(*currentGPR)++] = value[1];
		return;
	}

	stackSize = OF_ROUND_UP_POW2(2, (*context)->stackSize) + 2;

	if ((newContext = realloc(*context,
	    sizeof(**context) + stackSize * 8)) == NULL) {
		free(*context);
		@throw [OFOutOfMemoryException exceptionWithRequestedSize:
		    sizeof(**context) + stackSize * 8];
	}

	memset(&newContext->stack[newContext->stackSize], '\0',
	    (stackSize - newContext->stackSize) * 8);
	memcpy(&newContext->stack[stackSize - 2], value, 16);
	newContext->stackSize = stackSize;
	*context = newContext;
}
#endif

void
of_invocation_invoke(OFInvocation *invocation)
{
	OFMethodSignature *methodSignature = invocation.methodSignature;
	size_t numberOfArguments = methodSignature.numberOfArguments;
	struct call_context *context;
	const char *typeEncoding;
	uint_fast8_t currentGPR = 0, currentSSE = 0;

	if ((context = calloc(sizeof(*context), 1)) == NULL)
		@throw [OFOutOfMemoryException exception];

	for (size_t i = 0; i < numberOfArguments; i++) {
		typeEncoding = [methodSignature argumentTypeAtIndex: i];

		if (*typeEncoding == 'r')
			typeEncoding++;

		switch (*typeEncoding) {
#define CASE_GPR(encoding, type)					       \
		case encoding:						       \
			{						       \
				type tmp;				       \
				[invocation getArgument: &tmp		       \
						atIndex: i];		       \
				pushGPR(&context, &currentGPR, (uint64_t)tmp); \
			}						       \
			break;
		CASE_GPR('c', char)
		CASE_GPR('C', unsigned char)
		CASE_GPR('i', int)
		CASE_GPR('I', unsigned int)
		CASE_GPR('s', short)
		CASE_GPR('S', unsigned short)
		CASE_GPR('l', long)
		CASE_GPR('L', unsigned long)
		CASE_GPR('q', long long)
		CASE_GPR('Q', unsigned long long)
		CASE_GPR('B', _Bool)
		CASE_GPR('*', char *)
		CASE_GPR('@', id)
		CASE_GPR('#', Class)
		/*
		 * Using SEL triggers a warning that casting a SEL to an
		 * integer is deprecated.
		 */
		CASE_GPR(':', void *)
		CASE_GPR('^', void *)
#undef CASE_GPR
#ifdef __SIZEOF_INT128__
		case 't':
		case 'T':;
			uint64_t int128Tmp[2];
			[invocation getArgument: &int128Tmp
					atIndex: i];
# ifndef __clang__
			pushInt128(&context, &currentGPR, int128Tmp);
# else
			/* See https://bugs.llvm.org/show_bug.cgi?id=34646 */
			pushGPR(&context, &currentGPR, int128Tmp[0]);
			pushGPR(&context, &currentGPR, int128Tmp[1]);
# endif
			break;
#endif
		case 'f':;
			double floatTmp = 0;
			[invocation getArgument: &floatTmp
					atIndex: i];
			pushDouble(&context, &currentSSE, floatTmp);
			break;
		case 'd':;
			double doubleTmp;
			[invocation getArgument: &doubleTmp
					atIndex: i];
			pushDouble(&context, &currentSSE, doubleTmp);
			break;
		case 'D':;
			long double longDoubleTmp;
			[invocation getArgument: &longDoubleTmp
					atIndex: i];
			pushLongDouble(&context, longDoubleTmp);
			break;
		case 'j':
			switch (typeEncoding[1]) {
			case 'f':;
				double complexFloatTmp;
				[invocation getArgument: &complexFloatTmp
						atIndex: i];
				pushDouble(&context, &currentSSE,
				    complexFloatTmp);
				break;
			case 'd':;
				double complexDoubleTmp[2];
				[invocation getArgument: &complexDoubleTmp
						atIndex: i];
				pushQuad(&context, &currentSSE,
				    complexDoubleTmp[0], complexDoubleTmp[1]);
				break;
			case 'D':;
				long double complexLongDoubleTmp[2];
				[invocation getArgument: &complexLongDoubleTmp
						atIndex: i];
				pushLongDoublePair(&context,
				    complexLongDoubleTmp);
				break;
			default:
				free(context);
				@throw [OFInvalidFormatException exception];
			}

			break;
		/* TODO: '[' */
		/* TODO: '{' */
		/* TODO: '(' */
		default:
			free(context);
			@throw [OFInvalidFormatException exception];
		}
	}

	typeEncoding = methodSignature.methodReturnType;

	if (*typeEncoding == 'r')
		typeEncoding++;

	switch (*typeEncoding) {
	case 'v':
	case 'c':
	case 'C':
	case 'i':
	case 'I':
	case 's':
	case 'S':
	case 'l':
	case 'L':
	case 'q':
	case 'Q':
	case 'B':
	case '*':
	case '@':
	case '#':
	case ':':
	case '^':
#ifdef __SIZEOF_INT128__
	case 't':
	case 'T':
#endif
	case 'f':
	case 'd':
		context->returnType = RETURN_TYPE_NORMAL;
		break;
	case 'D':
		context->returnType = RETURN_TYPE_X87;
		break;
	case 'j':
		switch (typeEncoding[1]) {
		case 'f':
		case 'd':
			context->returnType = RETURN_TYPE_NORMAL;
			break;
		case 'D':
			context->returnType = RETURN_TYPE_COMPLEX_X87;
			break;
		default:
			free(context);
			@throw [OFInvalidFormatException exception];
		}

		break;
	/* TODO: '[' */
	/* TODO: '{' */
	/* TODO: '(' */
	default:
		free(context);
		@throw [OFInvalidFormatException exception];
	}

	of_invocation_call(context);

	switch (*typeEncoding) {
		case 'v':
			break;
#define CASE_GPR(encoding, type)					   \
		case encoding:						   \
			{						   \
				type tmp = (type)context->GPR[NUM_GPR_IN]; \
				[invocation setReturnValue: &tmp];	   \
			}						   \
			break;
		CASE_GPR('c', char)
		CASE_GPR('C', unsigned char)
		CASE_GPR('i', int)
		CASE_GPR('I', unsigned int)
		CASE_GPR('s', short)
		CASE_GPR('S', unsigned short)
		CASE_GPR('l', long)
		CASE_GPR('L', unsigned long)
		CASE_GPR('q', long long)
		CASE_GPR('Q', unsigned long long)
		CASE_GPR('B', _Bool)
		CASE_GPR('*', char *)
		CASE_GPR('@', id)
		CASE_GPR('#', Class)
		CASE_GPR(':', SEL)
		CASE_GPR('^', void *)
#undef CASE_GPR
#ifdef __SIZEOF_INT128__
		case 't':
		case 'T':;
			[invocation setReturnValue: &context->GPR[NUM_GPR_IN]];
			break;
#endif
		case 'f':;
			float floatTmp;
			_mm_store_ss(&floatTmp, context->SSE[0]);
			[invocation setReturnValue: &floatTmp];
			break;
		case 'd':;
			double doubleTmp;
			_mm_store_sd(&doubleTmp, (__m128d)context->SSE[0]);
			[invocation setReturnValue: &doubleTmp];
			break;
		case 'D':
			[invocation setReturnValue: &context->X87[0]];
			break;
		case 'j':
			switch (typeEncoding[1]) {
			case 'f':;
				double complexFloatTmp;
				_mm_store_sd(&complexFloatTmp,
				    (__m128d)context->SSE[0]);
				[invocation setReturnValue: &complexFloatTmp];
				break;
			case 'd':;
				double complexDoubleTmp[2];
				_mm_store_sd(&complexDoubleTmp[0],
				    (__m128d)context->SSE[0]);
				_mm_store_sd(&complexDoubleTmp[1],
				    (__m128d)context->SSE[1]);
				[invocation setReturnValue: &complexDoubleTmp];
				break;
			case 'D':
				[invocation setReturnValue: context->X87];
				break;
			default:
				free(context);
				@throw [OFInvalidFormatException exception];
			}

			break;
		/* TODO: '[' */
		/* TODO: '{' */
		/* TODO: '(' */
		default:
			free(context);
			@throw [OFInvalidFormatException exception];
	}

	free(context);
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted src/invocation/invoke.m version [619adef304].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

#if defined(OF_X86_64) && (defined(OF_APPLE_RUNTIME) || defined(OF_ELF))
# include "invoke-x86_64.m"
#else
/* To not have an empty translation unit otherwise */
int of_invocation_cannot_invoke;
#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































Modified src/libbases.m from [6db9ec2780] to [952ca43ba1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


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

Modified src/macros.h from [43e7dc4d8f] to [60a41b82a1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
23
24
25
26
27
28
29

30
31
32
33
34
35
36
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS
#endif
#ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS
#endif


#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>








>







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS
#endif
#ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS
#endif

#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# define OF_LIKELY(cond) (cond)
# define OF_UNLIKELY(cond) (cond)
# define OF_CONST_FUNC
# define OF_NO_RETURN_FUNC
# define OF_WEAK_REF(sym)
#endif

#ifdef OF_BIG_ENDIAN
# define OF_BYTE_ORDER_NATIVE OF_BYTE_ORDER_BIG_ENDIAN
#else
# define OF_BYTE_ORDER_NATIVE OF_BYTE_ORDER_LITTLE_ENDIAN
#endif

#if __STDC_VERSION__ >= 201112L
# define OF_ALIGNOF(type) _Alignof(type)
# define OF_ALIGNAS(type) _Alignas(type)
#else
# define OF_ALIGNOF(type) __alignof__(type)
# define OF_ALIGNAS(type) __attribute__((__aligned__(__alignof__(type))))
#endif







<
<
<
<
<
<







98
99
100
101
102
103
104






105
106
107
108
109
110
111
# define OF_LIKELY(cond) (cond)
# define OF_UNLIKELY(cond) (cond)
# define OF_CONST_FUNC
# define OF_NO_RETURN_FUNC
# define OF_WEAK_REF(sym)
#endif







#if __STDC_VERSION__ >= 201112L
# define OF_ALIGNOF(type) _Alignof(type)
# define OF_ALIGNAS(type) _Alignas(type)
#else
# define OF_ALIGNOF(type) __alignof__(type)
# define OF_ALIGNAS(type) __attribute__((__aligned__(__alignof__(type))))
#endif
144
145
146
147
148
149
150



151
152
153
154
155
156
157
#endif

#ifdef __GNUC__
# define OF_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
# define OF_GCC_VERSION 0
#endif




#ifndef __has_feature
# define __has_feature(x) 0
#endif

#ifndef __has_attribute
# define __has_attribute(x) 0







>
>
>







137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#endif

#ifdef __GNUC__
# define OF_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
# define OF_GCC_VERSION 0
#endif

#define OF_STRINGIFY(s) OF_STRINGIFY2(s)
#define OF_STRINGIFY2(s) #s

#ifndef __has_feature
# define __has_feature(x) 0
#endif

#ifndef __has_attribute
# define __has_attribute(x) 0
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# define OF_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
#else
# define OF_WARN_UNUSED_RESULT
#endif

#if __has_attribute(__unavailable__)
# define OF_UNAVAILABLE __attribute__((__unavailable__))
# define OF_HAVE_UNAVAILABLE
#else
# define OF_UNAVAILABLE
#endif

#if __has_attribute(__objc_requires_super__)
# define OF_REQUIRES_SUPER __attribute__((__objc_requires_super__))
#else







<







252
253
254
255
256
257
258

259
260
261
262
263
264
265
# define OF_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
#else
# define OF_WARN_UNUSED_RESULT
#endif

#if __has_attribute(__unavailable__)
# define OF_UNAVAILABLE __attribute__((__unavailable__))

#else
# define OF_UNAVAILABLE
#endif

#if __has_attribute(__objc_requires_super__)
# define OF_REQUIRES_SUPER __attribute__((__objc_requires_super__))
#else
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
#endif
#if __has_attribute(__objc_direct_members__) && defined(OF_APPLE_RUNTIME)
# define OF_DIRECT_MEMBERS __attribute__((__objc_direct_members__))
#else
# define OF_DIRECT_MEMBERS
#endif

#ifdef __GNUC__
# ifdef OF_X86_64
#  define OF_X86_64_ASM
# endif
# ifdef OF_X86
#  define OF_X86_ASM
# endif
# ifdef OF_POWERPC
#  define OF_POWERPC_ASM
# endif
# ifdef OF_ARM64
#  define OF_ARM64_ASM
# endif
# ifdef OF_ARM
#  define OF_ARM_ASM
# endif
# ifdef OF_ARMV7
#  define OF_ARMV7_ASM
# endif
# ifdef OF_ARMV6
#  define OF_ARMV6_ASM
# endif
# ifdef OF_MIPS64
#  define OF_MIPS64_ASM
# endif
# ifdef OF_MIPS
#  define OF_MIPS_ASM
# endif
# ifdef OF_PA_RISC
#  define OF_PA_RISC_ASM
# endif
# ifdef OF_ITANIUM
#  define OF_ITANIUM_ASM
# endif
#endif

#ifdef OF_APPLE_RUNTIME
# if defined(OF_X86_64) || defined(OF_X86) || defined(OF_ARM64) || \
    defined(OF_ARM) || defined(OF_POWERPC)
#  define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
#  define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
# endif
#else







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







311
312
313
314
315
316
317




































318
319
320
321
322
323
324
#endif
#if __has_attribute(__objc_direct_members__) && defined(OF_APPLE_RUNTIME)
# define OF_DIRECT_MEMBERS __attribute__((__objc_direct_members__))
#else
# define OF_DIRECT_MEMBERS
#endif





































#ifdef OF_APPLE_RUNTIME
# if defined(OF_X86_64) || defined(OF_X86) || defined(OF_ARM64) || \
    defined(OF_ARM) || defined(OF_POWERPC)
#  define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
#  define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
# endif
#else
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
#   if __OBJFW_RUNTIME_ABI__ >= 800
#    define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
#   endif
#  endif
# endif
#endif

#define OF_RETAIN_COUNT_MAX UINT_MAX
#define OF_NOT_FOUND SIZE_MAX


#define OF_ENSURE(cond)							\
	do {								\








		if (!(cond)) {						\
			fprintf(stderr, "Failed to ensure condition "	\
			    "in " __FILE__ ":%d:\n" #cond "\n",		\
			    __LINE__);					\
			abort();					\
		}							\
	} while (0)


#define OF_UNRECOGNIZED_SELECTOR of_method_not_found(self, _cmd);
#if __has_feature(objc_arc)
# define OF_INVALID_INIT_METHOD of_method_not_found(self, _cmd);
#else
# define OF_INVALID_INIT_METHOD				\
	@try {						\
		of_method_not_found(self, _cmd);	\
	} @catch (id e) {				\
		[self release];				\
		@throw e;				\
	}						\
							\
	abort();
#endif







<
|

>
|

>
>
>
>
>
>
>
>
|






>

|

|



|







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
#   if __OBJFW_RUNTIME_ABI__ >= 800
#    define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
#   endif
#  endif
# endif
#endif


#define OFMaxRetainCount UINT_MAX

#ifdef OBJC_COMPILING_RUNTIME
# define OFEnsure(cond)							\
	do {								\
		if OF_UNLIKELY (!(cond))				\
			objc_error("ObjFWRT @ " __FILE__ ":"		\
			    OF_STRINGIFY(__LINE__),			\
			    "Failed to ensure condition:\n" #cond);	\
	} while(0)
#else
# define OFEnsure(cond)							\
	do {								\
		if OF_UNLIKELY (!(cond)) {				\
			fprintf(stderr, "Failed to ensure condition "	\
			    "in " __FILE__ ":%d:\n" #cond "\n",		\
			    __LINE__);					\
			abort();					\
		}							\
	} while (0)
#endif

#define OF_UNRECOGNIZED_SELECTOR OFMethodNotFound(self, _cmd);
#if __has_feature(objc_arc)
# define OF_INVALID_INIT_METHOD OFMethodNotFound(self, _cmd);
#else
# define OF_INVALID_INIT_METHOD				\
	@try {						\
		OFMethodNotFound(self, _cmd);		\
	} @catch (id e) {				\
		[self release];				\
		@throw e;				\
	}						\
							\
	abort();
#endif
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
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
	static void __attribute__((__constructor__(prio)))	\
	OF_PREPROCESSOR_CONCAT(constructor, __LINE__)(void)
#define OF_DESTRUCTOR(prio)					\
	static void __attribute__((__destructor__(prio)))	\
	OF_PREPROCESSOR_CONCAT(destructor, __LINE__)(void)

static OF_INLINE uint16_t OF_CONST_FUNC
OF_BSWAP16_CONST(uint16_t i)
{
	return (i & 0xFF00) >> 8 | (i & 0x00FF) << 8;
}

static OF_INLINE uint32_t OF_CONST_FUNC
OF_BSWAP32_CONST(uint32_t i)
{
	return (i & 0xFF000000) >> 24 | (i & 0x00FF0000) >> 8 |

	    (i & 0x0000FF00) << 8 | (i & 0x000000FF) << 24;

}

static OF_INLINE uint64_t OF_CONST_FUNC
OF_BSWAP64_CONST(uint64_t i)
{

	return (i & 0xFF00000000000000) >> 56 | (i & 0x00FF000000000000) >> 40 |
	    (i & 0x0000FF0000000000) >> 24 | (i & 0x000000FF00000000) >> 8 |

	    (i & 0x00000000FF000000) << 8 | (i & 0x0000000000FF0000) << 24 |

	    (i & 0x000000000000FF00) << 40 | (i & 0x00000000000000FF) << 56;

}

static OF_INLINE uint16_t OF_CONST_FUNC
OF_BSWAP16_NONCONST(uint16_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP16)
	return __builtin_bswap16(i);
#elif defined(OF_X86_64_ASM) || defined(OF_X86_ASM)
	__asm__ (
	    "xchgb	%h0, %b0"
	    : "=Q"(i)
	    : "0"(i)
	);
#elif defined(OF_POWERPC_ASM)
	__asm__ (
	    "lhbrx	%0, 0, %1"
	    : "=r"(i)
	    : "r"(&i), "m"(i)
	);
#elif defined(OF_ARMV6_ASM)
	__asm__ (
	    "rev16	%0, %0"
	    : "=r"(i)
	    : "0"(i)
	);
#else
	i = (i & UINT16_C(0xFF00)) >> 8 |
	    (i & UINT16_C(0x00FF)) << 8;
#endif
	return i;
}

static OF_INLINE uint32_t OF_CONST_FUNC
OF_BSWAP32_NONCONST(uint32_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP32)
	return __builtin_bswap32(i);
#elif defined(OF_X86_64_ASM) || defined(OF_X86_ASM)
	__asm__ (
	    "bswap	%0"
	    : "=q"(i)
	    : "0"(i)
	);
#elif defined(OF_POWERPC_ASM)
	__asm__ (
	    "lwbrx	%0, 0, %1"
	    : "=r"(i)
	    : "r"(&i), "m"(i)
	);
#elif defined(OF_ARMV6_ASM)
	__asm__ (
	    "rev	%0, %0"
	    : "=r"(i)
	    : "0"(i)
	);
#else
	i = (i & UINT32_C(0xFF000000)) >> 24 |
	    (i & UINT32_C(0x00FF0000)) >>  8 |
	    (i & UINT32_C(0x0000FF00)) <<  8 |
	    (i & UINT32_C(0x000000FF)) << 24;
#endif
	return i;
}

static OF_INLINE uint64_t OF_CONST_FUNC
OF_BSWAP64_NONCONST(uint64_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP64)
	return __builtin_bswap64(i);
#elif defined(OF_X86_64_ASM)
	__asm__ (
	    "bswap	%0"
	    : "=r"(i)
	    : "0"(i)
	);
#elif defined(OF_X86_ASM)
	__asm__ (
	    "bswap	%%eax\n\t"
	    "bswap	%%edx\n\t"
	    "xchgl	%%eax, %%edx"
	    : "=A"(i)
	    : "0"(i)
	);
#else
	i = (uint64_t)OF_BSWAP32_NONCONST((uint32_t)(i & 0xFFFFFFFF)) << 32 |

	    OF_BSWAP32_NONCONST((uint32_t)(i >> 32));
#endif
	return i;
}

#ifdef __GNUC__
# define OF_BSWAP16(i) \
    (__builtin_constant_p(i) ? OF_BSWAP16_CONST(i) : OF_BSWAP16_NONCONST(i))
# define OF_BSWAP32(i) \
    (__builtin_constant_p(i) ? OF_BSWAP32_CONST(i) : OF_BSWAP32_NONCONST(i))
# define OF_BSWAP64(i) \
    (__builtin_constant_p(i) ? OF_BSWAP64_CONST(i) : OF_BSWAP64_NONCONST(i))
#else
# define OF_BSWAP16(i) OF_BSWAP16_CONST(i)
# define OF_BSWAP32(i) OF_BSWAP32_CONST(i)
# define OF_BSWAP64(i) OF_BSWAP64_CONST(i)
#endif

static OF_INLINE uint32_t
OF_FLOAT_TO_INT_RAW(float f)
{
	uint32_t ret;
	memcpy(&ret, &f, 4);
	return ret;
}

static OF_INLINE float
OF_INT_TO_FLOAT_RAW(uint32_t uInt32)
{
	float ret;
	memcpy(&ret, &uInt32, 4);
	return ret;
}

static OF_INLINE uint64_t
OF_DOUBLE_TO_INT_RAW(double d)
{
	uint64_t ret;
	memcpy(&ret, &d, 8);
	return ret;
}

static OF_INLINE double
OF_INT_TO_DOUBLE_RAW(uint64_t uInt64)
{
	double ret;
	memcpy(&ret, &uInt64, 8);
	return ret;
}

static OF_INLINE float OF_CONST_FUNC
OF_BSWAP_FLOAT(float f)
{
	return OF_INT_TO_FLOAT_RAW(OF_BSWAP32(OF_FLOAT_TO_INT_RAW(f)));
}

static OF_INLINE double OF_CONST_FUNC
OF_BSWAP_DOUBLE(double d)
{
	return OF_INT_TO_DOUBLE_RAW(OF_BSWAP64(OF_DOUBLE_TO_INT_RAW(d)));
}

#ifdef OF_BIG_ENDIAN
# define OF_BSWAP16_IF_BE(i) OF_BSWAP16(i)
# define OF_BSWAP32_IF_BE(i) OF_BSWAP32(i)
# define OF_BSWAP64_IF_BE(i) OF_BSWAP64(i)



# define OF_BSWAP16_IF_LE(i) (i)
# define OF_BSWAP32_IF_LE(i) (i)
# define OF_BSWAP64_IF_LE(i) (i)



#else



# define OF_BSWAP16_IF_BE(i) (i)
# define OF_BSWAP32_IF_BE(i) (i)
# define OF_BSWAP64_IF_BE(i) (i)



# define OF_BSWAP16_IF_LE(i) OF_BSWAP16(i)
# define OF_BSWAP32_IF_LE(i) OF_BSWAP32(i)
# define OF_BSWAP64_IF_LE(i) OF_BSWAP64(i)
#endif

#ifdef OF_FLOAT_BIG_ENDIAN
# define OF_BSWAP_FLOAT_IF_BE(i) OF_BSWAP_FLOAT(i)
# define OF_BSWAP_DOUBLE_IF_BE(i) OF_BSWAP_DOUBLE(i)


# define OF_BSWAP_FLOAT_IF_LE(i) (i)
# define OF_BSWAP_DOUBLE_IF_LE(i) (i)


#else


# define OF_BSWAP_FLOAT_IF_BE(i) (i)
# define OF_BSWAP_DOUBLE_IF_BE(i) (i)


# define OF_BSWAP_FLOAT_IF_LE(i) OF_BSWAP_FLOAT(i)
# define OF_BSWAP_DOUBLE_IF_LE(i) OF_BSWAP_DOUBLE(i)
#endif

static OF_INLINE uint16_t
of_be16_ptr_read(void *_Nonnull ptr)
{
	uint16_t value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP16_IF_LE(value);
}

static OF_INLINE uint32_t
of_be32_ptr_read(void *_Nonnull ptr)
{
	uint32_t value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP32_IF_LE(value);
}

static OF_INLINE uint64_t
of_be64_ptr_read(void *_Nonnull ptr)
{
	uint64_t value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP64_IF_LE(value);
}

static OF_INLINE float
of_be_float_ptr_read(void *_Nonnull ptr)
{
	float value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP_FLOAT_IF_LE(value);
}

static OF_INLINE double
of_be_double_ptr_read(void *_Nonnull ptr)
{
	double value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP_DOUBLE_IF_LE(value);
}

static OF_INLINE uint16_t
of_le16_ptr_read(void *_Nonnull ptr)
{
	uint16_t value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP16_IF_BE(value);
}

static OF_INLINE uint32_t
of_le32_ptr_read(void *_Nonnull ptr)
{
	uint32_t value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP32_IF_BE(value);
}

static OF_INLINE uint64_t
of_le64_ptr_read(void *_Nonnull ptr)
{
	uint64_t value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP64_IF_BE(value);
}

static OF_INLINE float
of_le_float_ptr_read(void *_Nonnull ptr)
{
	float value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP_FLOAT_IF_BE(value);
}

static OF_INLINE double
of_le_double_ptr_read(void *_Nonnull ptr)
{
	double value;
	memcpy(&value, ptr, sizeof(value));
	return OF_BSWAP_DOUBLE_IF_BE(value);
}

static OF_INLINE void
of_be16_ptr_write(void *_Nonnull ptr, uint16_t value)
{
	value = OF_BSWAP16_IF_LE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_be32_ptr_write(void *_Nonnull ptr, uint32_t value)
{
	value = OF_BSWAP32_IF_LE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_be64_ptr_write(void *_Nonnull ptr, uint64_t value)
{
	value = OF_BSWAP64_IF_LE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_be_float_ptr_write(void *_Nonnull ptr, float value)
{
	value = OF_BSWAP_FLOAT_IF_LE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_be_double_ptr_write(void *_Nonnull ptr, double value)
{
	value = OF_BSWAP_DOUBLE_IF_LE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_le16_ptr_write(void *_Nonnull ptr, uint16_t value)
{
	value = OF_BSWAP16_IF_BE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_le32_ptr_write(void *_Nonnull ptr, uint32_t value)
{
	value = OF_BSWAP32_IF_BE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_le64_ptr_write(void *_Nonnull ptr, uint64_t value)
{
	value = OF_BSWAP64_IF_BE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_le_float_ptr_write(void *_Nonnull ptr, float value)
{
	value = OF_BSWAP_FLOAT_IF_BE(value);
	memcpy(ptr, &value, sizeof(value));
}

static OF_INLINE void
of_le_double_ptr_write(void *_Nonnull ptr, double value)
{
	value = OF_BSWAP_DOUBLE_IF_BE(value);
	memcpy(ptr, &value, sizeof(value));
}

#define OF_ROL(value, bits)						\
    (((bits) % (sizeof(value) * 8)) > 0					\
    ? ((value) << ((bits) % (sizeof(value) * 8))) |			\
    ((value) >> (sizeof(value) * 8 - ((bits) % (sizeof(value) * 8))))	\
    : (value))
#define OF_ROR(value, bits)						\
    (((bits) % (sizeof(value) * 8)) > 0					\
    ? ((value) >> ((bits) % (sizeof(value) * 8))) |			\
    ((value) << (sizeof(value) * 8 - ((bits) % (sizeof(value) * 8))))	\
    : (value))


#define OF_ROUND_UP_POW2(pow2, value) (((value) + (pow2) - 1) & ~((pow2) - 1))

#define OF_HASH_INIT(hash) hash = of_hash_seed;
#define OF_HASH_ADD(hash, byte)			\
	{					\
		hash += (uint8_t)(byte);	\
		hash += (hash << 10);		\
		hash ^= (hash >> 6);		\
	}
#define OF_HASH_FINALIZE(hash)		\
	{				\
		hash += (hash << 3);	\
		hash ^= (hash >> 11);	\
		hash += (hash << 15);	\
	}
#define OF_HASH_ADD_HASH(hash, other)				\
	{							\
		uint32_t otherCopy = (uint32_t)other;		\
		OF_HASH_ADD(hash, (otherCopy >> 24) & 0xFF);	\
		OF_HASH_ADD(hash, (otherCopy >> 16) & 0xFF);	\
		OF_HASH_ADD(hash, (otherCopy >>  8) & 0xFF);	\
		OF_HASH_ADD(hash, otherCopy & 0xFF);		\
	}

static OF_INLINE bool
of_bitset_isset(unsigned char *_Nonnull storage, size_t idx)
{
	return storage[idx / 8] & (1u << (idx % 8));
}




static OF_INLINE void
of_bitset_set(unsigned char *_Nonnull storage, size_t idx)
{
	storage[idx / 8] |= (1u << (idx % 8));
}

static OF_INLINE void
of_bitset_clear(unsigned char *_Nonnull storage, size_t idx)
{
	storage[idx / 8] &= ~(1u << (idx % 8));
}

static OF_INLINE char *_Nullable
of_strdup(const char *_Nonnull string)
{
	char *copy;
	size_t length = strlen(string);

	if ((copy = (char *)malloc(length + 1)) == NULL)
		return NULL;

	memcpy(copy, string, length + 1);

	return copy;
}

static OF_INLINE void
of_explicit_memset(void *_Nonnull buffer_, int character, size_t length)
{
	volatile unsigned char *buffer = (volatile unsigned char *)buffer_;

	while (buffer < (unsigned char *)buffer_ + length)
		*buffer++ = character;
}

static OF_INLINE bool
of_ascii_isalpha(char c)
{
	return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
}

static OF_INLINE bool
of_ascii_isdigit(char c)
{
	return (c >= '0' && c <= '9');
}

static OF_INLINE bool
of_ascii_isalnum(char c)
{
	return (of_ascii_isalpha(c) || of_ascii_isdigit(c));
}

static OF_INLINE bool
of_ascii_isspace(char c)
{
	return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' ||
	    c == '\v');
}

static OF_INLINE char
of_ascii_toupper(char c)
{
	return (c >= 'a' && c <= 'z' ? 'A' + (c - 'a') : c);
}

static OF_INLINE char
of_ascii_tolower(char c)
{
	return (c >= 'A' && c <= 'Z' ? 'a' + (c - 'A') : c);
}
#endif







|

|



|

|
>
|
>



|

>
|
|
>
|
>
|
>



|



|





|





|













|



|





|





|















|



|





|








|
>
|





|
|
|
|
|
|

|
|
|



|







|







|







|







|

|



|

|



|
|
|
>
>
>
|
|
|
>
>
>

>
>
>
|
|
|
>
>
>
|
|
|



|
|
>
>
|
|
>
>

>
>
|
|
>
>
|
|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|




|





>
|

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

|

|

>
>
>
|
<
<
<
|



|

|


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

|




|



|





|





|

|



|






|





|




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
	static void __attribute__((__constructor__(prio)))	\
	OF_PREPROCESSOR_CONCAT(constructor, __LINE__)(void)
#define OF_DESTRUCTOR(prio)					\
	static void __attribute__((__destructor__(prio)))	\
	OF_PREPROCESSOR_CONCAT(destructor, __LINE__)(void)

static OF_INLINE uint16_t OF_CONST_FUNC
OFByteSwap16Const(uint16_t i)
{
	return (i & UINT16_C(0xFF00)) >> 8 | (i & UINT16_C(0x00FF)) << 8;
}

static OF_INLINE uint32_t OF_CONST_FUNC
OFByteSwap32Const(uint32_t i)
{
	return (i & UINT32_C(0xFF000000)) >> 24 |
	    (i & UINT32_C(0x00FF0000)) >> 8 |
	    (i & UINT32_C(0x0000FF00)) << 8 |
	    (i & UINT32_C(0x000000FF)) << 24;
}

static OF_INLINE uint64_t OF_CONST_FUNC
OFByteSwap64Const(uint64_t i)
{
	return (i & UINT64_C(0xFF00000000000000)) >> 56 |
	    (i & UINT64_C(0x00FF000000000000)) >> 40 |
	    (i & UINT64_C(0x0000FF0000000000)) >> 24 |
	    (i & UINT64_C(0x000000FF00000000)) >> 8 |
	    (i & UINT64_C(0x00000000FF000000)) << 8 |
	    (i & UINT64_C(0x0000000000FF0000)) << 24 |
	    (i & UINT64_C(0x000000000000FF00)) << 40 |
	    (i & UINT64_C(0x00000000000000FF)) << 56;
}

static OF_INLINE uint16_t OF_CONST_FUNC
OFByteSwap16NonConst(uint16_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP16)
	return __builtin_bswap16(i);
#elif (defined(OF_X86_64) || defined(OF_X86)) && defined(__GNUC__)
	__asm__ (
	    "xchgb	%h0, %b0"
	    : "=Q"(i)
	    : "0"(i)
	);
#elif defined(OF_POWERPC) && defined(__GNUC__)
	__asm__ (
	    "lhbrx	%0, 0, %1"
	    : "=r"(i)
	    : "r"(&i), "m"(i)
	);
#elif defined(OF_ARMV6) && defined(__GNUC__)
	__asm__ (
	    "rev16	%0, %0"
	    : "=r"(i)
	    : "0"(i)
	);
#else
	i = (i & UINT16_C(0xFF00)) >> 8 |
	    (i & UINT16_C(0x00FF)) << 8;
#endif
	return i;
}

static OF_INLINE uint32_t OF_CONST_FUNC
OFByteSwap32NonConst(uint32_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP32)
	return __builtin_bswap32(i);
#elif (defined(OF_X86_64) || defined(OF_X86)) && defined(__GNUC__)
	__asm__ (
	    "bswap	%0"
	    : "=q"(i)
	    : "0"(i)
	);
#elif defined(OF_POWERPC) && defined(__GNUC__)
	__asm__ (
	    "lwbrx	%0, 0, %1"
	    : "=r"(i)
	    : "r"(&i), "m"(i)
	);
#elif defined(OF_ARMV6) && defined(__GNUC__)
	__asm__ (
	    "rev	%0, %0"
	    : "=r"(i)
	    : "0"(i)
	);
#else
	i = (i & UINT32_C(0xFF000000)) >> 24 |
	    (i & UINT32_C(0x00FF0000)) >>  8 |
	    (i & UINT32_C(0x0000FF00)) <<  8 |
	    (i & UINT32_C(0x000000FF)) << 24;
#endif
	return i;
}

static OF_INLINE uint64_t OF_CONST_FUNC
OFByteSwap64NonConst(uint64_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP64)
	return __builtin_bswap64(i);
#elif defined(OF_X86_64) && defined(__GNUC__)
	__asm__ (
	    "bswap	%0"
	    : "=r"(i)
	    : "0"(i)
	);
#elif defined(OF_X86) && defined(__GNUC__)
	__asm__ (
	    "bswap	%%eax\n\t"
	    "bswap	%%edx\n\t"
	    "xchgl	%%eax, %%edx"
	    : "=A"(i)
	    : "0"(i)
	);
#else
	i = (uint64_t)OFByteSwap32NonConst(
	    (uint32_t)(i & UINT32_C(0xFFFFFFFF))) << 32 |
	    OFByteSwap32NonConst((uint32_t)(i >> 32));
#endif
	return i;
}

#ifdef __GNUC__
# define OFByteSwap16(i) \
    (__builtin_constant_p(i) ? OFByteSwap16Const(i) : OFByteSwap16NonConst(i))
# define OFByteSwap32(i) \
    (__builtin_constant_p(i) ? OFByteSwap32Const(i) : OFByteSwap32NonConst(i))
# define OFByteSwap64(i) \
    (__builtin_constant_p(i) ? OFByteSwap64Const(i) : OFByteSwap64NonConst(i))
#else
# define OFByteSwap16(i) OFByteSwap16Const(i)
# define OFByteSwap32(i) OFByteSwap32Const(i)
# define OFByteSwap64(i) OFByteSwap64Const(i)
#endif

static OF_INLINE uint32_t
OFFloatToRawUInt32(float f)
{
	uint32_t ret;
	memcpy(&ret, &f, 4);
	return ret;
}

static OF_INLINE float
OFRawUInt32ToFloat(uint32_t uInt32)
{
	float ret;
	memcpy(&ret, &uInt32, 4);
	return ret;
}

static OF_INLINE uint64_t
OFDoubleToRawUInt64(double d)
{
	uint64_t ret;
	memcpy(&ret, &d, 8);
	return ret;
}

static OF_INLINE double
OFRawUInt64ToDouble(uint64_t uInt64)
{
	double ret;
	memcpy(&ret, &uInt64, 8);
	return ret;
}

static OF_INLINE float OF_CONST_FUNC
OFByteSwapFloat(float f)
{
	return OFRawUInt32ToFloat(OFByteSwap32(OFFloatToRawUInt32(f)));
}

static OF_INLINE double OF_CONST_FUNC
OFByteSwapDouble(double d)
{
	return OFRawUInt64ToDouble(OFByteSwap64(OFDoubleToRawUInt64(d)));
}

#ifdef OF_BIG_ENDIAN
# define OFFromBigEndian16(i) (i)
# define OFFromBigEndian32(i) (i)
# define OFFromBigEndian64(i) (i)
# define OFFromLittleEndian16(i) OFByteSwap16(i)
# define OFFromLittleEndian32(i) OFByteSwap32(i)
# define OFFromLittleEndian64(i) OFByteSwap64(i)
# define OFToBigEndian16(i) (i)
# define OFToBigEndian32(i) (i)
# define OFToBigEndian64(i) (i)
# define OFToLittleEndian16(i) OFByteSwap16(i)
# define OFToLittleEndian32(i) OFByteSwap32(i)
# define OFToLittleEndian64(i) OFByteSwap64(i)
#else
# define OFFromBigEndian16(i) OFByteSwap16(i)
# define OFFromBigEndian32(i) OFByteSwap32(i)
# define OFFromBigEndian64(i) OFByteSwap64(i)
# define OFFromLittleEndian16(i) (i)
# define OFFromLittleEndian32(i) (i)
# define OFFromLittleEndian64(i) (i)
# define OFToBigEndian16(i) OFByteSwap16(i)
# define OFToBigEndian32(i) OFByteSwap32(i)
# define OFToBigEndian64(i) OFByteSwap64(i)
# define OFToLittleEndian16(i) (i)
# define OFToLittleEndian32(i) (i)
# define OFToLittleEndian64(i) (i)
#endif

#ifdef OF_FLOAT_BIG_ENDIAN
# define OFFromBigEndianFloat(f) (f)
# define OFFromBigEndianDouble(d) (d)
# define OFFromLittleEndianFloat(f) OFByteSwapFloat(f)
# define OFFromLittleEndianDouble(d) OFByteSwapDouble(d)
# define OFToBigEndianFloat(f) (f)
# define OFToBigEndianDouble(d) (d)
# define OFToLittleEndianFloat(f) OFByteSwapFloat(f)
# define OFToLittleEndianDouble(d) OFByteSwapDouble(d)
#else
# define OFFromBigEndianFloat(f) OFByteSwapFloat(f)
# define OFFromBigEndianDouble(d) OFByteSwapDouble(d)
# define OFFromLittleEndianFloat(f) (f)
# define OFFromLittleEndianDouble(d) (d)
# define OFToBigEndianFloat(f) OFByteSwapFloat(f)
# define OFToBigEndianDouble(d) OFByteSwapDouble(d)
# define OFToLittleEndianFloat(f) (f)
# define OFToLittleEndianDouble(d) (d)
#endif























































































































































#define OFRotateLeft(value, bits)					\
    (((bits) % (sizeof(value) * 8)) > 0					\
    ? ((value) << ((bits) % (sizeof(value) * 8))) |			\
    ((value) >> (sizeof(value) * 8 - ((bits) % (sizeof(value) * 8))))	\
    : (value))
#define OFRotateRight(value, bits)					\
    (((bits) % (sizeof(value) * 8)) > 0					\
    ? ((value) >> ((bits) % (sizeof(value) * 8))) |			\
    ((value) << (sizeof(value) * 8 - ((bits) % (sizeof(value) * 8))))	\
    : (value))

#define OFRoundUpToPowerOf2(pow2, value)	\
    (((value) + (pow2) - 1) & ~((pow2) - 1))























static OF_INLINE bool
OFBitsetIsSet(unsigned char *_Nonnull storage, size_t idx)
{
	return storage[idx / CHAR_BIT] & (1u << (idx % CHAR_BIT));
}

static OF_INLINE void
OFBitsetSet(unsigned char *_Nonnull storage, size_t idx)
{



	storage[idx / CHAR_BIT] |= (1u << (idx % CHAR_BIT));
}

static OF_INLINE void
OFBitsetClear(unsigned char *_Nonnull storage, size_t idx)
{
	storage[idx / CHAR_BIT] &= ~(1u << (idx % CHAR_BIT));
}















static OF_INLINE void
OFZeroMemory(void *_Nonnull buffer_, size_t length)
{
	volatile unsigned char *buffer = (volatile unsigned char *)buffer_;

	while (buffer < (unsigned char *)buffer_ + length)
		*buffer++ = '\0';
}

static OF_INLINE bool
OFASCIIIsAlpha(char c)
{
	return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
}

static OF_INLINE bool
OFASCIIIsDigit(char c)
{
	return (c >= '0' && c <= '9');
}

static OF_INLINE bool
OFASCIIIsAlnum(char c)
{
	return (OFASCIIIsAlpha(c) || OFASCIIIsDigit(c));
}

static OF_INLINE bool
OFASCIIIsSpace(char c)
{
	return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' ||
	    c == '\v');
}

static OF_INLINE char
OFASCIIToUpper(char c)
{
	return (c >= 'a' && c <= 'z' ? 'A' + (c - 'a') : c);
}

static OF_INLINE char
OFASCIIToLower(char c)
{
	return (c >= 'A' && c <= 'Z' ? 'a' + (c - 'A') : c);
}
#endif

Modified src/module.modulemap from [46be0ed236] to [33c24fc753].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
framework module ObjFW {
	umbrella header "ObjFW.h"

	/*
	 * These are included by atomic.h, but should never be included
	 * directly.
	 */
	exclude header "atomic_builtins.h"
	exclude header "atomic_no_threads.h"
	exclude header "atomic_osatomic.h"
	exclude header "atomic_powerpc.h"
	exclude header "atomic_sync_builtins.h"
	exclude header "atomic_x86.h"

	export *
}




|


|
|
|
|
<
|



1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
framework module ObjFW {
	umbrella header "ObjFW.h"

	/*
	 * These are included by OFAtomic.h, but should never be included
	 * directly.
	 */
	exclude header "platform/GCC4/OFAtomic.h"
	exclude header "platform/GCC4.7/OFAtomic.h"
	exclude header "platform/PowerPC/OFAtomic.h"
	exclude header "platform/macOS/OFAtomic.h"

	exclude header "platform/x86/OFAtomic.h"

	export *
}

Modified src/objfw-defs.h.in from [4530c4317b] to [69a2e4862c].

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
#undef INFINITY
#undef LLONG_MAX
#undef LLONG_MIN
#undef OF_APPLE_RUNTIME
#undef OF_BIG_ENDIAN
#undef OF_FLOAT_BIG_ENDIAN

#undef OF_HAVE_ATOMIC_BUILTINS
#undef OF_HAVE_ATOMIC_OPS
#undef OF_HAVE_BUILTIN_BSWAP16
#undef OF_HAVE_BUILTIN_BSWAP32
#undef OF_HAVE_BUILTIN_BSWAP64
#undef OF_HAVE_CHMOD
#undef OF_HAVE_CHOWN
#undef OF_HAVE_FILES
#undef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
#undef OF_HAVE_IPV6
#undef OF_HAVE_IPX
#undef OF_HAVE_LIMITS_H
#undef OF_HAVE_LINK
#undef OF_HAVE_MAX_ALIGN_T
#undef OF_HAVE_NETINET_IN_H
#undef OF_HAVE_NETINET_SCTP_H
#undef OF_HAVE_NETINET_TCP_H
#undef OF_HAVE_NETIPX_IPX_H
#undef OF_HAVE_OSATOMIC
#undef OF_HAVE_OSATOMIC_64
#undef OF_HAVE_PIPE
#undef OF_HAVE_PLEDGE
#undef OF_HAVE_PLUGINS
#undef OF_HAVE_PROCESSES
#undef OF_HAVE_PTHREADS
#undef OF_HAVE_PTHREAD_SPINLOCKS
#undef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES
#undef OF_HAVE_SCHED_YIELD
#undef OF_HAVE_SOCKETS
#undef OF_HAVE_STDNORETURN
#undef OF_HAVE_SYMLINK
#undef OF_HAVE_SYNC_BUILTINS
#undef OF_HAVE_SYS_SOCKET_H
#undef OF_HAVE_SYS_TYPES_H

#undef OF_HAVE_THREADS
#undef OF_HAVE_UNICODE_TABLES

#undef OF_HAVE__THREAD_LOCAL
#undef OF_HAVE___THREAD
#undef OF_NINTENDO_3DS
#undef OF_NINTENDO_DS
#undef OF_NO_SHARED
#undef OF_OBJFW_RUNTIME
#undef OF_UNIVERSAL
#undef OF_WII
#undef SIZE_MAX
#undef UINTPTR_MAX
#undef ULLONG_MAX
#undef __have_longlong64
<
<
<



>















<







|










>


>








<
<
<
<



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







#undef OF_APPLE_RUNTIME
#undef OF_BIG_ENDIAN
#undef OF_FLOAT_BIG_ENDIAN
#undef OF_HAVE_AFUNIX_H
#undef OF_HAVE_ATOMIC_BUILTINS
#undef OF_HAVE_ATOMIC_OPS
#undef OF_HAVE_BUILTIN_BSWAP16
#undef OF_HAVE_BUILTIN_BSWAP32
#undef OF_HAVE_BUILTIN_BSWAP64
#undef OF_HAVE_CHMOD
#undef OF_HAVE_CHOWN
#undef OF_HAVE_FILES
#undef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
#undef OF_HAVE_IPV6
#undef OF_HAVE_IPX
#undef OF_HAVE_LIMITS_H
#undef OF_HAVE_LINK
#undef OF_HAVE_MAX_ALIGN_T
#undef OF_HAVE_NETINET_IN_H

#undef OF_HAVE_NETINET_TCP_H
#undef OF_HAVE_NETIPX_IPX_H
#undef OF_HAVE_OSATOMIC
#undef OF_HAVE_OSATOMIC_64
#undef OF_HAVE_PIPE
#undef OF_HAVE_PLEDGE
#undef OF_HAVE_PLUGINS
#undef OF_HAVE_SUBPROCESSES
#undef OF_HAVE_PTHREADS
#undef OF_HAVE_PTHREAD_SPINLOCKS
#undef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES
#undef OF_HAVE_SCHED_YIELD
#undef OF_HAVE_SOCKETS
#undef OF_HAVE_STDNORETURN
#undef OF_HAVE_SYMLINK
#undef OF_HAVE_SYNC_BUILTINS
#undef OF_HAVE_SYS_SOCKET_H
#undef OF_HAVE_SYS_TYPES_H
#undef OF_HAVE_SYS_UN_H
#undef OF_HAVE_THREADS
#undef OF_HAVE_UNICODE_TABLES
#undef OF_HAVE_UNIX_SOCKETS
#undef OF_HAVE__THREAD_LOCAL
#undef OF_HAVE___THREAD
#undef OF_NINTENDO_3DS
#undef OF_NINTENDO_DS
#undef OF_NO_SHARED
#undef OF_OBJFW_RUNTIME
#undef OF_UNIVERSAL
#undef OF_WII




Modified src/platform.h from [40ddf7ef54] to [927be68827].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
#elif defined(__mips_eabi) && _MIPS_SZPTR == 32
# define OF_MIPS
# define OF_MIPS_EABI
#elif defined(__sparc64__) || (defined(__sparc__) && defined(__arch64__))
# define OF_SPARC64
#elif defined(__sparc__) && !defined(__arch64__)
# define OF_SPARC
#elif defined(__hppa__) || defined(__HPPA__) || \

    defined(_PA_RISC1_0) || defined(_PA_RISC1_1)
# define OF_PA_RISC
#elif defined(__ia64__) || defined(__IA64__)
# define OF_ITANIUM
#elif defined(__m68k__)
# define OF_M68K
# ifdef __mc68060__
#  define OF_M68060







|
>
|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#elif defined(__mips_eabi) && _MIPS_SZPTR == 32
# define OF_MIPS
# define OF_MIPS_EABI
#elif defined(__sparc64__) || (defined(__sparc__) && defined(__arch64__))
# define OF_SPARC64
#elif defined(__sparc__) && !defined(__arch64__)
# define OF_SPARC
#elif defined(__hppa64__) || defined(_PA_RISC2_0)
# define OF_PA_RISC_2_0
#elif defined(__hppa__) || defined(_PA_RISC1_0) || defined(_PA_RISC1_1)
# define OF_PA_RISC
#elif defined(__ia64__) || defined(__IA64__)
# define OF_ITANIUM
#elif defined(__m68k__)
# define OF_M68K
# ifdef __mc68060__
#  define OF_M68060
94
95
96
97
98
99
100


101
102
103
104
105
106
107
# define OF_RISC_V_64
#elif defined(__riscv)
# define OF_RISC_V
#elif defined(__s390x__)
# define OF_S390X
#elif defined(__s390__)
# define OF_S390


#endif

#if defined(__APPLE__)
# include <TargetConditionals.h>
# if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \
    (defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR)
#  define OF_IOS







>
>







93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# define OF_RISC_V_64
#elif defined(__riscv)
# define OF_RISC_V
#elif defined(__s390x__)
# define OF_S390X
#elif defined(__s390__)
# define OF_S390
#elif defined(__e2k__)
# define OF_ELBRUS_2000
#endif

#if defined(__APPLE__)
# include <TargetConditionals.h>
# if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \
    (defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR)
#  define OF_IOS
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
#elif defined(__ANDROID__)
# define OF_ANDROID
#elif defined(__HAIKU__)
# define OF_HAIKU
#elif defined(_AIX)
# define OF_AIX
#elif defined(__MORPHOS__)
# ifndef __ixemul__
#  define OF_MORPHOS
#  define OF_AMIGAOS
# else
#  define OF_MORPHOS_IXEMUL
# endif
#elif defined(__amigaos4__)
# define OF_AMIGAOS4
# define OF_AMIGAOS
#elif defined(__amigaos__)
# define OF_AMIGAOS_M68K
# define OF_AMIGAOS
#elif defined(__sun__)
# define OF_SOLARIS
#elif defined(__QNX__)
# define OF_QNX


#elif defined(_PSP)
# define OF_PSP
#elif defined(__DJGPP__)
# define OF_DJGPP
# define OF_MSDOS
#elif defined(__riscos__)
# define OF_ACORN_RISC_OS


#endif

#if defined(__ELF__)
# define OF_ELF
#elif defined(__MACH__)
# define OF_MACH_O
#endif

#if defined(__PIC__) || defined(__pic__)
# define OF_PIC
#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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#elif defined(__ANDROID__)
# define OF_ANDROID
#elif defined(__HAIKU__)
# define OF_HAIKU
#elif defined(_AIX)
# define OF_AIX
#elif defined(__MORPHOS__)

# define OF_MORPHOS
# define OF_AMIGAOS



#elif defined(__amigaos4__)
# define OF_AMIGAOS4
# define OF_AMIGAOS
#elif defined(__amigaos__)
# define OF_AMIGAOS_M68K
# define OF_AMIGAOS
#elif defined(__sun__)
# define OF_SOLARIS
#elif defined(__QNX__)
# define OF_QNX
#elif defined(__hpux__)
# define OF_HPUX
#elif defined(_PSP)
# define OF_PSP
#elif defined(__DJGPP__)
# define OF_DJGPP
# define OF_MSDOS
#elif defined(__riscos__)
# define OF_ACORN_RISC_OS
#elif defined(__MINT__)
# define OF_MINT
#endif

#if defined(__ELF__)
# define OF_ELF
#elif defined(__MACH__)
# define OF_MACH_O
#endif

#if defined(__PIC__) || defined(__pic__)
# define OF_PIC
#endif

Renamed and modified src/platform/amiga/condition.m [022f3487c0] to src/platform/AmigaOS/OFPlainCondition.m [b17297ed32].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "condition.h"

#include <proto/exec.h>
#include <devices/timer.h>
#ifndef OF_AMIGAOS4
# include <clib/alib_protos.h>
#endif

bool

of_condition_new(of_condition_t *condition)
{
	condition->waitingTasks = NULL;

	return true;
}

bool

of_condition_signal(of_condition_t *condition)
{
	Forbid();
	@try {
		if (condition->waitingTasks == NULL)
			return true;

		Signal(condition->waitingTasks->task,
		    (1ul << condition->waitingTasks->sigBit));

		condition->waitingTasks = condition->waitingTasks->next;
	} @finally {
		Permit();
	}

	return true;
}

bool

of_condition_broadcast(of_condition_t *condition)
{
	Forbid();
	@try {
		if (condition->waitingTasks == NULL)
			return true;

		while (condition->waitingTasks != NULL) {
			Signal(condition->waitingTasks->task,
			    (1ul << condition->waitingTasks->sigBit));

			condition->waitingTasks = condition->waitingTasks->next;
		}
	} @finally {
		Permit();
	}

	return true;
}

bool

of_condition_wait(of_condition_t *condition, of_mutex_t *mutex)
{
	ULONG signalMask = 0;

	return of_condition_wait_or_signal(condition, mutex, &signalMask);
}

bool

of_condition_wait_or_signal(of_condition_t *condition, of_mutex_t *mutex,
    ULONG *signalMask)
{
	struct of_condition_waiting_task waitingTask = {
		.task = FindTask(NULL),
		.sigBit = AllocSignal(-1)
	};
	bool ret;
	ULONG mask;

	if (waitingTask.sigBit == -1) {
		errno = EAGAIN;
		return false;
	}

	Forbid();

	if (!of_mutex_unlock(mutex)) {
		FreeSignal(waitingTask.sigBit);
		return false;
	}

	waitingTask.next = condition->waitingTasks;
	condition->waitingTasks = &waitingTask;

	mask = Wait((1ul << waitingTask.sigBit) | *signalMask);
	if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask))
		ret = of_mutex_lock(mutex);
	else {
		/*
		 * This should not happen - it means something interrupted the
		 * Wait(), so the best we can do is return EINTR.
		 */
		ret = false;
		errno = EINTR;
	}

	FreeSignal(waitingTask.sigBit);

	Permit();

	return ret;
}

bool

of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex,
    of_time_interval_t timeout)
{
	ULONG signalMask = 0;

	return of_condition_timed_wait_or_signal(condition, mutex, timeout,
	    &signalMask);
}

bool

of_condition_timed_wait_or_signal(of_condition_t *condition, of_mutex_t *mutex,
    of_time_interval_t timeout, ULONG *signalMask)
{
	struct of_condition_waiting_task waitingTask = {
		.task = FindTask(NULL),
		.sigBit = AllocSignal(-1)
	};
	struct MsgPort port = {
		.mp_Node = {
			.ln_Type = NT_MSGPORT
		},

<
<
|

















|







<
>
|



|


<
>
|




|









|


<
>
|




|











|


<
>
|



|


<
>
|
|

|



|


|
<
|
<



|

|







|
|




<
|
<





|


<
>
|
|



|



<
>
|
|

|







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


 * 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 "OFPlainCondition.h"

#include <proto/exec.h>
#include <devices/timer.h>
#ifndef OF_AMIGAOS4
# include <clib/alib_protos.h>
#endif


int
OFPlainConditionNew(OFPlainCondition *condition)
{
	condition->waitingTasks = NULL;

	return 0;
}


int
OFPlainConditionSignal(OFPlainCondition *condition)
{
	Forbid();
	@try {
		if (condition->waitingTasks == NULL)
			return 0;

		Signal(condition->waitingTasks->task,
		    (1ul << condition->waitingTasks->sigBit));

		condition->waitingTasks = condition->waitingTasks->next;
	} @finally {
		Permit();
	}

	return 0;
}


int
OFPlainConditionBroadcast(OFPlainCondition *condition)
{
	Forbid();
	@try {
		if (condition->waitingTasks == NULL)
			return 0;

		while (condition->waitingTasks != NULL) {
			Signal(condition->waitingTasks->task,
			    (1ul << condition->waitingTasks->sigBit));

			condition->waitingTasks = condition->waitingTasks->next;
		}
	} @finally {
		Permit();
	}

	return 0;
}


int
OFPlainConditionWait(OFPlainCondition *condition, OFPlainMutex *mutex)
{
	ULONG signalMask = 0;

	return OFPlainConditionWaitOrExecSignal(condition, mutex, &signalMask);
}


int
OFPlainConditionWaitOrExecSignal(OFPlainCondition *condition,
    OFPlainMutex *mutex, ULONG *signalMask)
{
	struct OFPlainConditionWaitingTask waitingTask = {
		.task = FindTask(NULL),
		.sigBit = AllocSignal(-1)
	};
	int error = 0;
	ULONG mask;

	if (waitingTask.sigBit == -1)

		return EAGAIN;


	Forbid();

	if ((error = OFPlainMutexUnlock(mutex)) != 0) {
		FreeSignal(waitingTask.sigBit);
		return error;
	}

	waitingTask.next = condition->waitingTasks;
	condition->waitingTasks = &waitingTask;

	mask = Wait((1ul << waitingTask.sigBit) | *signalMask);
	if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask))
		error = OFPlainMutexLock(mutex);
	else
		/*
		 * This should not happen - it means something interrupted the
		 * Wait(), so the best we can do is return EINTR.
		 */

		error = EINTR;


	FreeSignal(waitingTask.sigBit);

	Permit();

	return error;
}


int
OFPlainConditionTimedWait(OFPlainCondition *condition, OFPlainMutex *mutex,
    OFTimeInterval timeout)
{
	ULONG signalMask = 0;

	return OFPlainConditionTimedWaitOrExecSignal(condition, mutex, timeout,
	    &signalMask);
}


int
OFPlainConditionTimedWaitOrExecSignal(OFPlainCondition *condition,
    OFPlainMutex *mutex, OFTimeInterval timeout, ULONG *signalMask)
{
	struct OFPlainConditionWaitingTask waitingTask = {
		.task = FindTask(NULL),
		.sigBit = AllocSignal(-1)
	};
	struct MsgPort port = {
		.mp_Node = {
			.ln_Type = NT_MSGPORT
		},
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
#else
		.tr_time = {
			.tv_sec = (ULONG)timeout,
			.tv_micro = (timeout - request.tr_time.tv_sec) * 1000000
#endif
		}
	};

	ULONG mask;
	bool ret;

	NewList(&port.mp_MsgList);

	if (waitingTask.sigBit == -1 || port.mp_SigBit == -1) {
		errno = EAGAIN;
		goto fail;
	}

	if (OpenDevice("timer.device", UNIT_MICROHZ,
	    (struct IORequest *)&request, 0) != 0) {
		errno = EAGAIN;
		goto fail;
	}

	Forbid();

	if (!of_mutex_unlock(mutex)) {
		Permit();
		goto fail;
	}

	waitingTask.next = condition->waitingTasks;
	condition->waitingTasks = &waitingTask;

	SendIO((struct IORequest *)&request);

	mask = Wait((1ul << waitingTask.sigBit) | (1ul << port.mp_SigBit) |
	    *signalMask);
	if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask))
		ret = of_mutex_lock(mutex);
	else if (mask & (1ul << port.mp_SigBit)) {
		ret = false;
		errno = ETIMEDOUT;
	} else {
		/*
		 * This should not happen - it means something interrupted the
		 * Wait(), so the best we can do is return EINTR.
		 */
		ret = false;
		errno = EINTR;
	}

	condition->waitingTasks = waitingTask.next;

	if (!CheckIO((struct IORequest *)&request)) {
		AbortIO((struct IORequest *)&request);
		WaitIO((struct IORequest *)&request);
	}
	CloseDevice((struct IORequest *)&request);

	Permit();

	FreeSignal(waitingTask.sigBit);
	FreeSignal(port.mp_SigBit);

	return ret;

fail:
	if (waitingTask.sigBit != -1)
		FreeSignal(waitingTask.sigBit);
	if (port.mp_SigBit != -1)
		FreeSignal(port.mp_SigBit);

	return false;
}

bool

of_condition_free(of_condition_t *condition)
{
	Forbid();
	@try {
		if (condition->waitingTasks != NULL) {
			errno = EBUSY;
			return false;
		}
	} @finally {
		Permit();
	}

	return true;
}







>

<




|





|





|












|
|
<
|
|




<
|
<











<
<
<
<
<






|


<
>
|



|
<
|
<




|

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
#else
		.tr_time = {
			.tv_sec = (ULONG)timeout,
			.tv_micro = (timeout - request.tr_time.tv_sec) * 1000000
#endif
		}
	};
	int error = 0;
	ULONG mask;


	NewList(&port.mp_MsgList);

	if (waitingTask.sigBit == -1 || port.mp_SigBit == -1) {
		error = EAGAIN;
		goto fail;
	}

	if (OpenDevice("timer.device", UNIT_MICROHZ,
	    (struct IORequest *)&request, 0) != 0) {
		error = EAGAIN;
		goto fail;
	}

	Forbid();

	if ((error = OFPlainMutexUnlock(mutex)) != 0) {
		Permit();
		goto fail;
	}

	waitingTask.next = condition->waitingTasks;
	condition->waitingTasks = &waitingTask;

	SendIO((struct IORequest *)&request);

	mask = Wait((1ul << waitingTask.sigBit) | (1ul << port.mp_SigBit) |
	    *signalMask);
	if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask))
		error = OFPlainMutexLock(mutex);
	else if (mask & (1ul << port.mp_SigBit))

		error = ETIMEDOUT;
	else
		/*
		 * This should not happen - it means something interrupted the
		 * Wait(), so the best we can do is return EINTR.
		 */

		error = EINTR;


	condition->waitingTasks = waitingTask.next;

	if (!CheckIO((struct IORequest *)&request)) {
		AbortIO((struct IORequest *)&request);
		WaitIO((struct IORequest *)&request);
	}
	CloseDevice((struct IORequest *)&request);

	Permit();






fail:
	if (waitingTask.sigBit != -1)
		FreeSignal(waitingTask.sigBit);
	if (port.mp_SigBit != -1)
		FreeSignal(port.mp_SigBit);

	return error;
}


int
OFPlainConditionFree(OFPlainCondition *condition)
{
	Forbid();
	@try {
		if (condition->waitingTasks != NULL)

			return EBUSY;

	} @finally {
		Permit();
	}

	return 0;
}

Renamed and modified src/platform/amiga/mutex.m [c8d24f47bf] to src/platform/AmigaOS/OFPlainMutex.m [635aa59c59].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "mutex.h"

#include <proto/exec.h>

bool

of_mutex_new(of_mutex_t *mutex)
{
	InitSemaphore(mutex);

	return true;
}

bool

of_mutex_lock(of_mutex_t *mutex)
{
	ObtainSemaphore(mutex);

	return true;
}

bool

of_mutex_trylock(of_mutex_t *mutex)
{
	if (!AttemptSemaphore(mutex)) {
		errno = EBUSY;
		return false;
	}

	return true;
}

bool

of_mutex_unlock(of_mutex_t *mutex)
{
	ReleaseSemaphore(mutex);

	return true;
}

bool

of_mutex_free(of_mutex_t *mutex)
{
	return true;
}

bool

of_rmutex_new(of_rmutex_t *rmutex)
{
	return of_mutex_new(rmutex);
}

bool

of_rmutex_lock(of_rmutex_t *rmutex)
{
	return of_mutex_lock(rmutex);
}

bool

of_rmutex_trylock(of_rmutex_t *rmutex)
{
	return of_mutex_trylock(rmutex);
}

bool

of_rmutex_unlock(of_rmutex_t *rmutex)
{
	return of_mutex_unlock(rmutex);
}

bool

of_rmutex_free(of_rmutex_t *rmutex)
{
	return of_mutex_free(rmutex);
}

<
<
|

















|



<
>
|



|


<
>
|



|


<
>
|

|
<
|
|
<
|


<
>
|



|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|

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

#include "config.h"

#include <errno.h>

#import "OFPlainMutex.h"

#include <proto/exec.h>


int
OFPlainMutexNew(OFPlainMutex *mutex)
{
	InitSemaphore(mutex);

	return 0;
}


int
OFPlainMutexLock(OFPlainMutex *mutex)
{
	ObtainSemaphore(mutex);

	return 0;
}


int
OFPlainMutexTryLock(OFPlainMutex *mutex)
{
	if (!AttemptSemaphore(mutex))

		return EBUSY;


	return 0;
}


int
OFPlainMutexUnlock(OFPlainMutex *mutex)
{
	ReleaseSemaphore(mutex);

	return 0;
}


int
OFPlainMutexFree(OFPlainMutex *mutex)
{
	return 0;
}


int
OFPlainRecursiveMutexNew(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexNew(rmutex);
}


int
OFPlainRecursiveMutexLock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexLock(rmutex);
}


int
OFPlainRecursiveMutexTryLock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexTryLock(rmutex);
}


int
OFPlainRecursiveMutexUnlock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexUnlock(rmutex);
}


int
OFPlainRecursiveMutexFree(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexFree(rmutex);
}

Renamed and modified src/platform/amiga/thread.m [de7e15e20a] to src/platform/AmigaOS/OFPlainThread.m [70ea6c25e9].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <assert.h>
#include <errno.h>

#import "OFData.h"

#import "thread.h"
#import "tlskey.h"

#include <dos/dostags.h>
#include <proto/dos.h>
#include <proto/exec.h>


extern void of_tlskey_thread_exited(void);

static of_tlskey_t threadKey;

OF_CONSTRUCTOR()
{
	OF_ENSURE(of_tlskey_new(&threadKey));
}

static void
functionWrapper(void)
{
	bool detached = false;
	of_thread_t thread =
	    (of_thread_t)((struct Process *)FindTask(NULL))->pr_ExitData;
	OF_ENSURE(of_tlskey_set(threadKey, thread));

	thread->function(thread->object);

	ObtainSemaphore(&thread->semaphore);
	@try {
		thread->done = true;


		of_tlskey_thread_exited();


		if (thread->detached)
			detached = true;
		else if (thread->joinTask != NULL)
			Signal(thread->joinTask, (1ul << thread->joinSigBit));
	} @finally {
		ReleaseSemaphore(&thread->semaphore);
	}

	if (detached)
		free(thread);
}

bool

of_thread_attr_init(of_thread_attr_t *attr)
{
	attr->priority = 0;
	attr->stackSize = 0;

	return true;
}

bool

of_thread_new(of_thread_t *thread, const char *name, void (*function)(id),
    id object, const of_thread_attr_t *attr)
{
	OFMutableData *tags = nil;

	if ((*thread = calloc(1, sizeof(**thread))) == NULL) {
		errno = ENOMEM;
		return false;
	}

	@try {
		(*thread)->function = function;
		(*thread)->object = object;
		InitSemaphore(&(*thread)->semaphore);

		tags = [[OFMutableData alloc]

<
<
|


















|
<
|
|





>
|
>
|



|






|
|
|







>
|
>













<
>
|




|


<
>
|
|



|
<
|
<







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

#include "config.h"

#include <assert.h>
#include <errno.h>

#import "OFPlainThread.h"

#import "OFData.h"
#import "OFTLSKey.h"

#include <dos/dostags.h>
#include <proto/dos.h>
#include <proto/exec.h>

#ifndef OF_MORPHOS
extern void OFTLSKeyThreadExited(void);
#endif
static OFTLSKey threadKey;

OF_CONSTRUCTOR()
{
	OFEnsure(OFTLSKeyNew(&threadKey) == 0);
}

static void
functionWrapper(void)
{
	bool detached = false;
	OFPlainThread thread =
	    (OFPlainThread)((struct Process *)FindTask(NULL))->pr_ExitData;
	OFEnsure(OFTLSKeySet(threadKey, thread) == 0);

	thread->function(thread->object);

	ObtainSemaphore(&thread->semaphore);
	@try {
		thread->done = true;

#ifndef OF_MORPHOS
		OFTLSKeyThreadExited();
#endif

		if (thread->detached)
			detached = true;
		else if (thread->joinTask != NULL)
			Signal(thread->joinTask, (1ul << thread->joinSigBit));
	} @finally {
		ReleaseSemaphore(&thread->semaphore);
	}

	if (detached)
		free(thread);
}


int
OFPlainThreadAttributesInit(OFPlainThreadAttributes *attr)
{
	attr->priority = 0;
	attr->stackSize = 0;

	return 0;
}


int
OFPlainThreadNew(OFPlainThread *thread, const char *name, void (*function)(id),
    id object, const OFPlainThreadAttributes *attr)
{
	OFMutableData *tags = nil;

	if ((*thread = calloc(1, sizeof(**thread))) == NULL)

		return ENOMEM;


	@try {
		(*thread)->function = function;
		(*thread)->object = object;
		InitSemaphore(&(*thread)->semaphore);

		tags = [[OFMutableData alloc]
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
		ADD_TAG(NP_Output, ((struct Process *)FindTask(NULL))->pr_COS)
		ADD_TAG(NP_Error, ((struct Process *)FindTask(NULL))->pr_CES)
		ADD_TAG(NP_CloseInput, FALSE)
		ADD_TAG(NP_CloseOutput, FALSE)
		ADD_TAG(NP_CloseError, FALSE)

		if (attr != NULL && attr->priority != 0) {
			if (attr->priority < 1 || attr->priority > 1) {
				errno = EINVAL;
				return false;
			}

			/*
			 * -1 should be -128 (lowest possible priority) while
			 * +1 should be +127 (highest possible priority).
			 */
			ADD_TAG(NP_Priority, (attr->priority > 0
			    ? attr->priority * 127 : attr->priority * 128))







|
<
|
<







115
116
117
118
119
120
121
122

123

124
125
126
127
128
129
130
		ADD_TAG(NP_Output, ((struct Process *)FindTask(NULL))->pr_COS)
		ADD_TAG(NP_Error, ((struct Process *)FindTask(NULL))->pr_CES)
		ADD_TAG(NP_CloseInput, FALSE)
		ADD_TAG(NP_CloseOutput, FALSE)
		ADD_TAG(NP_CloseError, FALSE)

		if (attr != NULL && attr->priority != 0) {
			if (attr->priority < 1 || attr->priority > 1)

				return EINVAL;


			/*
			 * -1 should be -128 (lowest possible priority) while
			 * +1 should be +127 (highest possible priority).
			 */
			ADD_TAG(NP_Priority, (attr->priority > 0
			    ? attr->priority * 127 : attr->priority * 128))
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

		ADD_TAG(TAG_DONE, 0)
#undef ADD_TAG

		(*thread)->task = (struct Task *)CreateNewProc(tags.items);
		if ((*thread)->task == NULL) {
			free(*thread);
			errno = EAGAIN;
			return false;
		}
	} @catch (id e) {
		free(*thread);
		@throw e;
	} @finally {
		[tags release];
	}

	return true;
}

of_thread_t
of_thread_current(void)
{
	return of_tlskey_get(threadKey);
}

bool




of_thread_join(of_thread_t thread)


{
	ObtainSemaphore(&thread->semaphore);

	if (thread->done) {
		ReleaseSemaphore(&thread->semaphore);

		free(thread);
		return true;
	}

	@try {
		if (thread->detached || thread->joinTask != NULL) {
			errno = EINVAL;
			return false;
		}

		if ((thread->joinSigBit = AllocSignal(-1)) == -1) {
			errno = EAGAIN;
			return false;
		}

		thread->joinTask = FindTask(NULL);
	} @finally {
		ReleaseSemaphore(&thread->semaphore);
	}

	Wait(1ul << thread->joinSigBit);
	FreeSignal(thread->joinSigBit);

	assert(thread->done);
	free(thread);

	return true;
}

bool

of_thread_detach(of_thread_t thread)
{
	ObtainSemaphore(&thread->semaphore);

	if (thread->done)
		free(thread);
	else
		thread->detached = true;

	ReleaseSemaphore(&thread->semaphore);

	return true;
}

void
of_thread_set_name(const char *name)
{
}







<
|








|


|
|

|



>
>
>
>
|
>
>







|



|
<
|
|
<
|
<
|
<












|


<
>
|










|



|


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

		ADD_TAG(TAG_DONE, 0)
#undef ADD_TAG

		(*thread)->task = (struct Task *)CreateNewProc(tags.items);
		if ((*thread)->task == NULL) {
			free(*thread);

			return EAGAIN;
		}
	} @catch (id e) {
		free(*thread);
		@throw e;
	} @finally {
		[tags release];
	}

	return 0;
}

OFPlainThread
OFCurrentPlainThread(void)
{
	return OFTLSKeyGet(threadKey);
}

bool
OFPlainThreadIsCurrent(OFPlainThread thread)
{
	return (thread->task == FindTask(NULL));
}

int
OFPlainThreadJoin(OFPlainThread thread)
{
	ObtainSemaphore(&thread->semaphore);

	if (thread->done) {
		ReleaseSemaphore(&thread->semaphore);

		free(thread);
		return 0;
	}

	@try {
		if (thread->detached || thread->joinTask != NULL)

			return EINVAL;


		if ((thread->joinSigBit = AllocSignal(-1)) == -1)

			return EAGAIN;


		thread->joinTask = FindTask(NULL);
	} @finally {
		ReleaseSemaphore(&thread->semaphore);
	}

	Wait(1ul << thread->joinSigBit);
	FreeSignal(thread->joinSigBit);

	assert(thread->done);
	free(thread);

	return 0;
}


int
OFPlainThreadDetach(OFPlainThread thread)
{
	ObtainSemaphore(&thread->semaphore);

	if (thread->done)
		free(thread);
	else
		thread->detached = true;

	ReleaseSemaphore(&thread->semaphore);

	return 0;
}

void
OFSetThreadName(const char *name)
{
}

Renamed and modified src/platform/amiga/OFString+PathAdditions.m [6f79ae7bc0] to src/platform/AmigaOS/OFString+PathAdditions.m [8907c31289].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret, *fileName;
	size_t pos;

	fileName = self.lastPathComponent;
	pos = [fileName rangeOfString: @"."
			      options: OF_STRING_SEARCH_BACKWARDS].location;
	if (pos == OF_NOT_FOUND || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return @"";
	}

	ret = [fileName substringFromIndex: pos + 1];

	[ret retain];







|
|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret, *fileName;
	size_t pos;

	fileName = self.lastPathComponent;
	pos = [fileName rangeOfString: @"."
			      options: OFStringSearchBackwards].location;
	if (pos == OFNotFound || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return @"";
	}

	ret = [fileName substringFromIndex: pos + 1];

	[ret retain];
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
		}

		objc_autoreleasePoolPop(pool);
		return @"";
	}

	components = [components objectsInRange:
	    of_range(0, components.count - 1)];
	ret = [OFString pathWithComponents: components];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}








|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
		}

		objc_autoreleasePoolPop(pool);
		return @"";
	}

	components = [components objectsInRange:
	    OFRangeMake(0, components.count - 1)];
	ret = [OFString pathWithComponents: components];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
		return [[self copy] autorelease];

	pool = objc_autoreleasePoolPush();
	components = [[self.pathComponents mutableCopy] autorelease];
	fileName = components.lastObject;

	pos = [fileName rangeOfString: @"."
			      options: OF_STRING_SEARCH_BACKWARDS].location;
	if (pos == OF_NOT_FOUND || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return [[self copy] autorelease];
	}

	fileName = [fileName substringToIndex: pos];
	[components replaceObjectAtIndex: components.count - 1
			      withObject: fileName];







|
|







174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
		return [[self copy] autorelease];

	pool = objc_autoreleasePoolPush();
	components = [[self.pathComponents mutableCopy] autorelease];
	fileName = components.lastObject;

	pos = [fileName rangeOfString: @"."
			      options: OFStringSearchBackwards].location;
	if (pos == OFNotFound || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return [[self copy] autorelease];
	}

	fileName = [fileName substringToIndex: pos];
	[components replaceObjectAtIndex: components.count - 1
			      withObject: fileName];
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
				done = false;
				break;
			}

			if ([component isEqual: @"/"] &&
			    parent != nil && ![parent isEqual: @"/"]) {
				[array removeObjectsInRange:
				    of_range(i - 1, 2)];

				done = false;
				break;
			}
		}
	}








|







231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
				done = false;
				break;
			}

			if ([component isEqual: @"/"] &&
			    parent != nil && ![parent isEqual: @"/"]) {
				[array removeObjectsInRange:
				    OFRangeMake(i - 1, 2)];

				done = false;
				break;
			}
		}
	}

322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
			count--;

			i--;
			continue;
		}

		if ([component isEqual: @".."])
			[components replaceObjectAtIndex: i
					      withObject: @"/"];
	}

	return [OFString pathWithComponents: components];
}

- (OFString *)of_pathComponentToURLPathComponent
{
	return self;
}
@end







|
<










320
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335
336
337
			count--;

			i--;
			continue;
		}

		if ([component isEqual: @".."])
			[components replaceObjectAtIndex: i withObject: @"/"];

	}

	return [OFString pathWithComponents: components];
}

- (OFString *)of_pathComponentToURLPathComponent
{
	return self;
}
@end

Renamed and modified src/platform/amiga/tlskey.m [c1bdeb2ced] to src/platform/AmigaOS/OFTLSKey.m [7115be10da].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "tlskey.h"

#include <exec/semaphores.h>
#include <proto/exec.h>

/*
 * As we use this file in both the runtime and ObjFW, and since AmigaOS always
 * has the runtime, use the hashtable from the runtime.
 */
#import "runtime/private.h"

static of_tlskey_t firstKey = NULL, lastKey = NULL;
static struct SignalSemaphore semaphore;
static bool semaphoreInitialized = false;

static uint32_t
hashFunc(const void *ptr)
{
	return (uint32_t)(uintptr_t)ptr;

<
<
|















|










|







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


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

#import "OFTLSKey.h"

#include <exec/semaphores.h>
#include <proto/exec.h>

/*
 * As we use this file in both the runtime and ObjFW, and since AmigaOS always
 * has the runtime, use the hashtable from the runtime.
 */
#import "runtime/private.h"

static OFTLSKey firstKey = NULL, lastKey = NULL;
static struct SignalSemaphore semaphore;
static bool semaphoreInitialized = false;

static uint32_t
hashFunc(const void *ptr)
{
	return (uint32_t)(uintptr_t)ptr;
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
{
	if (!semaphoreInitialized) {
		InitSemaphore(&semaphore);
		semaphoreInitialized = true;
	}
}

bool

of_tlskey_new(of_tlskey_t *key)
{
	if (!semaphoreInitialized) {
		/*
		 * We might be called from another constructor, while ours has
		 * not run yet. This is safe, as the constructor is definitely
		 * run before a thread is spawned.
		 */
		InitSemaphore(&semaphore);
		semaphoreInitialized = true;
	}

	if ((*key = malloc(sizeof(**key))) == NULL)
		return false;

	(*key)->table = NULL;

	ObtainSemaphore(&semaphore);
	@try {
		(*key)->next = NULL;
		(*key)->previous = lastKey;

		if (lastKey != NULL)
			lastKey->next = *key;

		lastKey = *key;

		if (firstKey == NULL)
			firstKey = *key;
	} @finally {
		ReleaseSemaphore(&semaphore);
	}

	/* We create the hash table lazily. */
	return true;
}

bool

of_tlskey_free(of_tlskey_t key)
{
	ObtainSemaphore(&semaphore);
	@try {
		if (key->previous != NULL)
			key->previous->next = key->next;
		if (key->next != NULL)
			key->next->previous = key->previous;

		if (firstKey == key)
			firstKey = key->next;
		if (lastKey == key)
			lastKey = key->previous;

		objc_hashtable_free(key->table);
		free(key);
	} @finally {
		ReleaseSemaphore(&semaphore);
	}

	return true;
}

void *
of_tlskey_get(of_tlskey_t key)
{
	void *ret;

	ObtainSemaphore(&semaphore);
	@try {
		if (key->table == NULL)
			return NULL;

		ret = objc_hashtable_get(key->table, FindTask(NULL));
	} @finally {
		ReleaseSemaphore(&semaphore);
	}

	return ret;
}

bool

of_tlskey_set(of_tlskey_t key, void *ptr)
{
	ObtainSemaphore(&semaphore);
	@try {
		struct Task *task = FindTask(NULL);

		if (key->table == NULL)
			key->table = objc_hashtable_new(hashFunc, equalFunc, 2);

		if (ptr == NULL)
			objc_hashtable_delete(key->table, task);
		else
			objc_hashtable_set(key->table, task, ptr);
	} @catch (id e) {
		return false;
	} @finally {
		ReleaseSemaphore(&semaphore);
	}

	return true;
}

void
of_tlskey_thread_exited(void)
{
	ObtainSemaphore(&semaphore);
	@try {
		struct Task *task = FindTask(NULL);

		for (of_tlskey_t iter = firstKey; iter != NULL;
		    iter = iter->next)
			if (iter->table != NULL)
				objc_hashtable_delete(iter->table, task);
	} @finally {
		ReleaseSemaphore(&semaphore);
	}
}







<
>
|












|




















|


<
>
|



















|



|
















<
>
|












<
<




|



|





|
<






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
{
	if (!semaphoreInitialized) {
		InitSemaphore(&semaphore);
		semaphoreInitialized = true;
	}
}


int
OFTLSKeyNew(OFTLSKey *key)
{
	if (!semaphoreInitialized) {
		/*
		 * We might be called from another constructor, while ours has
		 * not run yet. This is safe, as the constructor is definitely
		 * run before a thread is spawned.
		 */
		InitSemaphore(&semaphore);
		semaphoreInitialized = true;
	}

	if ((*key = malloc(sizeof(**key))) == NULL)
		return ENOMEM;

	(*key)->table = NULL;

	ObtainSemaphore(&semaphore);
	@try {
		(*key)->next = NULL;
		(*key)->previous = lastKey;

		if (lastKey != NULL)
			lastKey->next = *key;

		lastKey = *key;

		if (firstKey == NULL)
			firstKey = *key;
	} @finally {
		ReleaseSemaphore(&semaphore);
	}

	/* We create the hash table lazily. */
	return 0;
}


int
OFTLSKeyFree(OFTLSKey key)
{
	ObtainSemaphore(&semaphore);
	@try {
		if (key->previous != NULL)
			key->previous->next = key->next;
		if (key->next != NULL)
			key->next->previous = key->previous;

		if (firstKey == key)
			firstKey = key->next;
		if (lastKey == key)
			lastKey = key->previous;

		objc_hashtable_free(key->table);
		free(key);
	} @finally {
		ReleaseSemaphore(&semaphore);
	}

	return 0;
}

void *
OFTLSKeyGet(OFTLSKey key)
{
	void *ret;

	ObtainSemaphore(&semaphore);
	@try {
		if (key->table == NULL)
			return NULL;

		ret = objc_hashtable_get(key->table, FindTask(NULL));
	} @finally {
		ReleaseSemaphore(&semaphore);
	}

	return ret;
}


int
OFTLSKeySet(OFTLSKey key, void *ptr)
{
	ObtainSemaphore(&semaphore);
	@try {
		struct Task *task = FindTask(NULL);

		if (key->table == NULL)
			key->table = objc_hashtable_new(hashFunc, equalFunc, 2);

		if (ptr == NULL)
			objc_hashtable_delete(key->table, task);
		else
			objc_hashtable_set(key->table, task, ptr);


	} @finally {
		ReleaseSemaphore(&semaphore);
	}

	return 0;
}

void
OFTLSKeyThreadExited(void)
{
	ObtainSemaphore(&semaphore);
	@try {
		struct Task *task = FindTask(NULL);

		for (OFTLSKey iter = firstKey; iter != NULL; iter = iter->next)

			if (iter->table != NULL)
				objc_hashtable_delete(iter->table, task);
	} @finally {
		ReleaseSemaphore(&semaphore);
	}
}

Renamed and modified src/atomic_builtins.h [249a6da122] to src/platform/GCC4.7/OFAtomic.h [3a2198950d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

static OF_INLINE int
of_atomic_int_add(volatile int *_Nonnull p, int i)
{
	return __atomic_add_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE int32_t
of_atomic_int32_add(volatile int32_t *_Nonnull p, int32_t i)
{
	return __atomic_add_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE void *_Nullable
of_atomic_ptr_add(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return __atomic_add_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE int
of_atomic_int_sub(volatile int *_Nonnull p, int i)
{
	return __atomic_sub_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE int32_t
of_atomic_int32_sub(volatile int32_t *_Nonnull p, int32_t i)
{
	return __atomic_sub_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE void *_Nullable
of_atomic_ptr_sub(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return __atomic_sub_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE int
of_atomic_int_inc(volatile int *_Nonnull p)
{
	return __atomic_add_fetch(p, 1, __ATOMIC_RELAXED);
}

static OF_INLINE int32_t
of_atomic_int32_inc(volatile int32_t *_Nonnull p)
{
	return __atomic_add_fetch(p, 1, __ATOMIC_RELAXED);
}

static OF_INLINE int
of_atomic_int_dec(volatile int *_Nonnull p)
{
	return __atomic_sub_fetch(p, 1, __ATOMIC_RELAXED);
}

static OF_INLINE int32_t
of_atomic_int32_dec(volatile int32_t *_Nonnull p)
{
	return __atomic_sub_fetch(p, 1, __ATOMIC_RELAXED);
}

static OF_INLINE unsigned int
of_atomic_int_or(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __atomic_or_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE uint32_t
of_atomic_int32_or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __atomic_or_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE unsigned int
of_atomic_int_and(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __atomic_and_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE uint32_t
of_atomic_int32_and(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __atomic_and_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE unsigned int
of_atomic_int_xor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __atomic_xor_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE uint32_t
of_atomic_int32_xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __atomic_xor_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE bool
of_atomic_int_cmpswap(volatile int *_Nonnull p, int o, int n)







{
	return __atomic_compare_exchange(p, &o, &n, false,
	    __ATOMIC_RELAXED, __ATOMIC_RELAXED);
}

static OF_INLINE bool
of_atomic_int32_cmpswap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	return __atomic_compare_exchange(p, &o, &n, false,
	    __ATOMIC_RELAXED, __ATOMIC_RELAXED);
}

static OF_INLINE bool
of_atomic_ptr_cmpswap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	return __atomic_compare_exchange(p, &o, &n, false,
	    __ATOMIC_RELAXED, __ATOMIC_RELAXED);
}

static OF_INLINE void
of_memory_barrier_full(void)
{
	__atomic_thread_fence(__ATOMIC_SEQ_CST);
}

static OF_INLINE void
of_memory_barrier_acquire(void)
{
	__atomic_thread_fence(__ATOMIC_ACQUIRE);
}

static OF_INLINE void
of_memory_barrier_release(void)
{
	__atomic_thread_fence(__ATOMIC_RELEASE);
}

<
<
|














|





|





|





|





|





|





|





|





|





|





|





|





|





|





|





|





|
>
>
>
>
>
>
>






<
<
<
<
<
<
<
|







|





|





|



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


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

static OF_INLINE int
OFAtomicIntAdd(volatile int *_Nonnull p, int i)
{
	return __atomic_add_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE int32_t
OFAtomicInt32Add(volatile int32_t *_Nonnull p, int32_t i)
{
	return __atomic_add_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE void *_Nullable
OFAtomicPointerAdd(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return __atomic_add_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE int
OFAtomicIntSubtract(volatile int *_Nonnull p, int i)
{
	return __atomic_sub_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE int32_t
OFAtomicInt32Subtract(volatile int32_t *_Nonnull p, int32_t i)
{
	return __atomic_sub_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE void *_Nullable
OFAtomicPointerSubtract(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return __atomic_sub_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE int
OFAtomicIntIncrease(volatile int *_Nonnull p)
{
	return __atomic_add_fetch(p, 1, __ATOMIC_RELAXED);
}

static OF_INLINE int32_t
OFAtomicInt32Increase(volatile int32_t *_Nonnull p)
{
	return __atomic_add_fetch(p, 1, __ATOMIC_RELAXED);
}

static OF_INLINE int
OFAtomicIntDecrease(volatile int *_Nonnull p)
{
	return __atomic_sub_fetch(p, 1, __ATOMIC_RELAXED);
}

static OF_INLINE int32_t
OFAtomicInt32Decrease(volatile int32_t *_Nonnull p)
{
	return __atomic_sub_fetch(p, 1, __ATOMIC_RELAXED);
}

static OF_INLINE unsigned int
OFAtomicIntOr(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __atomic_or_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE uint32_t
OFAtomicInt32Or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __atomic_or_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE unsigned int
OFAtomicIntAnd(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __atomic_and_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE uint32_t
OFAtomicInt32And(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __atomic_and_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE unsigned int
OFAtomicIntXor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __atomic_xor_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE uint32_t
OFAtomicInt32Xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __atomic_xor_fetch(p, i, __ATOMIC_RELAXED);
}

static OF_INLINE bool
OFAtomicIntCompareAndSwap(volatile int *_Nonnull p, int o, int n)
{
	return __atomic_compare_exchange(p, &o, &n, false,
	    __ATOMIC_RELAXED, __ATOMIC_RELAXED);
}

static OF_INLINE bool
OFAtomicInt32CompareAndSwap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	return __atomic_compare_exchange(p, &o, &n, false,
	    __ATOMIC_RELAXED, __ATOMIC_RELAXED);
}

static OF_INLINE bool







OFAtomicPointerCompareAndSwap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	return __atomic_compare_exchange(p, &o, &n, false,
	    __ATOMIC_RELAXED, __ATOMIC_RELAXED);
}

static OF_INLINE void
OFMemoryBarrier(void)
{
	__atomic_thread_fence(__ATOMIC_SEQ_CST);
}

static OF_INLINE void
OFAcquireMemoryBarrier(void)
{
	__atomic_thread_fence(__ATOMIC_ACQUIRE);
}

static OF_INLINE void
OFReleaseMemoryBarrier(void)
{
	__atomic_thread_fence(__ATOMIC_RELEASE);
}

Renamed and modified src/atomic_sync_builtins.h [9b099e15cf] to src/platform/GCC4/OFAtomic.h [46ae36c82b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

static OF_INLINE int
of_atomic_int_add(volatile int *_Nonnull p, int i)
{
	return __sync_add_and_fetch(p, i);
}

static OF_INLINE int32_t
of_atomic_int32_add(volatile int32_t *_Nonnull p, int32_t i)
{
	return __sync_add_and_fetch(p, i);
}

static OF_INLINE void *_Nullable
of_atomic_ptr_add(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return __sync_add_and_fetch(p, (void *)i);
}

static OF_INLINE int
of_atomic_int_sub(volatile int *_Nonnull p, int i)
{
	return __sync_sub_and_fetch(p, i);
}

static OF_INLINE int32_t
of_atomic_int32_sub(volatile int32_t *_Nonnull p, int32_t i)
{
	return __sync_sub_and_fetch(p, i);
}

static OF_INLINE void *_Nullable
of_atomic_ptr_sub(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return __sync_sub_and_fetch(p, (void *)i);
}

static OF_INLINE int
of_atomic_int_inc(volatile int *_Nonnull p)
{
	return __sync_add_and_fetch(p, 1);
}

static OF_INLINE int32_t
of_atomic_int32_inc(volatile int32_t *_Nonnull p)
{
	return __sync_add_and_fetch(p, 1);
}

static OF_INLINE int
of_atomic_int_dec(volatile int *_Nonnull p)
{
	return __sync_sub_and_fetch(p, 1);
}

static OF_INLINE int32_t
of_atomic_int32_dec(volatile int32_t *_Nonnull p)
{
	return __sync_sub_and_fetch(p, 1);
}

static OF_INLINE unsigned int
of_atomic_int_or(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __sync_or_and_fetch(p, i);
}

static OF_INLINE uint32_t
of_atomic_int32_or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __sync_or_and_fetch(p, i);
}

static OF_INLINE unsigned int
of_atomic_int_and(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __sync_and_and_fetch(p, i);
}

static OF_INLINE uint32_t
of_atomic_int32_and(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __sync_and_and_fetch(p, i);
}

static OF_INLINE unsigned int
of_atomic_int_xor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __sync_xor_and_fetch(p, i);
}

static OF_INLINE uint32_t
of_atomic_int32_xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __sync_xor_and_fetch(p, i);
}

static OF_INLINE bool
of_atomic_int_cmpswap(volatile int *_Nonnull p, int o, int n)
{
	return __sync_bool_compare_and_swap(p, o, n);
}

static OF_INLINE bool
of_atomic_int32_cmpswap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	return __sync_bool_compare_and_swap(p, o, n);
}

static OF_INLINE bool
of_atomic_ptr_cmpswap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	return __sync_bool_compare_and_swap(p, o, n);
}

static OF_INLINE void
of_memory_barrier(void)
{
	__sync_synchronize();
}

static OF_INLINE void
of_memory_barrier_acquire(void)
{
	__sync_synchronize();
}

static OF_INLINE void
of_memory_barrier_release(void)
{
	__sync_synchronize();
}

<
<
|














|





|





|





|





|





|





|





|





|





|





|





|





|





|





|





|





|





|





|






|





|





|



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


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

static OF_INLINE int
OFAtomicIntAdd(volatile int *_Nonnull p, int i)
{
	return __sync_add_and_fetch(p, i);
}

static OF_INLINE int32_t
OFAtomicInt32Add(volatile int32_t *_Nonnull p, int32_t i)
{
	return __sync_add_and_fetch(p, i);
}

static OF_INLINE void *_Nullable
OFAtomicPointerAdd(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return __sync_add_and_fetch(p, (void *)i);
}

static OF_INLINE int
OFAtomicIntSubtract(volatile int *_Nonnull p, int i)
{
	return __sync_sub_and_fetch(p, i);
}

static OF_INLINE int32_t
OFAtomicInt32Subtract(volatile int32_t *_Nonnull p, int32_t i)
{
	return __sync_sub_and_fetch(p, i);
}

static OF_INLINE void *_Nullable
OFAtomicPointerSubtract(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	return __sync_sub_and_fetch(p, (void *)i);
}

static OF_INLINE int
OFAtomicIntIncrease(volatile int *_Nonnull p)
{
	return __sync_add_and_fetch(p, 1);
}

static OF_INLINE int32_t
OFAtomicInt32Increase(volatile int32_t *_Nonnull p)
{
	return __sync_add_and_fetch(p, 1);
}

static OF_INLINE int
OFAtomicIntDecrease(volatile int *_Nonnull p)
{
	return __sync_sub_and_fetch(p, 1);
}

static OF_INLINE int32_t
OFAtomicInt32Decrease(volatile int32_t *_Nonnull p)
{
	return __sync_sub_and_fetch(p, 1);
}

static OF_INLINE unsigned int
OFAtomicIntOr(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __sync_or_and_fetch(p, i);
}

static OF_INLINE uint32_t
OFAtomicInt32Or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __sync_or_and_fetch(p, i);
}

static OF_INLINE unsigned int
OFAtomicIntAnd(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __sync_and_and_fetch(p, i);
}

static OF_INLINE uint32_t
OFAtomicInt32And(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __sync_and_and_fetch(p, i);
}

static OF_INLINE unsigned int
OFAtomicIntXor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return __sync_xor_and_fetch(p, i);
}

static OF_INLINE uint32_t
OFAtomicInt32Xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return __sync_xor_and_fetch(p, i);
}

static OF_INLINE bool
OFAtomicIntCompareAndSwap(volatile int *_Nonnull p, int o, int n)
{
	return __sync_bool_compare_and_swap(p, o, n);
}

static OF_INLINE bool
OFAtomicInt32CompareAndSwap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	return __sync_bool_compare_and_swap(p, o, n);
}

static OF_INLINE bool
OFAtomicPointerCompareAndSwap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	return __sync_bool_compare_and_swap(p, o, n);
}

static OF_INLINE void
OFMemoryBarrier(void)
{
	__sync_synchronize();
}

static OF_INLINE void
OFAcquireMemoryBarrier(void)
{
	__sync_synchronize();
}

static OF_INLINE void
OFReleaseMemoryBarrier(void)
{
	__sync_synchronize();
}

Added src/platform/MorphOS/OFTLSKey.m version [f46c8d88c6].







































































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

#import "OFTLSKey.h"

int
OFTLSKeyNew(OFTLSKey *key)
{
	*key = TLSAllocA(NULL);

	if (*key == TLS_INVALID_INDEX)
		return EAGAIN;

	return 0;
}

int
OFTLSKeyFree(OFTLSKey key)
{
	return (TLSFree(key) ? 0 : EINVAL);
}

Renamed and modified src/platform/posix/condition.m [59d3a90643] to src/platform/POSIX/OFPlainCondition.m [d0abcf1839].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "condition.h"

bool

of_condition_new(of_condition_t *condition)
{
	return (pthread_cond_init(condition, NULL) == 0);
}

bool

of_condition_signal(of_condition_t *condition)
{
	return (pthread_cond_signal(condition) == 0);
}

bool

of_condition_broadcast(of_condition_t *condition)
{
	return (pthread_cond_broadcast(condition) == 0);
}

bool

of_condition_wait(of_condition_t *condition, of_mutex_t *mutex)
{
	return (pthread_cond_wait(condition, mutex) == 0);
}

bool

of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex,
    of_time_interval_t timeout)
{
	struct timespec ts;

	ts.tv_sec = (time_t)timeout;
	ts.tv_nsec = (long)((timeout - ts.tv_sec) * 1000000000);

	return (pthread_cond_timedwait(condition, mutex, &ts) == 0);
}

bool

of_condition_free(of_condition_t *condition)
{
	return (pthread_cond_destroy(condition) == 0);
}

<
<
|















|

<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|
|






|


<
>
|

|

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


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

#import "OFPlainCondition.h"


int
OFPlainConditionNew(OFPlainCondition *condition)
{
	return pthread_cond_init(condition, NULL);
}


int
OFPlainConditionSignal(OFPlainCondition *condition)
{
	return pthread_cond_signal(condition);
}


int
OFPlainConditionBroadcast(OFPlainCondition *condition)
{
	return pthread_cond_broadcast(condition);
}


int
OFPlainConditionWait(OFPlainCondition *condition, OFPlainMutex *mutex)
{
	return pthread_cond_wait(condition, mutex);
}


int
OFPlainConditionTimedWait(OFPlainCondition *condition, OFPlainMutex *mutex,
    OFTimeInterval timeout)
{
	struct timespec ts;

	ts.tv_sec = (time_t)timeout;
	ts.tv_nsec = (long)((timeout - ts.tv_sec) * 1000000000);

	return pthread_cond_timedwait(condition, mutex, &ts);
}


int
OFPlainConditionFree(OFPlainCondition *condition)
{
	return pthread_cond_destroy(condition);
}

Renamed and modified src/platform/posix/mutex.m [608d6df3a8] to src/platform/POSIX/OFPlainMutex.m [bcf658e6be].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "mutex.h"

bool

of_mutex_new(of_mutex_t *mutex)
{
	return (pthread_mutex_init(mutex, NULL) == 0);
}

bool

of_mutex_lock(of_mutex_t *mutex)
{
	return (pthread_mutex_lock(mutex) == 0);
}

bool

of_mutex_trylock(of_mutex_t *mutex)
{
	return (pthread_mutex_trylock(mutex) == 0);
}

bool

of_mutex_unlock(of_mutex_t *mutex)
{
	return (pthread_mutex_unlock(mutex) == 0);
}

bool

of_mutex_free(of_mutex_t *mutex)
{
	return (pthread_mutex_destroy(mutex) == 0);
}

#ifdef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES
bool

of_rmutex_new(of_rmutex_t *rmutex)
{

	pthread_mutexattr_t attr;

	if (pthread_mutexattr_init(&attr) != 0)
		return false;

	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)

		return false;

	if (pthread_mutex_init(rmutex, &attr) != 0)
		return false;

	if (pthread_mutexattr_destroy(&attr) != 0)
		return false;

	return true;
}

bool

of_rmutex_lock(of_rmutex_t *rmutex)
{
	return of_mutex_lock(rmutex);
}

bool

of_rmutex_trylock(of_rmutex_t *rmutex)
{
	return of_mutex_trylock(rmutex);
}

bool

of_rmutex_unlock(of_rmutex_t *rmutex)
{
	return of_mutex_unlock(rmutex);
}

bool

of_rmutex_free(of_rmutex_t *rmutex)
{
	return of_mutex_free(rmutex);
}
#else
bool

of_rmutex_new(of_rmutex_t *rmutex)
{


	if (!of_mutex_new(&rmutex->mutex))
		return false;

	if (!of_tlskey_new(&rmutex->count))
		return false;

	return true;
}

bool

of_rmutex_lock(of_rmutex_t *rmutex)
{
	uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count);


	if (count > 0) {

		if (!of_tlskey_set(rmutex->count, (void *)(count + 1)))
			return false;

		return true;
	}

	if (!of_mutex_lock(&rmutex->mutex))
		return false;

	if (!of_tlskey_set(rmutex->count, (void *)1)) {
		of_mutex_unlock(&rmutex->mutex);
		return false;
	}

	return true;
}

bool

of_rmutex_trylock(of_rmutex_t *rmutex)
{
	uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count);


	if (count > 0) {

		if (!of_tlskey_set(rmutex->count, (void *)(count + 1)))
			return false;

		return true;
	}

	if (!of_mutex_trylock(&rmutex->mutex))
		return false;

	if (!of_tlskey_set(rmutex->count, (void *)1)) {
		of_mutex_unlock(&rmutex->mutex);
		return false;
	}

	return true;
}

bool

of_rmutex_unlock(of_rmutex_t *rmutex)
{
	uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count);


	if (count > 1) {

		if (!of_tlskey_set(rmutex->count, (void *)(count - 1)))
			return false;

		return true;
	}

	if (!of_tlskey_set(rmutex->count, (void *)0))
		return false;

	if (!of_mutex_unlock(&rmutex->mutex))
		return false;

	return true;
}

bool

of_rmutex_free(of_rmutex_t *rmutex)
{


	if (!of_mutex_free(&rmutex->mutex))
		return false;

	if (!of_tlskey_free(rmutex->count))
		return false;

	return true;
}
#endif

<
<
|















|

<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|



<
>
|

>


|
|

|
>
|

|
|

|
|

|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

>
>
|
|

|
|

|


<
>
|

|
>


>
|
|

|


|
|

|
|
|


|


<
>
|

|
>


>
|
|

|


|
|

|
|
|


|


<
>
|

|
>


>
|
|

|


|
|

|
|

|


<
>
|

>
>
|
|

|
|

|


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


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

#import "OFPlainMutex.h"


int
OFPlainMutexNew(OFPlainMutex *mutex)
{
	return pthread_mutex_init(mutex, NULL);
}


int
OFPlainMutexLock(OFPlainMutex *mutex)
{
	return pthread_mutex_lock(mutex);
}


int
OFPlainMutexTryLock(OFPlainMutex *mutex)
{
	return pthread_mutex_trylock(mutex);
}


int
OFPlainMutexUnlock(OFPlainMutex *mutex)
{
	return pthread_mutex_unlock(mutex);
}


int
OFPlainMutexFree(OFPlainMutex *mutex)
{
	return pthread_mutex_destroy(mutex);
}

#ifdef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES

int
OFPlainRecursiveMutexNew(OFPlainRecursiveMutex *rmutex)
{
	int error;
	pthread_mutexattr_t attr;

	if ((error = pthread_mutexattr_init(&attr)) != 0)
		return error;

	if ((error = pthread_mutexattr_settype(&attr,
	    PTHREAD_MUTEX_RECURSIVE)) != 0)
		return error;

	if ((error = pthread_mutex_init(rmutex, &attr)) != 0)
		return error;

	if ((error = pthread_mutexattr_destroy(&attr)) != 0)
		return error;

	return 0;
}


int
OFPlainRecursiveMutexLock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexLock(rmutex);
}


int
OFPlainRecursiveMutexTryLock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexTryLock(rmutex);
}


int
OFPlainRecursiveMutexUnlock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexUnlock(rmutex);
}


int
OFPlainRecursiveMutexFree(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexFree(rmutex);
}
#else

int
OFPlainRecursiveMutexNew(OFPlainRecursiveMutex *rmutex)
{
	int error;

	if ((error = OFPlainMutexNew(&rmutex->mutex)) != 0)
		return error;

	if ((error = OFTLSKeyNew(&rmutex->count)) != 0)
		return error;

	return 0;
}


int
OFPlainRecursiveMutexLock(OFPlainRecursiveMutex *rmutex)
{
	uintptr_t count = (uintptr_t)OFTLSKeyGet(rmutex->count);
	int error;

	if (count > 0) {
		if ((error = OFTLSKeySet(rmutex->count,
		    (void *)(count + 1))) != 0)
			return error;

		return 0;
	}

	if ((error = OFPlainMutexLock(&rmutex->mutex)) != 0)
		return error;

	if ((error = OFTLSKeySet(rmutex->count, (void *)1)) != 0) {
		OFPlainMutexUnlock(&rmutex->mutex);
		return error;
	}

	return 0;
}


int
OFPlainRecursiveMutexTryLock(OFPlainRecursiveMutex *rmutex)
{
	uintptr_t count = (uintptr_t)OFTLSKeyGet(rmutex->count);
	int error;

	if (count > 0) {
		if ((error = OFTLSKeySet(rmutex->count,
		    (void *)(count + 1))) != 0)
			return error;

		return 0;
	}

	if ((error = OFPlainMutexTryLock(&rmutex->mutex)) != 0)
		return error;

	if ((error = OFTLSKeySet(rmutex->count, (void *)1)) != 0) {
		OFPlainMutexUnlock(&rmutex->mutex);
		return error;
	}

	return 0;
}


int
OFPlainRecursiveMutexUnlock(OFPlainRecursiveMutex *rmutex)
{
	uintptr_t count = (uintptr_t)OFTLSKeyGet(rmutex->count);
	int error;

	if (count > 1) {
		if ((error = OFTLSKeySet(rmutex->count,
		    (void *)(count - 1))) != 0)
			return error;

		return 0;
	}

	if ((error = OFTLSKeySet(rmutex->count, (void *)0)) != 0)
		return error;

	if ((error = OFPlainMutexUnlock(&rmutex->mutex)) != 0)
		return error;

	return 0;
}


int
OFPlainRecursiveMutexFree(OFPlainRecursiveMutex *rmutex)
{
	int error;

	if ((error = OFPlainMutexFree(&rmutex->mutex)) != 0)
		return error;

	if ((error = OFTLSKeyFree(rmutex->count)) != 0)
		return error;

	return 0;
}
#endif

Renamed and modified src/platform/posix/thread.m [cdd7c510a1] to src/platform/POSIX/OFPlainThread.m [7bbb313eb6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
# include <pthread_np.h>
#endif

#ifdef OF_HAIKU
# include <kernel/OS.h>
#endif

#import "thread.h"

#import "macros.h"

static int minPrio = 0, maxPrio = 0, normalPrio = 0;

struct thread_ctx {
	void (*function)(id object);
	id object;
	const char *name;
};

/*
 * This is done here to make sure this is done as early as possible in the main
 * thread.
 */
OF_CONSTRUCTOR()
{
	pthread_attr_t pattr;

	if (pthread_attr_init(&pattr) == 0) {
#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY
		int policy;
#endif
		struct sched_param param;

#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY
		if (pthread_attr_getschedpolicy(&pattr, &policy) == 0) {
			minPrio = sched_get_priority_min(policy);
			maxPrio = sched_get_priority_max(policy);

			if (minPrio == -1 || maxPrio == -1)
				minPrio = maxPrio = 0;
		}
#endif

		if (pthread_attr_getschedparam(&pattr, &param) != 0)
			normalPrio = param.sched_priority;
		else
			minPrio = maxPrio = 0;

		pthread_attr_destroy(&pattr);
	}
}

static void *
functionWrapper(void *data)
{
	struct thread_ctx *ctx = data;

	if (ctx->name != NULL)
		of_thread_set_name(ctx->name);

	pthread_cleanup_push(free, data);

	ctx->function(ctx->object);

	pthread_cleanup_pop(1);
	return NULL;
}

bool

of_thread_attr_init(of_thread_attr_t *attr)
{

	pthread_attr_t pattr;




	if (pthread_attr_init(&pattr) != 0)

		return false;

	@try {
		attr->priority = 0;


		if (pthread_attr_getstacksize(&pattr, &attr->stackSize) != 0)
			return false;
	} @finally {
		pthread_attr_destroy(&pattr);
	}

	return true;
}

bool

of_thread_new(of_thread_t *thread, const char *name, void (*function)(id),
    id object, const of_thread_attr_t *attr)
{
	bool ret;

	pthread_attr_t pattr;


	if (pthread_attr_init(&pattr) != 0)



		return false;


	@try {
		struct thread_ctx *ctx;

		if (attr != NULL) {

			struct sched_param param;


			if (attr->priority < -1 || attr->priority > 1) {
				errno = EINVAL;
				return false;
			}

#ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
			if (pthread_attr_setinheritsched(&pattr,
			    PTHREAD_EXPLICIT_SCHED) != 0)
				return false;
#endif





			if (attr->priority < 0) {
				param.sched_priority = minPrio +
				    (1.0f + attr->priority) *
				    (normalPrio - minPrio);
			} else
				param.sched_priority = normalPrio +
				    attr->priority * (maxPrio - normalPrio);

			if (pthread_attr_setschedparam(&pattr, &param) != 0)

				return false;


			if (attr->stackSize > 0) {
				if (pthread_attr_setstacksize(&pattr,
				    attr->stackSize) != 0)
					return false;
			}
		}

		if ((ctx = malloc(sizeof(*ctx))) == NULL) {
			errno = ENOMEM;
			return false;
		}

		ctx->function = function;
		ctx->object = object;
		ctx->name = name;

		ret = (pthread_create(thread, &pattr,
		    functionWrapper, ctx) == 0);

	} @finally {

		pthread_attr_destroy(&pattr);
	}

	return ret;
}

bool

of_thread_join(of_thread_t thread)
{
	void *ret;

	return (pthread_join(thread, &ret) == 0);
}

bool

of_thread_detach(of_thread_t thread)
{
	return (pthread_detach(thread) == 0);
}

void
of_thread_set_name(const char *name)
{
#if defined(OF_HAIKU)
	rename_thread(find_thread(NULL), name);
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
	pthread_set_name_np(pthread_self(), name);
#elif defined(HAVE_PTHREAD_SETNAME_NP)
# if defined(OF_MACOS) || defined(OF_IOS)







|
>




|











|

|






|








|




|






|


|









<
>
|

>
|

>
>
>
|
>
|

<
|
|
>
|
|
<
|
|
<
|


<
>
|
|

<
>
|
>

|
>
>
>
|
|
>

|

|
>

>

|
<
|
|
|
|
|
|
|
|
>
>
>
>









|
>
|
>


|
|
|



|
<
|
<





|
|
>

>
|


|


<
>
|



|


<
>
|

|



|







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
# include <pthread_np.h>
#endif

#ifdef OF_HAIKU
# include <kernel/OS.h>
#endif

#import "OFPlainThread.h"

#import "macros.h"

static int minPrio = 0, maxPrio = 0, normalPrio = 0;

struct ThreadContext {
	void (*function)(id object);
	id object;
	const char *name;
};

/*
 * This is done here to make sure this is done as early as possible in the main
 * thread.
 */
OF_CONSTRUCTOR()
{
	pthread_attr_t attr;

	if (pthread_attr_init(&attr) == 0) {
#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY
		int policy;
#endif
		struct sched_param param;

#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY
		if (pthread_attr_getschedpolicy(&attr, &policy) == 0) {
			minPrio = sched_get_priority_min(policy);
			maxPrio = sched_get_priority_max(policy);

			if (minPrio == -1 || maxPrio == -1)
				minPrio = maxPrio = 0;
		}
#endif

		if (pthread_attr_getschedparam(&attr, &param) != 0)
			normalPrio = param.sched_priority;
		else
			minPrio = maxPrio = 0;

		pthread_attr_destroy(&attr);
	}
}

static void *
functionWrapper(void *data)
{
	struct ThreadContext *ctx = data;

	if (ctx->name != NULL)
		OFSetThreadName(ctx->name);

	pthread_cleanup_push(free, data);

	ctx->function(ctx->object);

	pthread_cleanup_pop(1);
	return NULL;
}


int
OFPlainThreadAttributesInit(OFPlainThreadAttributes *attr)
{
	int error;
	pthread_attr_t POSIXAttr;

	attr->priority = 0;
	attr->stackSize = 0;

	if ((error = pthread_attr_init(&POSIXAttr)) != 0) {
		if (error == ENOSYS)
			return 0;


		return error;
	}

	error = pthread_attr_getstacksize(&POSIXAttr, &attr->stackSize);


	pthread_attr_destroy(&POSIXAttr);


	return error;
}


int
OFPlainThreadNew(OFPlainThread *thread, const char *name, void (*function)(id),
    id object, const OFPlainThreadAttributes *attr)
{

	int error = 0;
	pthread_attr_t POSIXAttr;
	bool POSIXAttrAvailable = true;

	if ((error = pthread_attr_init(&POSIXAttr)) != 0) {
		if (error == ENOSYS)
			POSIXAttrAvailable = false;
		else
			return error;
	}

	@try {
		struct ThreadContext *ctx;

		if (attr != NULL && POSIXAttrAvailable) {
#ifndef OF_HPUX
			struct sched_param param;
#endif

			if (attr->priority < -1 || attr->priority > 1)

				return EINVAL;

#ifndef OF_HPUX
# ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
			if ((error = pthread_attr_setinheritsched(&POSIXAttr,
			    PTHREAD_EXPLICIT_SCHED)) != 0)
				return error;
# endif

			if ((error = pthread_attr_getschedparam(&POSIXAttr,
			    &param)) != 0)
				return error;

			if (attr->priority < 0) {
				param.sched_priority = minPrio +
				    (1.0f + attr->priority) *
				    (normalPrio - minPrio);
			} else
				param.sched_priority = normalPrio +
				    attr->priority * (maxPrio - normalPrio);

			if ((error = pthread_attr_setschedparam(&POSIXAttr,
			    &param)) != 0)
				return error;
#endif

			if (attr->stackSize > 0) {
				if ((error = pthread_attr_setstacksize(
				    &POSIXAttr, attr->stackSize)) != 0)
					return error;
			}
		}

		if ((ctx = malloc(sizeof(*ctx))) == NULL)

			return ENOMEM;


		ctx->function = function;
		ctx->object = object;
		ctx->name = name;

		error = pthread_create(thread,
		    (POSIXAttrAvailable ? &POSIXAttr : NULL), functionWrapper,
		    ctx);
	} @finally {
		if (POSIXAttrAvailable)
			pthread_attr_destroy(&POSIXAttr);
	}

	return error;
}


int
OFPlainThreadJoin(OFPlainThread thread)
{
	void *ret;

	return pthread_join(thread, &ret);
}


int
OFPlainThreadDetach(OFPlainThread thread)
{
	return pthread_detach(thread);
}

void
OFSetThreadName(const char *name)
{
#if defined(OF_HAIKU)
	rename_thread(find_thread(NULL), name);
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
	pthread_set_name_np(pthread_self(), name);
#elif defined(HAVE_PTHREAD_SETNAME_NP)
# if defined(OF_MACOS) || defined(OF_IOS)

Renamed and modified src/platform/posix/OFProcess.m [a1d2451059] to src/platform/POSIX/OFSubprocess.m [f41e3bb02c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#endif

#include "unistd_wrapper.h"
#ifdef HAVE_SPAWN_H
# include <spawn.h>
#endif

#import "OFProcess.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFLocale.h"

#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#ifndef HAVE_POSIX_SPAWNP
extern char **environ;
#endif

@interface OFProcess ()
- (void)of_getArgv: (char ***)argv
    forProgramName: (OFString *)programName
      andArguments: (OFArray *)arguments;
- (char **)of_environmentForDictionary: (OFDictionary *)dictionary;
@end

@implementation OFProcess
+ (instancetype)processWithProgram: (OFString *)program
{
	return [[[self alloc] initWithProgram: program] autorelease];
}

+ (instancetype)processWithProgram: (OFString *)program
			 arguments: (OFArray *)arguments
{
	return [[[self alloc] initWithProgram: program
				    arguments: arguments] autorelease];
}

+ (instancetype)processWithProgram: (OFString *)program
		       programName: (OFString *)programName
			 arguments: (OFArray *)arguments
{
	return [[[self alloc] initWithProgram: program
				  programName: programName
				    arguments: arguments] autorelease];
}

+ (instancetype)processWithProgram: (OFString *)program
		       programName: (OFString *)programName
			 arguments: (OFArray *)arguments
		       environment: (OFDictionary *)environment
{
	return [[[self alloc] initWithProgram: program
				  programName: programName
				    arguments: arguments
				  environment: environment] autorelease];
}








|















|






|
|




|
|





|
|
|






|
|
|
|







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
#endif

#include "unistd_wrapper.h"
#ifdef HAVE_SPAWN_H
# include <spawn.h>
#endif

#import "OFSubprocess.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFLocale.h"

#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#ifndef HAVE_POSIX_SPAWNP
extern char **environ;
#endif

@interface OFSubprocess ()
- (void)of_getArgv: (char ***)argv
    forProgramName: (OFString *)programName
      andArguments: (OFArray *)arguments;
- (char **)of_environmentForDictionary: (OFDictionary *)dictionary;
@end

@implementation OFSubprocess
+ (instancetype)subprocessWithProgram: (OFString *)program
{
	return [[[self alloc] initWithProgram: program] autorelease];
}

+ (instancetype)subprocessWithProgram: (OFString *)program
			    arguments: (OFArray *)arguments
{
	return [[[self alloc] initWithProgram: program
				    arguments: arguments] autorelease];
}

+ (instancetype)subprocessWithProgram: (OFString *)program
			  programName: (OFString *)programName
			    arguments: (OFArray *)arguments
{
	return [[[self alloc] initWithProgram: program
				  programName: programName
				    arguments: arguments] autorelease];
}

+ (instancetype)subprocessWithProgram: (OFString *)program
			  programName: (OFString *)programName
			    arguments: (OFArray *)arguments
			  environment: (OFDictionary *)environment
{
	return [[[self alloc] initWithProgram: program
				  programName: programName
				    arguments: arguments
				  environment: environment] autorelease];
}

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
		    environment: (OFDictionary *)environment
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		const char *path;
		char **argv;

		_pid = -1;
		_readPipe[0] = _writePipe[1] = -1;

		if (pipe(_readPipe) != 0 || pipe(_writePipe) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		path = [program cStringWithEncoding: [OFLocale encoding]];
		[self of_getArgv: &argv
		  forProgramName: programName
		    andArguments: arguments];

		@try {
			char **env = [self
			    of_environmentForDictionary: environment];
#ifdef HAVE_POSIX_SPAWNP
			posix_spawn_file_actions_t actions;
			posix_spawnattr_t attr;

			if (posix_spawn_file_actions_init(&actions) != 0)
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];







|














<
|







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
		    environment: (OFDictionary *)environment
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		const char *path;
		char **argv, **env = NULL;

		_pid = -1;
		_readPipe[0] = _writePipe[1] = -1;

		if (pipe(_readPipe) != 0 || pipe(_writePipe) != 0)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		path = [program cStringWithEncoding: [OFLocale encoding]];
		[self of_getArgv: &argv
		  forProgramName: programName
		    andArguments: arguments];

		@try {

			env = [self of_environmentForDictionary: environment];
#ifdef HAVE_POSIX_SPAWNP
			posix_spawn_file_actions_t actions;
			posix_spawnattr_t attr;

			if (posix_spawn_file_actions_init(&actions) != 0)
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];
202
203
204
205
206
207
208


209
210
211






212
213
214
215
216
217
218
			}

			if (_pid == -1)
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];
#endif
		} @finally {


			close(_readPipe[1]);
			close(_writePipe[0]);
			[self freeMemory: argv];






		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}







>
>


|
>
>
>
>
>
>







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
			}

			if (_pid == -1)
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];
#endif
		} @finally {
			char **iter;

			close(_readPipe[1]);
			close(_writePipe[0]);
			OFFreeMemory(argv);

			if (env != NULL)
				for (iter = env; *iter != NULL; iter++)
					OFFreeMemory(*iter);

			OFFreeMemory(env);
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}
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

- (void)of_getArgv: (char ***)argv
    forProgramName: (OFString *)programName
      andArguments: (OFArray *)arguments
{
	OFString *const *objects = arguments.objects;
	size_t i, count = arguments.count;
	of_string_encoding_t encoding;

	*argv = [self allocMemoryWithSize: sizeof(char *)
				    count: count + 2];

	encoding = [OFLocale encoding];

	(*argv)[0] = (char *)[programName cStringWithEncoding: encoding];

	for (i = 0; i < count; i++)
		(*argv)[i + 1] =
		    (char *)[objects[i] cStringWithEncoding: encoding];

	(*argv)[i + 1] = NULL;
}

- (char **)of_environmentForDictionary: (OFDictionary *)environment
{
	OFEnumerator *keyEnumerator, *objectEnumerator;
	char **envp;
	size_t i, count;
	of_string_encoding_t encoding;

	if (environment == nil)
		return NULL;

	encoding = [OFLocale encoding];

	count = environment.count;
	envp = [self allocMemoryWithSize: sizeof(char *)
				   count: count + 1];


	keyEnumerator = [environment keyEnumerator];
	objectEnumerator = [environment objectEnumerator];

	for (i = 0; i < count; i++) {
		OFString *key;
		OFString *object;
		size_t keyLen, objectLen;

		key = [keyEnumerator nextObject];
		object = [objectEnumerator nextObject];

		keyLen = [key cStringLengthWithEncoding: encoding];

		objectLen = [object cStringLengthWithEncoding: encoding];

		envp[i] = [self allocMemoryWithSize: keyLen + objectLen + 2];


		memcpy(envp[i], [key cStringWithEncoding: encoding], keyLen);
		envp[i][keyLen] = '=';
		memcpy(envp[i] + keyLen + 1,
		    [object cStringWithEncoding: encoding], objectLen);
		envp[i][keyLen + objectLen + 1] = '\0';
	}





	envp[i] = NULL;



	return envp;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_readPipe[0] == -1)







|

|
<














<

|
|







|
<

>
|
|

|
|
|
|

|
|

|
>
|

|

>
|
|
|
|
|
|
>
>
>

>
|
>
>







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

- (void)of_getArgv: (char ***)argv
    forProgramName: (OFString *)programName
      andArguments: (OFArray *)arguments
{
	OFString *const *objects = arguments.objects;
	size_t i, count = arguments.count;
	OFStringEncoding encoding;

	*argv = OFAllocMemory(count + 2, sizeof(char *));


	encoding = [OFLocale encoding];

	(*argv)[0] = (char *)[programName cStringWithEncoding: encoding];

	for (i = 0; i < count; i++)
		(*argv)[i + 1] =
		    (char *)[objects[i] cStringWithEncoding: encoding];

	(*argv)[i + 1] = NULL;
}

- (char **)of_environmentForDictionary: (OFDictionary *)environment
{

	char **envp;
	size_t count;
	OFStringEncoding encoding;

	if (environment == nil)
		return NULL;

	encoding = [OFLocale encoding];

	count = environment.count;
	envp = OFAllocZeroedMemory(count + 1, sizeof(char *));


	@try {
		OFEnumerator *keyEnumerator = [environment keyEnumerator];
		OFEnumerator *objectEnumerator = [environment objectEnumerator];

		for (size_t i = 0; i < count; i++) {
			OFString *key;
			OFString *object;
			size_t keyLen, objectLen;

			key = [keyEnumerator nextObject];
			object = [objectEnumerator nextObject];

			keyLen = [key cStringLengthWithEncoding: encoding];
			objectLen = [object
			    cStringLengthWithEncoding: encoding];

			envp[i] = OFAllocMemory(keyLen + objectLen + 2, 1);

			memcpy(envp[i],
			    [key cStringWithEncoding: encoding], keyLen);
			envp[i][keyLen] = '=';
			memcpy(envp[i] + keyLen + 1,
			    [object cStringWithEncoding: encoding], objectLen);
			envp[i][keyLen + objectLen + 1] = '\0';
		}
	} @catch (id e) {
		for (size_t i = 0; i < count; i++)
			OFFreeMemory(envp[i]);

		OFFreeMemory(envp);

		@throw e;
	}

	return envp;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_readPipe[0] == -1)
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	ssize_t bytesWritten;

	if (_writePipe[1] == -1)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (length > SSIZE_MAX)







|
<







328
329
330
331
332
333
334
335

336
337
338
339
340
341
342

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{
	ssize_t bytesWritten;

	if (_writePipe[1] == -1)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (length > SSIZE_MAX)

Renamed and modified src/platform/posix/tlskey.m [01a5f7adde] to src/platform/POSIX/OFTLSKey.m [e854931fa2].

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, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "tlskey.h"

bool

of_tlskey_new(of_tlskey_t *key)
{
	return (pthread_key_create(key, NULL) == 0);
}

bool

of_tlskey_free(of_tlskey_t key)
{
	return (pthread_key_delete(key) == 0);
}

<
<
|















|

<
>
|

|


<
>
|

|

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


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

#import "OFTLSKey.h"


int
OFTLSKeyNew(OFTLSKey *key)
{
	return pthread_key_create(key, NULL);
}


int
OFTLSKeyFree(OFTLSKey key)
{
	return pthread_key_delete(key);
}

Renamed and modified src/atomic_powerpc.h [11a109fab0] to src/platform/PowerPC/OFAtomic.h [d8964b5a28].

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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

static OF_INLINE int
of_atomic_int_add(volatile int *_Nonnull p, int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "add	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int32_t
of_atomic_int32_add(volatile int32_t *_Nonnull p, int32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "add	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE void *_Nullable
of_atomic_ptr_add(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "add	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return (void *)i;
}

static OF_INLINE int
of_atomic_int_sub(volatile int *_Nonnull p, int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "sub	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int32_t
of_atomic_int32_sub(volatile int32_t *_Nonnull p, int32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "sub	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE void *_Nullable
of_atomic_ptr_sub(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "sub	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return (void *)i;
}

static OF_INLINE int
of_atomic_int_inc(volatile int *_Nonnull p)
{
	int i;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %1\n\t"
	    "addi	%0, %0, 1\n\t"
	    "stwcx.	%0, 0, %1\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int32_t
of_atomic_int32_inc(volatile int32_t *_Nonnull p)
{
	int32_t i;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %1\n\t"
	    "addi	%0, %0, 1\n\t"
	    "stwcx.	%0, 0, %1\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int
of_atomic_int_dec(volatile int *_Nonnull p)
{
	int i;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %1\n\t"
	    "subi	%0, %0, 1\n\t"
	    "stwcx.	%0, 0, %1\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int32_t
of_atomic_int32_dec(volatile int32_t *_Nonnull p)
{
	int32_t i;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %1\n\t"
	    "subi	%0, %0, 1\n\t"
	    "stwcx.	%0, 0, %1\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE unsigned int
of_atomic_int_or(volatile unsigned int *_Nonnull p, unsigned int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "or		%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE uint32_t
of_atomic_int32_or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "or		%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE unsigned int
of_atomic_int_and(volatile unsigned int *_Nonnull p, unsigned int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "and	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE uint32_t
of_atomic_int32_and(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "and	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE unsigned int
of_atomic_int_xor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "xor	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE uint32_t
of_atomic_int32_xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "xor	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE bool
of_atomic_int_cmpswap(volatile int *_Nonnull p, int o, int n)


























{
	int r;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %3\n\t"
	    "cmpw	%0, %1\n\t"

<
<
|














|
















|
















|
















|
















|
















|
















|


















|


















|


















|


















|
















|
















|
















|
















|
















|
















|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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


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

static OF_INLINE int
OFAtomicIntAdd(volatile int *_Nonnull p, int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "add	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int32_t
OFAtomicInt32Add(volatile int32_t *_Nonnull p, int32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "add	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE void *_Nullable
OFAtomicPointerAdd(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "add	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return (void *)i;
}

static OF_INLINE int
OFAtomicIntSubtract(volatile int *_Nonnull p, int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "sub	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int32_t
OFAtomicInt32Subtract(volatile int32_t *_Nonnull p, int32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "sub	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE void *_Nullable
OFAtomicPointerSubtract(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "sub	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return (void *)i;
}

static OF_INLINE int
OFAtomicIntIncrease(volatile int *_Nonnull p)
{
	int i;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %1\n\t"
	    "addi	%0, %0, 1\n\t"
	    "stwcx.	%0, 0, %1\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int32_t
OFAtomicInt32Increase(volatile int32_t *_Nonnull p)
{
	int32_t i;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %1\n\t"
	    "addi	%0, %0, 1\n\t"
	    "stwcx.	%0, 0, %1\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int
OFAtomicIntDecrease(volatile int *_Nonnull p)
{
	int i;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %1\n\t"
	    "subi	%0, %0, 1\n\t"
	    "stwcx.	%0, 0, %1\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE int32_t
OFAtomicInt32Decrease(volatile int32_t *_Nonnull p)
{
	int32_t i;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %1\n\t"
	    "subi	%0, %0, 1\n\t"
	    "stwcx.	%0, 0, %1\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE unsigned int
OFAtomicIntOr(volatile unsigned int *_Nonnull p, unsigned int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "or		%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE uint32_t
OFAtomicInt32Or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "or		%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE unsigned int
OFAtomicIntAnd(volatile unsigned int *_Nonnull p, unsigned int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "and	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE uint32_t
OFAtomicInt32And(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "and	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE unsigned int
OFAtomicIntXor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "xor	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE uint32_t
OFAtomicInt32Xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %2\n\t"
	    "xor	%0, %0, %1\n\t"
	    "stwcx.	%0, 0, %2\n\t"
	    "bne-	0b"
	    : "=&r"(i)
	    : "r"(i), "r"(p)
	    : "cc", "memory"
	);

	return i;
}

static OF_INLINE bool
OFAtomicIntCompareAndSwap(volatile int *_Nonnull p, int o, int n)
{
	int r;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %3\n\t"
	    "cmpw	%0, %1\n\t"
	    "bne	1f\n\t"
	    "stwcx.	%2, 0, %3\n\t"
	    "bne-	0b\n\t"
	    "li		%0, 1\n\t"
	    "b		2f\n\t"
	    "1:\n\t"
	    "stwcx.	%0, 0, %3\n\t"
	    "li		%0, 0\n\t"
	    "2:"
	    : "=&r"(r)
	    : "r"(o), "r"(n), "r"(p)
	    : "cc", "memory"
	);

	return r;
}

static OF_INLINE bool
OFAtomicInt32CompareAndSwap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	int r;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %3\n\t"
	    "cmpw	%0, %1\n\t"
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
	    : "cc", "memory"
	);

	return r;
}

static OF_INLINE bool
of_atomic_int32_cmpswap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	int r;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %3\n\t"
	    "cmpw	%0, %1\n\t"
	    "bne	1f\n\t"
	    "stwcx.	%2, 0, %3\n\t"
	    "bne-	0b\n\t"
	    "li		%0, 1\n\t"
	    "b		2f\n\t"
	    "1:\n\t"
	    "stwcx.	%0, 0, %3\n\t"
	    "li		%0, 0\n\t"
	    "2:"
	    : "=&r"(r)
	    : "r"(o), "r"(n), "r"(p)
	    : "cc", "memory"
	);

	return r;
}

static OF_INLINE bool
of_atomic_ptr_cmpswap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	int r;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %3\n\t"







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|







342
343
344
345
346
347
348


























349
350
351
352
353
354
355
356
	    : "cc", "memory"
	);

	return r;
}

static OF_INLINE bool


























OFAtomicPointerCompareAndSwap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	int r;

	__asm__ __volatile__ (
	    "0:\n\t"
	    "lwarx	%0, 0, %3\n\t"
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
	    : "cc", "memory"
	);

	return r;
}

static OF_INLINE void
of_memory_barrier(void)
{
	__asm__ __volatile__ (
	    ".long 0x7C2004AC /* lwsync */" ::: "memory"
	);
}

static OF_INLINE void
of_memory_barrier_acquire(void)
{
	__asm__ __volatile__ (
	    ".long 0x7C2004AC /* lwsync */" ::: "memory"
	);
}

static OF_INLINE void
of_memory_barrier_release(void)
{
	__asm__ __volatile__ (
	    ".long 0x7C2004AC /* lwsync */" ::: "memory"
	);
}







|







|







|





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
	    : "cc", "memory"
	);

	return r;
}

static OF_INLINE void
OFMemoryBarrier(void)
{
	__asm__ __volatile__ (
	    ".long 0x7C2004AC /* lwsync */" ::: "memory"
	);
}

static OF_INLINE void
OFAcquireMemoryBarrier(void)
{
	__asm__ __volatile__ (
	    ".long 0x7C2004AC /* lwsync */" ::: "memory"
	);
}

static OF_INLINE void
OFReleaseMemoryBarrier(void)
{
	__asm__ __volatile__ (
	    ".long 0x7C2004AC /* lwsync */" ::: "memory"
	);
}

Renamed and modified src/platform/windows/condition.m [848a636ef0] to src/platform/Windows/OFPlainCondition.m [cd952f15d7].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "condition.h"

#include <windows.h>

bool

of_condition_new(of_condition_t *condition)
{
	condition->count = 0;

	if ((condition->event = CreateEvent(NULL, FALSE, 0, NULL)) == NULL) {
		errno = EAGAIN;
		return false;
	}

	return true;
}

bool

of_condition_signal(of_condition_t *condition)
{
	if (!SetEvent(condition->event)) {
		switch (GetLastError()) {
		case ERROR_INVALID_HANDLE:
			errno = EINVAL;
			return false;
		default:
			OF_ENSURE(0);
		}
	}

	return true;
}

bool

of_condition_broadcast(of_condition_t *condition)
{
	int count = condition->count;

	for (int i = 0; i < count; i++) {
		if (!SetEvent(condition->event)) {
			switch (GetLastError()) {
			case ERROR_INVALID_HANDLE:
				errno = EINVAL;
				return false;
			default:
				OF_ENSURE(0);
			}
		}
	}

	return true;
}

bool

of_condition_wait(of_condition_t *condition, of_mutex_t *mutex)
{

	DWORD status;

	if (!of_mutex_unlock(mutex))
		return false;

	of_atomic_int_inc(&condition->count);
	status = WaitForSingleObject(condition->event, INFINITE);
	of_atomic_int_dec(&condition->count);

	switch (status) {
	case WAIT_OBJECT_0:
		return of_mutex_lock(mutex);
	case WAIT_FAILED:
		switch (GetLastError()) {
		case ERROR_INVALID_HANDLE:
			errno = EINVAL;
			return false;
		default:
			OF_ENSURE(0);
		}
	default:
		OF_ENSURE(0);
	}
}

bool
of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex,


    of_time_interval_t timeout)
{

	DWORD status;

	if (!of_mutex_unlock(mutex))
		return false;

	of_atomic_int_inc(&condition->count);
	status = WaitForSingleObject(condition->event, timeout * 1000);
	of_atomic_int_dec(&condition->count);

	switch (status) {
	case WAIT_OBJECT_0:
		return of_mutex_lock(mutex);
	case WAIT_TIMEOUT:
		errno = ETIMEDOUT;
		return false;
	case WAIT_FAILED:
		switch (GetLastError()) {
		case ERROR_INVALID_HANDLE:
			errno = EINVAL;
			return false;
		default:
			OF_ENSURE(0);
		}
	default:
		OF_ENSURE(0);
	}
}

bool

of_condition_free(of_condition_t *condition)
{
	if (condition->count != 0) {
		errno = EBUSY;
		return false;
	}

	return CloseHandle(condition->event);
}

<
<
|

















|



<
>
|



|
<
|
|
<
|


<
>
|




<
|

|



|


<
>
|







<
|

|




|


<
>
|

>


|
|

|

|



|



<
|

|


|


<
<
|
>
>
|

>


|
|

|

|



|

<
|



<
|

|


|



<
>
|

|
<
|
|
<
|

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


 * 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 "OFPlainCondition.h"

#include <windows.h>


int
OFPlainConditionNew(OFPlainCondition *condition)
{
	condition->count = 0;

	if ((condition->event = CreateEvent(NULL, FALSE, 0, NULL)) == NULL)

		return EAGAIN;


	return 0;
}


int
OFPlainConditionSignal(OFPlainCondition *condition)
{
	if (!SetEvent(condition->event)) {
		switch (GetLastError()) {
		case ERROR_INVALID_HANDLE:

			return EINVAL;
		default:
			OFEnsure(0);
		}
	}

	return 0;
}


int
OFPlainConditionBroadcast(OFPlainCondition *condition)
{
	int count = condition->count;

	for (int i = 0; i < count; i++) {
		if (!SetEvent(condition->event)) {
			switch (GetLastError()) {
			case ERROR_INVALID_HANDLE:

				return EINVAL;
			default:
				OFEnsure(0);
			}
		}
	}

	return 0;
}


int
OFPlainConditionWait(OFPlainCondition *condition, OFPlainMutex *mutex)
{
	int error;
	DWORD status;

	if ((error = OFPlainMutexUnlock(mutex)) != 0)
		return error;

	OFAtomicIntIncrease(&condition->count);
	status = WaitForSingleObject(condition->event, INFINITE);
	OFAtomicIntDecrease(&condition->count);

	switch (status) {
	case WAIT_OBJECT_0:
		return OFPlainMutexLock(mutex);
	case WAIT_FAILED:
		switch (GetLastError()) {
		case ERROR_INVALID_HANDLE:

			return EINVAL;
		default:
			OFEnsure(0);
		}
	default:
		OFEnsure(0);
	}
}



int
OFPlainConditionTimedWait(OFPlainCondition *condition, OFPlainMutex *mutex,
    OFTimeInterval timeout)
{
	int error;
	DWORD status;

	if ((error = OFPlainMutexUnlock(mutex)) != 0)
		return error;

	OFAtomicIntIncrease(&condition->count);
	status = WaitForSingleObject(condition->event, timeout * 1000);
	OFAtomicIntDecrease(&condition->count);

	switch (status) {
	case WAIT_OBJECT_0:
		return OFPlainMutexLock(mutex);
	case WAIT_TIMEOUT:

		return ETIMEDOUT;
	case WAIT_FAILED:
		switch (GetLastError()) {
		case ERROR_INVALID_HANDLE:

			return EINVAL;
		default:
			OFEnsure(0);
		}
	default:
		OFEnsure(0);
	}
}


int
OFPlainConditionFree(OFPlainCondition *condition)
{
	if (condition->count != 0)

		return EBUSY;


	return (CloseHandle(condition->event) ? 0 : EINVAL);
}

Renamed and modified src/platform/windows/mutex.m [c67aeb237e] to src/platform/Windows/OFPlainMutex.m [f924adddc3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "mutex.h"

#include <windows.h>

bool

of_mutex_new(of_mutex_t *mutex)
{
	InitializeCriticalSection(mutex);

	return true;
}

bool

of_mutex_lock(of_mutex_t *mutex)
{
	EnterCriticalSection(mutex);

	return true;
}

bool

of_mutex_trylock(of_mutex_t *mutex)
{
	if (!TryEnterCriticalSection(mutex)) {
		errno = EBUSY;
		return false;
	}

	return true;
}

bool

of_mutex_unlock(of_mutex_t *mutex)
{
	LeaveCriticalSection(mutex);

	return true;
}

bool

of_mutex_free(of_mutex_t *mutex)
{
	DeleteCriticalSection(mutex);

	return true;
}

bool

of_rmutex_new(of_rmutex_t *rmutex)
{
	return of_mutex_new(rmutex);
}

bool

of_rmutex_lock(of_rmutex_t *rmutex)
{
	return of_mutex_lock(rmutex);
}

bool

of_rmutex_trylock(of_rmutex_t *rmutex)
{
	return of_mutex_trylock(rmutex);
}

bool

of_rmutex_unlock(of_rmutex_t *rmutex)
{
	return of_mutex_unlock(rmutex);
}

bool

of_rmutex_free(of_rmutex_t *rmutex)
{
	return of_mutex_free(rmutex);
}

<
<
|

















|



<
>
|



|


<
>
|



|


<
>
|

|
<
|
|
<
|


<
>
|



|


<
>
|



|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|


<
>
|

|

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


 * 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 "OFPlainMutex.h"

#include <windows.h>


int
OFPlainMutexNew(OFPlainMutex *mutex)
{
	InitializeCriticalSection(mutex);

	return 0;
}


int
OFPlainMutexLock(OFPlainMutex *mutex)
{
	EnterCriticalSection(mutex);

	return 0;
}


int
OFPlainMutexTryLock(OFPlainMutex *mutex)
{
	if (!TryEnterCriticalSection(mutex))

		return EBUSY;


	return 0;
}


int
OFPlainMutexUnlock(OFPlainMutex *mutex)
{
	LeaveCriticalSection(mutex);

	return 0;
}


int
OFPlainMutexFree(OFPlainMutex *mutex)
{
	DeleteCriticalSection(mutex);

	return 0;
}


int
OFPlainRecursiveMutexNew(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexNew(rmutex);
}


int
OFPlainRecursiveMutexLock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexLock(rmutex);
}


int
OFPlainRecursiveMutexTryLock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexTryLock(rmutex);
}


int
OFPlainRecursiveMutexUnlock(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexUnlock(rmutex);
}


int
OFPlainRecursiveMutexFree(OFPlainRecursiveMutex *rmutex)
{
	return OFPlainMutexFree(rmutex);
}

Renamed and modified src/platform/windows/thread.m [d3d9eb9ec8] to src/platform/Windows/OFPlainThread.m [d2bc6a9806].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "thread.h"

#import "macros.h"

#include <windows.h>

struct thread_context {
	void (*function)(id);
	id object;
};

static WINAPI void
functionWrapper(struct thread_context *context)
{
	context->function(context->object);

	free(context);
}

bool

of_thread_attr_init(of_thread_attr_t *attr)
{
	attr->priority = 0;
	attr->stackSize = 0;

	return true;
}

bool

of_thread_new(of_thread_t *thread, const char *name, void (*function)(id),
    id object, const of_thread_attr_t *attr)
{
	DWORD priority = THREAD_PRIORITY_NORMAL;
	struct thread_context *context;
	DWORD threadID;

	if (attr != NULL && attr->priority != 0) {
		if (attr->priority < -1 || attr->priority > 1) {
			errno = EINVAL;
			return false;
		}

		if (attr->priority < 0)
			priority = THREAD_PRIORITY_LOWEST +
			    (1.0 + attr->priority) *
			    (THREAD_PRIORITY_NORMAL - THREAD_PRIORITY_LOWEST);
		else
			priority = THREAD_PRIORITY_NORMAL +
			    attr->priority *
			    (THREAD_PRIORITY_HIGHEST - THREAD_PRIORITY_NORMAL);
	}

	if ((context = malloc(sizeof(*context))) == NULL) {
		errno = ENOMEM;
		return false;
	}

	context->function = function;
	context->object = object;

	*thread = CreateThread(NULL, (attr != NULL ? attr->stackSize : 0),
	    (LPTHREAD_START_ROUTINE)functionWrapper, context, 0, &threadID);

	if (thread == NULL) {
		int errNo;

		switch (GetLastError()) {
		case ERROR_NOT_ENOUGH_MEMORY:
			errNo = ENOMEM;
			break;
		case ERROR_ACCESS_DENIED:
			errNo = EACCES;
			break;
		default:
			OF_ENSURE(0);
		}

		free(context);
		errno = errNo;
		return false;
	}

	if (attr != NULL && attr->priority != 0)
		OF_ENSURE(!SetThreadPriority(*thread, priority));

	return true;
}

bool

of_thread_join(of_thread_t thread)
{
	switch (WaitForSingleObject(thread, INFINITE)) {
	case WAIT_OBJECT_0:
		CloseHandle(thread);
		return true;
	case WAIT_FAILED:
		switch (GetLastError()) {
		case ERROR_INVALID_HANDLE:
			errno = EINVAL;
			return false;
		default:
			OF_ENSURE(0);
		}
	default:
		OF_ENSURE(0);
	}
}

bool

of_thread_detach(of_thread_t thread)
{
	CloseHandle(thread);

	return true;
}

void
of_thread_set_name(const char *name)
{
}

<
<
|

















|
>




|





|






<
>
|




|


<
>
|
|


|



|
<
|
<











|
<
|
<








|



|


|


|



<
|



|

|


<
>
|




|



<
|

|


|



<
>
|



|



|


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


 * 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 "OFPlainThread.h"

#import "macros.h"

#include <windows.h>

struct ThreadContext {
	void (*function)(id);
	id object;
};

static WINAPI void
functionWrapper(struct ThreadContext *context)
{
	context->function(context->object);

	free(context);
}


int
OFPlainThreadAttributesInit(OFPlainThreadAttributes *attr)
{
	attr->priority = 0;
	attr->stackSize = 0;

	return 0;
}


int
OFPlainThreadNew(OFPlainThread *thread, const char *name, void (*function)(id),
    id object, const OFPlainThreadAttributes *attr)
{
	DWORD priority = THREAD_PRIORITY_NORMAL;
	struct ThreadContext *context;
	DWORD threadID;

	if (attr != NULL && attr->priority != 0) {
		if (attr->priority < -1 || attr->priority > 1)

			return EINVAL;


		if (attr->priority < 0)
			priority = THREAD_PRIORITY_LOWEST +
			    (1.0 + attr->priority) *
			    (THREAD_PRIORITY_NORMAL - THREAD_PRIORITY_LOWEST);
		else
			priority = THREAD_PRIORITY_NORMAL +
			    attr->priority *
			    (THREAD_PRIORITY_HIGHEST - THREAD_PRIORITY_NORMAL);
	}

	if ((context = malloc(sizeof(*context))) == NULL)

		return ENOMEM;


	context->function = function;
	context->object = object;

	*thread = CreateThread(NULL, (attr != NULL ? attr->stackSize : 0),
	    (LPTHREAD_START_ROUTINE)functionWrapper, context, 0, &threadID);

	if (thread == NULL) {
		int error;

		switch (GetLastError()) {
		case ERROR_NOT_ENOUGH_MEMORY:
			error = ENOMEM;
			break;
		case ERROR_ACCESS_DENIED:
			error = EACCES;
			break;
		default:
			OFEnsure(0);
		}

		free(context);

		return error;
	}

	if (attr != NULL && attr->priority != 0)
		OFEnsure(!SetThreadPriority(*thread, priority));

	return 0;
}


int
OFPlainThreadJoin(OFPlainThread thread)
{
	switch (WaitForSingleObject(thread, INFINITE)) {
	case WAIT_OBJECT_0:
		CloseHandle(thread);
		return 0;
	case WAIT_FAILED:
		switch (GetLastError()) {
		case ERROR_INVALID_HANDLE:

			return EINVAL;
		default:
			OFEnsure(0);
		}
	default:
		OFEnsure(0);
	}
}


int
OFPlainThreadDetach(OFPlainThread thread)
{
	CloseHandle(thread);

	return 0;
}

void
OFSetThreadName(const char *name)
{
}

Renamed and modified src/platform/windows/OFProcess.m [816ee6b0db] to src/platform/Windows/OFSubprocess.m [0ef8b1e769].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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>
#include <string.h>

#import "OFProcess.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#include <windows.h>

@interface OFProcess ()
- (of_char16_t *)of_wideEnvironmentForDictionary: (OFDictionary *)dictionary;
- (char *)of_environmentForDictionary: (OFDictionary *)environment;
@end

@implementation OFProcess
+ (instancetype)processWithProgram: (OFString *)program
{
	return [[[self alloc] initWithProgram: program] autorelease];
}

+ (instancetype)processWithProgram: (OFString *)program
			 arguments: (OFArray *)arguments
{
	return [[[self alloc] initWithProgram: program
				    arguments: arguments] autorelease];
}

+ (instancetype)processWithProgram: (OFString *)program
		       programName: (OFString *)programName
			 arguments: (OFArray *)arguments
{
	return [[[self alloc] initWithProgram: program
				  programName: programName
				    arguments: arguments] autorelease];
}

+ (instancetype)processWithProgram: (OFString *)program
		       programName: (OFString *)programName
			 arguments: (OFArray *)arguments
		       environment: (OFDictionary *)environment
{
	return [[[self alloc] initWithProgram: program
				  programName: programName
				    arguments: arguments
				  environment: environment] autorelease];
}


<
<
|


















|















|
|



|
|




|
|





|
|
|






|
|
|
|







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


 * 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>
#include <string.h>

#import "OFSubprocess.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#include <windows.h>

@interface OFSubprocess ()
- (OFChar16 *)of_wideEnvironmentForDictionary: (OFDictionary *)dictionary;
- (char *)of_environmentForDictionary: (OFDictionary *)environment;
@end

@implementation OFSubprocess
+ (instancetype)subprocessWithProgram: (OFString *)program
{
	return [[[self alloc] initWithProgram: program] autorelease];
}

+ (instancetype)subprocessWithProgram: (OFString *)program
			    arguments: (OFArray *)arguments
{
	return [[[self alloc] initWithProgram: program
				    arguments: arguments] autorelease];
}

+ (instancetype)subprocessWithProgram: (OFString *)program
			  programName: (OFString *)programName
			    arguments: (OFArray *)arguments
{
	return [[[self alloc] initWithProgram: program
				  programName: programName
				    arguments: arguments] autorelease];
}

+ (instancetype)subprocessWithProgram: (OFString *)program
			  programName: (OFString *)programName
			    arguments: (OFArray *)arguments
			  environment: (OFDictionary *)environment
{
	return [[[self alloc] initWithProgram: program
				  programName: programName
				    arguments: arguments
				  environment: environment] autorelease];
}

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

	@try {
		SECURITY_ATTRIBUTES sa;
		PROCESS_INFORMATION pi;
		void *pool;
		OFMutableString *argumentsString;

		_process = INVALID_HANDLE_VALUE;
		_readPipe[0] = _writePipe[1] = NULL;

		sa.nLength = sizeof(sa);
		sa.bInheritHandle = TRUE;
		sa.lpSecurityDescriptor = NULL;

		if (!CreatePipe(&_readPipe[0], &_readPipe[1], &sa, 0))







|







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

	@try {
		SECURITY_ATTRIBUTES sa;
		PROCESS_INFORMATION pi;
		void *pool;
		OFMutableString *argumentsString;

		_handle = INVALID_HANDLE_VALUE;
		_readPipe[0] = _writePipe[1] = NULL;

		sa.nLength = sizeof(sa);
		sa.bInheritHandle = TRUE;
		sa.lpSecurityDescriptor = NULL;

		if (!CreatePipe(&_readPipe[0], &_readPipe[1], &sa, 0))
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

			if (containsSpaces)
				[argumentsString appendString: @"\""];
		}

		if ([OFSystemInfo isWindowsNT]) {
			size_t length;
			of_char16_t *argumentsCopy;
			STARTUPINFOW si;

			memset(&si, 0, sizeof(si));
			si.cb = sizeof(si);
			si.hStdInput = _writePipe[0];
			si.hStdOutput = _readPipe[1];
			si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
			si.dwFlags |= STARTF_USESTDHANDLES;

			length = argumentsString.UTF16StringLength;
			argumentsCopy = [self
			    allocMemoryWithSize: sizeof(of_char16_t)
					  count: length + 1];
			memcpy(argumentsCopy, argumentsString.UTF16String,
			    (length + 1) * 2);
			@try {
				if (!CreateProcessW(program.UTF16String,
				    argumentsCopy, NULL, NULL, TRUE,
				    CREATE_UNICODE_ENVIRONMENT,
				    [self of_wideEnvironmentForDictionary:
				    environment], NULL, &si, &pi))
					@throw [OFInitializationFailedException
					    exceptionWithClass: self.class];
			} @finally {
				[self freeMemory: argumentsCopy];
			}
		} else {
			of_string_encoding_t encoding = [OFLocale encoding];
			STARTUPINFO si;

			memset(&si, 0, sizeof(si));
			si.cb = sizeof(si);
			si.hStdInput = _writePipe[0];
			si.hStdOutput = _readPipe[1];
			si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
			si.dwFlags |= STARTF_USESTDHANDLES;

			if (!CreateProcessA([program cStringWithEncoding:
			    encoding], (char *)[argumentsString
			    cStringWithEncoding: encoding], NULL, NULL, TRUE, 0,
			    [self of_environmentForDictionary: environment],
			    NULL, &si, &pi))
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];
		}

		objc_autoreleasePoolPop(pool);

		_process = pi.hProcess;
		CloseHandle(pi.hThread);

		CloseHandle(_readPipe[1]);
		CloseHandle(_writePipe[0]);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_readPipe[0] != NULL)
		[self close];

	[super dealloc];
}

- (of_char16_t *)of_wideEnvironmentForDictionary: (OFDictionary *)environment
{
	OFMutableData *env;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object;
	const of_char16_t equal = '=';
	const of_char16_t zero[2] = { 0, 0 };

	if (environment == nil)
		return NULL;

	env = [OFMutableData dataWithItemSize: sizeof(of_char16_t)];

	keyEnumerator = [environment keyEnumerator];
	objectEnumerator = [environment objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil) {
		[env addItems: key.UTF16String
			count: key.UTF16StringLength];
		[env addItems: &equal
			count: 1];
		[env addItems: object.UTF16String
			count: object.UTF16StringLength];
		[env addItems: &zero
			count: 1];
	}
	[env addItems: zero
		count: 2];

	return env.mutableItems;
}

- (char *)of_environmentForDictionary: (OFDictionary *)environment
{
	of_string_encoding_t encoding = [OFLocale encoding];
	OFMutableData *env;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object;

	if (environment == nil)
		return NULL;

	env = [OFMutableData data];

	keyEnumerator = [environment keyEnumerator];
	objectEnumerator = [environment objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil) {
		[env addItems: [key cStringWithEncoding: encoding]
			count: [key cStringLengthWithEncoding: encoding]];
		[env addItems: "="
			count: 1];
		[env addItems: [object cStringWithEncoding: encoding]
			count: [object cStringLengthWithEncoding: encoding]];
		[env addItems: ""
			count: 1];
	}
	[env addItems: "\0"
		count: 2];

	return env.mutableItems;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_readPipe[0] == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	DWORD ret;

	if (length > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	if (_readPipe[0] == NULL)







|










|
|
<











|


|




















|




















|




|
|




|





|
<
|
<


|
<

|
<






|















|
<


|
<

|
<












|
<







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

			if (containsSpaces)
				[argumentsString appendString: @"\""];
		}

		if ([OFSystemInfo isWindowsNT]) {
			size_t length;
			OFChar16 *argumentsCopy;
			STARTUPINFOW si;

			memset(&si, 0, sizeof(si));
			si.cb = sizeof(si);
			si.hStdInput = _writePipe[0];
			si.hStdOutput = _readPipe[1];
			si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
			si.dwFlags |= STARTF_USESTDHANDLES;

			length = argumentsString.UTF16StringLength;
			argumentsCopy = OFAllocMemory(length + 1,
			    sizeof(OFChar16));

			memcpy(argumentsCopy, argumentsString.UTF16String,
			    (length + 1) * 2);
			@try {
				if (!CreateProcessW(program.UTF16String,
				    argumentsCopy, NULL, NULL, TRUE,
				    CREATE_UNICODE_ENVIRONMENT,
				    [self of_wideEnvironmentForDictionary:
				    environment], NULL, &si, &pi))
					@throw [OFInitializationFailedException
					    exceptionWithClass: self.class];
			} @finally {
				OFFreeMemory(argumentsCopy);
			}
		} else {
			OFStringEncoding encoding = [OFLocale encoding];
			STARTUPINFO si;

			memset(&si, 0, sizeof(si));
			si.cb = sizeof(si);
			si.hStdInput = _writePipe[0];
			si.hStdOutput = _readPipe[1];
			si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
			si.dwFlags |= STARTF_USESTDHANDLES;

			if (!CreateProcessA([program cStringWithEncoding:
			    encoding], (char *)[argumentsString
			    cStringWithEncoding: encoding], NULL, NULL, TRUE, 0,
			    [self of_environmentForDictionary: environment],
			    NULL, &si, &pi))
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];
		}

		objc_autoreleasePoolPop(pool);

		_handle = pi.hProcess;
		CloseHandle(pi.hThread);

		CloseHandle(_readPipe[1]);
		CloseHandle(_writePipe[0]);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_readPipe[0] != NULL)
		[self close];

	[super dealloc];
}

- (OFChar16 *)of_wideEnvironmentForDictionary: (OFDictionary *)environment
{
	OFMutableData *env;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object;
	const OFChar16 equal = '=';
	const OFChar16 zero[2] = { 0, 0 };

	if (environment == nil)
		return NULL;

	env = [OFMutableData dataWithItemSize: sizeof(OFChar16)];

	keyEnumerator = [environment keyEnumerator];
	objectEnumerator = [environment objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil) {
		[env addItems: key.UTF16String count: key.UTF16StringLength];

		[env addItems: &equal count: 1];

		[env addItems: object.UTF16String
			count: object.UTF16StringLength];
		[env addItems: &zero count: 1];

	}
	[env addItems: zero count: 2];


	return env.mutableItems;
}

- (char *)of_environmentForDictionary: (OFDictionary *)environment
{
	OFStringEncoding encoding = [OFLocale encoding];
	OFMutableData *env;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object;

	if (environment == nil)
		return NULL;

	env = [OFMutableData data];

	keyEnumerator = [environment keyEnumerator];
	objectEnumerator = [environment objectEnumerator];
	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil) {
		[env addItems: [key cStringWithEncoding: encoding]
			count: [key cStringLengthWithEncoding: encoding]];
		[env addItems: "=" count: 1];

		[env addItems: [object cStringWithEncoding: encoding]
			count: [object cStringLengthWithEncoding: encoding]];
		[env addItems: "" count: 1];

	}
	[env addItems: "\0" count: 2];


	return env.mutableItems;
}

- (bool)lowlevelIsAtEndOfStream
{
	if (_readPipe[0] == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length

{
	DWORD ret;

	if (length > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	if (_readPipe[0] == NULL)
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

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	DWORD bytesWritten;

	if (length > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	if (_writePipe[1] == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (!WriteFile(_writePipe[1], buffer, (DWORD)length, &bytesWritten,
	    NULL)) {
		int errNo = EIO;

		if (GetLastError() == ERROR_BROKEN_PIPE)
			errNo = EPIPE;

		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: 0
							     errNo: errNo];
	}

	return (size_t)bytesWritten;
}

- (void)closeForWriting







|
<


















|







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

	if (ret == 0)
		_atEndOfStream = true;

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length

{
	DWORD bytesWritten;

	if (length > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	if (_writePipe[1] == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (!WriteFile(_writePipe[1], buffer, (DWORD)length, &bytesWritten,
	    NULL)) {
		int errNo = EIO;

		if (GetLastError() == ERROR_BROKEN_PIPE)
			errNo = EPIPE;

		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: bytesWritten
							     errNo: errNo];
	}

	return (size_t)bytesWritten;
}

- (void)closeForWriting
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
{
	if (_readPipe[0] == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	[self closeForWriting];
	CloseHandle(_readPipe[0]);

	if (_process != INVALID_HANDLE_VALUE) {
		TerminateProcess(_process, 0);
		CloseHandle(_process);
	}

	_process = INVALID_HANDLE_VALUE;
	_readPipe[0] = NULL;

	[super close];
}

- (int)waitForTermination
{
	if (_readPipe[0] == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_process != INVALID_HANDLE_VALUE) {
		DWORD exitCode;

		WaitForSingleObject(_process, INFINITE);

		if (GetExitCodeProcess(_process, &exitCode))
			_status = exitCode;
		else
			_status = GetLastError();

		CloseHandle(_process);
		_process = INVALID_HANDLE_VALUE;
	}

	return _status;
}
@end







|
|
|


|










|


|

|




|
|





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
{
	if (_readPipe[0] == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	[self closeForWriting];
	CloseHandle(_readPipe[0]);

	if (_handle != INVALID_HANDLE_VALUE) {
		TerminateProcess(_handle, 0);
		CloseHandle(_handle);
	}

	_handle = INVALID_HANDLE_VALUE;
	_readPipe[0] = NULL;

	[super close];
}

- (int)waitForTermination
{
	if (_readPipe[0] == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_handle != INVALID_HANDLE_VALUE) {
		DWORD exitCode;

		WaitForSingleObject(_handle, INFINITE);

		if (GetExitCodeProcess(_handle, &exitCode))
			_status = exitCode;
		else
			_status = GetLastError();

		CloseHandle(_handle);
		_handle = INVALID_HANDLE_VALUE;
	}

	return _status;
}
@end

Renamed and modified src/platform/windows/tlskey.m [c8aecc8bc3] to src/platform/Windows/OFTLSKey.m [819b78979d].

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, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "tlskey.h"

bool

of_tlskey_new(of_tlskey_t *key)
{
	return ((*key = TlsAlloc()) != TLS_OUT_OF_INDEXES);
}





bool

of_tlskey_free(of_tlskey_t key)
{
	return TlsFree(key);
}

<
<
|















|

<
>
|

|
|
>
>

>
>
|
>
|

|

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


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

#import "OFTLSKey.h"


int
OFTLSKeyNew(OFTLSKey *key)
{
	*key = TlsAlloc();

	if (*key == TLS_OUT_OF_INDEXES)
		return EAGAIN;

	return 0;
}

int
OFTLSKeyFree(OFTLSKey key)
{
	return (TlsFree(key) ? 0 : EINVAL);
}

Modified src/platform/libfat/OFString+PathAdditions.m from [67bd48bd8c] to [9b9e52f706].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret, *fileName;
	size_t pos;

	fileName = self.lastPathComponent;
	pos = [fileName rangeOfString: @"."
			      options: OF_STRING_SEARCH_BACKWARDS].location;
	if (pos == OF_NOT_FOUND || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return @"";
	}

	ret = [fileName substringFromIndex: pos + 1];

	[ret retain];







|
|







149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret, *fileName;
	size_t pos;

	fileName = self.lastPathComponent;
	pos = [fileName rangeOfString: @"."
			      options: OFStringSearchBackwards].location;
	if (pos == OFNotFound || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return @"";
	}

	ret = [fileName substringFromIndex: pos + 1];

	[ret retain];
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
		return [[self copy] autorelease];

	pool = objc_autoreleasePoolPush();
	components = [[self.pathComponents mutableCopy] autorelease];
	fileName = components.lastObject;

	pos = [fileName rangeOfString: @"."
			      options: OF_STRING_SEARCH_BACKWARDS].location;
	if (pos == OF_NOT_FOUND || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return [[self copy] autorelease];
	}

	fileName = [fileName substringToIndex: pos];
	[components replaceObjectAtIndex: components.count - 1
			      withObject: fileName];







|
|







219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
		return [[self copy] autorelease];

	pool = objc_autoreleasePoolPush();
	components = [[self.pathComponents mutableCopy] autorelease];
	fileName = components.lastObject;

	pos = [fileName rangeOfString: @"."
			      options: OFStringSearchBackwards].location;
	if (pos == OFNotFound || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return [[self copy] autorelease];
	}

	fileName = [fileName substringToIndex: pos];
	[components replaceObjectAtIndex: components.count - 1
			      withObject: fileName];
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
				done = false;
				break;
			}

			if ([component isEqual: @".."] &&
			    parent != nil && ![parent isEqual: @".."]) {
				[array removeObjectsInRange:
				    of_range(i - 1, 2)];

				done = false;
				break;
			}
		}
	}








|







277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
				done = false;
				break;
			}

			if ([component isEqual: @".."] &&
			    parent != nil && ![parent isEqual: @".."]) {
				[array removeObjectsInRange:
				    OFRangeMake(i - 1, 2)];

				done = false;
				break;
			}
		}
	}

Renamed and modified src/atomic_osatomic.h [b2346ae318] to src/platform/macOS/OFAtomic.h [af25398c26].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <libkern/OSAtomic.h>

static OF_INLINE int
of_atomic_int_add(volatile int *_Nonnull p, int i)
{
	return OSAtomicAdd32(i, p);
}

static OF_INLINE int32_t
of_atomic_int32_add(volatile int32_t *_Nonnull p, int32_t i)
{
	return OSAtomicAdd32(i, p);
}

static OF_INLINE void *_Nullable
of_atomic_ptr_add(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
#ifdef __LP64__
	return (void *)OSAtomicAdd64(i, (int64_t *)p);
#else
	return (void *)OSAtomicAdd32(i, (int32_t *)p);
#endif
}

static OF_INLINE int
of_atomic_int_sub(volatile int *_Nonnull p, int i)
{
	return OSAtomicAdd32(-i, p);
}

static OF_INLINE int32_t
of_atomic_int32_sub(volatile int32_t *_Nonnull p, int32_t i)
{
	return OSAtomicAdd32(-i, p);
}

static OF_INLINE void *_Nullable
of_atomic_ptr_sub(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
#ifdef __LP64__
	return (void *)OSAtomicAdd64(-i, (int64_t *)p);
#else
	return (void *)OSAtomicAdd32(-i, (int32_t *)p);
#endif
}

static OF_INLINE int
of_atomic_int_inc(volatile int *_Nonnull p)
{
	return OSAtomicIncrement32(p);
}

static OF_INLINE int32_t
of_atomic_int32_inc(volatile int32_t *_Nonnull p)
{
	return OSAtomicIncrement32(p);
}

static OF_INLINE int
of_atomic_int_dec(volatile int *_Nonnull p)
{
	return OSAtomicDecrement32(p);
}

static OF_INLINE int32_t
of_atomic_int32_dec(volatile int32_t *_Nonnull p)
{
	return OSAtomicDecrement32(p);
}

static OF_INLINE unsigned int
of_atomic_int_or(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return OSAtomicOr32(i, p);
}

static OF_INLINE uint32_t
of_atomic_int32_or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return OSAtomicOr32(i, p);
}

static OF_INLINE unsigned int
of_atomic_int_and(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return OSAtomicAnd32(i, p);
}

static OF_INLINE uint32_t
of_atomic_int32_and(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return OSAtomicAnd32(i, p);
}

static OF_INLINE unsigned int
of_atomic_int_xor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return OSAtomicXor32(i, p);
}

static OF_INLINE uint32_t
of_atomic_int32_xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return OSAtomicXor32(i, p);
}

static OF_INLINE bool
of_atomic_int_cmpswap(volatile int *_Nonnull p, int o, int n)
{
	return OSAtomicCompareAndSwapInt(o, n, p);
}

static OF_INLINE bool
of_atomic_int32_cmpswap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	return OSAtomicCompareAndSwap32(o, n, p);
}

static OF_INLINE bool
of_atomic_ptr_cmpswap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	return OSAtomicCompareAndSwapPtr(o, n, p);
}

static OF_INLINE void
of_memory_barrier(void)
{
	OSMemoryBarrier();
}

static OF_INLINE void
of_memory_barrier_acquire(void)
{
	OSMemoryBarrier();
}

static OF_INLINE void
of_memory_barrier_release(void)
{
	OSMemoryBarrier();
}

<
<
|
















|





|





|









|





|





|









|





|





|





|





|





|





|





|





|





|





|





|





|






|





|





|



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


 * 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 <libkern/OSAtomic.h>

static OF_INLINE int
OFAtomicIntAdd(volatile int *_Nonnull p, int i)
{
	return OSAtomicAdd32(i, p);
}

static OF_INLINE int32_t
OFAtomicInt32Add(volatile int32_t *_Nonnull p, int32_t i)
{
	return OSAtomicAdd32(i, p);
}

static OF_INLINE void *_Nullable
OFAtomicPointerAdd(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
#ifdef __LP64__
	return (void *)OSAtomicAdd64(i, (int64_t *)p);
#else
	return (void *)OSAtomicAdd32(i, (int32_t *)p);
#endif
}

static OF_INLINE int
OFAtomicIntSubtract(volatile int *_Nonnull p, int i)
{
	return OSAtomicAdd32(-i, p);
}

static OF_INLINE int32_t
OFAtomicInt32Subtract(volatile int32_t *_Nonnull p, int32_t i)
{
	return OSAtomicAdd32(-i, p);
}

static OF_INLINE void *_Nullable
OFAtomicPointerSubtract(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
#ifdef __LP64__
	return (void *)OSAtomicAdd64(-i, (int64_t *)p);
#else
	return (void *)OSAtomicAdd32(-i, (int32_t *)p);
#endif
}

static OF_INLINE int
OFAtomicIntIncrease(volatile int *_Nonnull p)
{
	return OSAtomicIncrement32(p);
}

static OF_INLINE int32_t
OFAtomicInt32Increase(volatile int32_t *_Nonnull p)
{
	return OSAtomicIncrement32(p);
}

static OF_INLINE int
OFAtomicIntDecrease(volatile int *_Nonnull p)
{
	return OSAtomicDecrement32(p);
}

static OF_INLINE int32_t
OFAtomicInt32Decrease(volatile int32_t *_Nonnull p)
{
	return OSAtomicDecrement32(p);
}

static OF_INLINE unsigned int
OFAtomicIntOr(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return OSAtomicOr32(i, p);
}

static OF_INLINE uint32_t
OFAtomicInt32Or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return OSAtomicOr32(i, p);
}

static OF_INLINE unsigned int
OFAtomicIntAnd(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return OSAtomicAnd32(i, p);
}

static OF_INLINE uint32_t
OFAtomicInt32And(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return OSAtomicAnd32(i, p);
}

static OF_INLINE unsigned int
OFAtomicIntXor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	return OSAtomicXor32(i, p);
}

static OF_INLINE uint32_t
OFAtomicInt32Xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	return OSAtomicXor32(i, p);
}

static OF_INLINE bool
OFAtomicIntCompareAndSwap(volatile int *_Nonnull p, int o, int n)
{
	return OSAtomicCompareAndSwapInt(o, n, p);
}

static OF_INLINE bool
OFAtomicInt32CompareAndSwap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	return OSAtomicCompareAndSwap32(o, n, p);
}

static OF_INLINE bool
OFAtomicPointerCompareAndSwap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	return OSAtomicCompareAndSwapPtr(o, n, p);
}

static OF_INLINE void
OFMemoryBarrier(void)
{
	OSMemoryBarrier();
}

static OF_INLINE void
OFAcquireMemoryBarrier(void)
{
	OSMemoryBarrier();
}

static OF_INLINE void
OFReleaseMemoryBarrier(void)
{
	OSMemoryBarrier();
}

Modified src/platform/posix/OFString+PathAdditions.m from [b28e56e3b6] to [33c84f0ae4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret, *fileName;
	size_t pos;

	fileName = self.lastPathComponent;
	pos = [fileName rangeOfString: @"."
			      options: OF_STRING_SEARCH_BACKWARDS].location;
	if (pos == OF_NOT_FOUND || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return @"";
	}

	ret = [fileName substringFromIndex: pos + 1];

	[ret retain];







|
|







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret, *fileName;
	size_t pos;

	fileName = self.lastPathComponent;
	pos = [fileName rangeOfString: @"."
			      options: OFStringSearchBackwards].location;
	if (pos == OFNotFound || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return @"";
	}

	ret = [fileName substringFromIndex: pos + 1];

	[ret retain];
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
		return [[self copy] autorelease];

	pool = objc_autoreleasePoolPush();
	components = [[self.pathComponents mutableCopy] autorelease];
	fileName = components.lastObject;

	pos = [fileName rangeOfString: @"."
			      options: OF_STRING_SEARCH_BACKWARDS].location;
	if (pos == OF_NOT_FOUND || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return [[self copy] autorelease];
	}

	fileName = [fileName substringToIndex: pos];
	[components replaceObjectAtIndex: [components count] - 1
			      withObject: fileName];







|
|







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
		return [[self copy] autorelease];

	pool = objc_autoreleasePoolPush();
	components = [[self.pathComponents mutableCopy] autorelease];
	fileName = components.lastObject;

	pos = [fileName rangeOfString: @"."
			      options: OFStringSearchBackwards].location;
	if (pos == OFNotFound || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return [[self copy] autorelease];
	}

	fileName = [fileName substringToIndex: pos];
	[components replaceObjectAtIndex: [components count] - 1
			      withObject: fileName];
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
				done = false;
				break;
			}

			if ([component isEqual: @".."] &&
			    parent != nil && ![parent isEqual: @".."]) {
				[array removeObjectsInRange:
				    of_range(i - 1, 2)];

				done = false;
				break;
			}
		}
	}

	if (startsWithSlash)
		[array insertObject: @""
			    atIndex: 0];

	if ([self hasSuffix: @"/"])
		[array addObject: @""];

	ret = [[array componentsJoinedByString: @"/"] retain];

	objc_autoreleasePoolPop(pool);







|








|
<







273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
				done = false;
				break;
			}

			if ([component isEqual: @".."] &&
			    parent != nil && ![parent isEqual: @".."]) {
				[array removeObjectsInRange:
				    OFRangeMake(i - 1, 2)];

				done = false;
				break;
			}
		}
	}

	if (startsWithSlash)
		[array insertObject: @"" atIndex: 0];


	if ([self hasSuffix: @"/"])
		[array addObject: @""];

	ret = [[array componentsJoinedByString: @"/"] retain];

	objc_autoreleasePoolPop(pool);

Modified src/platform/windows/OFString+PathAdditions.m from [3c2f48d580] to [3d18ffcbdd].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

/*
 * This file is also used for MS-DOS! Don't forget to #ifdef Windows-specific
 * parts!
 */

#include "config.h"

#import "OFString+PathAdditions.h"
#import "OFArray.h"
#import "OFFileURLHandler.h"

<
<
|














|
|







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


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

/*
 * This file is also used for MS-DOS and MiNT! Don't forget to #ifdef
 * Windows-specific parts!
 */

#include "config.h"

#import "OFString+PathAdditions.h"
#import "OFArray.h"
#import "OFFileURLHandler.h"
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret, *fileName;
	size_t pos;

	fileName = self.lastPathComponent;
	pos = [fileName rangeOfString: @"."
			      options: OF_STRING_SEARCH_BACKWARDS].location;
	if (pos == OF_NOT_FOUND || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return @"";
	}

	ret = [fileName substringFromIndex: pos + 1];

	[ret retain];







|
|







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
{
	void *pool = objc_autoreleasePoolPush();
	OFString *ret, *fileName;
	size_t pos;

	fileName = self.lastPathComponent;
	pos = [fileName rangeOfString: @"."
			      options: OFStringSearchBackwards].location;
	if (pos == OFNotFound || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return @"";
	}

	ret = [fileName substringFromIndex: pos + 1];

	[ret retain];
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
		}

		objc_autoreleasePoolPop(pool);
		return @".";
	}

	components = [components objectsInRange:
	    of_range(0, components.count - 1)];
	ret = [OFString pathWithComponents: components];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}








|







199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
		}

		objc_autoreleasePoolPop(pool);
		return @".";
	}

	components = [components objectsInRange:
	    OFRangeMake(0, components.count - 1)];
	ret = [OFString pathWithComponents: components];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
		return [[self copy] autorelease];

	pool = objc_autoreleasePoolPush();
	components = [[self.pathComponents mutableCopy] autorelease];
	fileName = components.lastObject;

	pos = [fileName rangeOfString: @"."
			      options: OF_STRING_SEARCH_BACKWARDS].location;
	if (pos == OF_NOT_FOUND || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return [[self copy] autorelease];
	}

	fileName = [fileName substringToIndex: pos];
	[components replaceObjectAtIndex: components.count - 1
			      withObject: fileName];







|
|







222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
		return [[self copy] autorelease];

	pool = objc_autoreleasePoolPush();
	components = [[self.pathComponents mutableCopy] autorelease];
	fileName = components.lastObject;

	pos = [fileName rangeOfString: @"."
			      options: OFStringSearchBackwards].location;
	if (pos == OFNotFound || pos == 0) {
		objc_autoreleasePoolPop(pool);
		return [[self copy] autorelease];
	}

	fileName = [fileName substringToIndex: pos];
	[components replaceObjectAtIndex: components.count - 1
			      withObject: fileName];
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
			if ([component isEqual: @".."] && parent != nil &&
			    ![parent isEqual: @".."] &&
			    ![parent hasSuffix: @":"] &&
			    ![parent hasSuffix: @":\\"] &&
			    ![parent hasSuffix: @"://"] &&
			    (![parent hasPrefix: @"\\"] || i != 1)) {
				[array removeObjectsInRange:
				    of_range(i - 1, 2)];

				done = false;
				break;
			}
		}
	}








|







284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
			if ([component isEqual: @".."] && parent != nil &&
			    ![parent isEqual: @".."] &&
			    ![parent hasSuffix: @":"] &&
			    ![parent hasSuffix: @":\\"] &&
			    ![parent hasSuffix: @"://"] &&
			    (![parent hasPrefix: @"\\"] || i != 1)) {
				[array removeObjectsInRange:
				    OFRangeMake(i - 1, 2)];

				done = false;
				break;
			}
		}
	}

337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
		if (components.count < 2)
			@throw [OFInvalidFormatException exception];

		*URLEncodedHost = [[components objectAtIndex: 1]
		     stringByURLEncodingWithAllowedCharacters:
		     [OFCharacterSet URLHostAllowedCharacterSet]];
		path = [OFString pathWithComponents: [components
		    objectsInRange: of_range(2, components.count - 2)]];
	}

	path = [path stringByReplacingOccurrencesOfString: @"\\"
					       withString: @"/"];
	path = [path stringByPrependingString: @"/"];

	return path;







|







335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
		if (components.count < 2)
			@throw [OFInvalidFormatException exception];

		*URLEncodedHost = [[components objectAtIndex: 1]
		     stringByURLEncodingWithAllowedCharacters:
		     [OFCharacterSet URLHostAllowedCharacterSet]];
		path = [OFString pathWithComponents: [components
		    objectsInRange: OFRangeMake(2, components.count - 2)]];
	}

	path = [path stringByReplacingOccurrencesOfString: @"\\"
					       withString: @"/"];
	path = [path stringByPrependingString: @"/"];

	return path;

Renamed and modified src/atomic_x86.h [61a2fa833b] to src/platform/x86/OFAtomic.h [db489edc3a].

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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 */

OF_ASSUME_NONNULL_BEGIN

static OF_INLINE int
of_atomic_int_add(volatile int *_Nonnull p, int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "lock\n\t"
		    "xaddl	%0, %2\n\t"
		    "addl	%1, %0"
		    : "+&r"(i)
		    : "r"(i), "m"(*p)
		);
#ifdef OF_X86_64_ASM
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "lock\n\t"
		    "xaddq	%0, %2\n\t"
		    "addq	%1, %0"
		    : "+&r"(i)
		    : "r"(i), "m"(*p)
		);
#endif
	else
		abort();

	return i;
}

static OF_INLINE int32_t
of_atomic_int32_add(volatile int32_t *_Nonnull p, int32_t i)
{
	__asm__ __volatile__ (
	    "lock\n\t"
	    "xaddl	%0, %2\n\t"
	    "addl	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return i;
}

static OF_INLINE void *_Nullable
of_atomic_ptr_add(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
#if defined(OF_X86_64_ASM)
	__asm__ __volatile__ (
	    "lock\n\t"
	    "xaddq	%0, %2\n\t"
	    "addq	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return (void *)i;
#elif defined(OF_X86_ASM)
	__asm__ __volatile__ (
	    "lock\n\t"
	    "xaddl	%0, %2\n\t"
	    "addl	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return (void *)i;
#endif
}

static OF_INLINE int
of_atomic_int_sub(volatile int *_Nonnull p, int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "negl	%0\n\t"
		    "lock\n\t"
		    "xaddl	%0, %2\n\t"
		    "subl	%1, %0"
		    : "+&r"(i)
		    : "r"(i), "m"(*p)
		);
#ifdef OF_X86_64_ASM
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "negq	%0\n\t"
		    "lock\n\t"
		    "xaddq	%0, %2\n\t"
		    "subq	%1, %0"
		    : "+&r"(i)
		    : "r"(i), "m"(*p)
		);
#endif
	else
		abort();

	return i;
}

static OF_INLINE int32_t
of_atomic_int32_sub(volatile int32_t *_Nonnull p, int32_t i)
{
	__asm__ __volatile__ (
	    "negl	%0\n\t"
	    "lock\n\t"
	    "xaddl	%0, %2\n\t"
	    "subl	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return i;
}

static OF_INLINE void *_Nullable
of_atomic_ptr_sub(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
#if defined(OF_X86_64_ASM)
	__asm__ __volatile__ (
	    "negq	%0\n\t"
	    "lock\n\t"
	    "xaddq	%0, %2\n\t"
	    "subq	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return (void *)i;
#elif defined(OF_X86_ASM)
	__asm__ __volatile__ (
	    "negl	%0\n\t"
	    "lock\n\t"
	    "xaddl	%0, %2\n\t"
	    "subl	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return (void *)i;
#endif
}

static OF_INLINE int
of_atomic_int_inc(volatile int *_Nonnull p)
{
	int i;

	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "xorl	%0, %0\n\t"
		    "incl	%0\n\t"
		    "lock\n\t"
		    "xaddl	%0, %1\n\t"
		    "incl	%0"
		    : "=&r"(i)
		    : "m"(*p)
		);
#ifdef OF_X86_64_ASM
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "xorq	%0, %0\n\t"
		    "incq	%0\n\t"
		    "lock\n\t"
		    "xaddq	%0, %1\n\t"
		    "incq	%0"
		    : "=&r"(i)
		    : "m"(*p)
		);
#endif
	else
		abort();

	return i;
}

static OF_INLINE int32_t
of_atomic_int32_inc(volatile int32_t *_Nonnull p)
{
	int32_t i;

	__asm__ __volatile__ (
	    "xorl	%0, %0\n\t"
	    "incl	%0\n\t"
	    "lock\n\t"
	    "xaddl	%0, %1\n\t"
	    "incl	%0"
	    : "=&r"(i)
	    : "m"(*p)
	);

	return i;
}

static OF_INLINE int
of_atomic_int_dec(volatile int *_Nonnull p)
{
	int i;

	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "xorl	%0, %0\n\t"
		    "decl	%0\n\t"
		    "lock\n\t"
		    "xaddl	%0, %1\n\t"
		    "decl	%0"
		    : "=&r"(i)
		    : "m"(*p)
		);
#ifdef OF_X86_64_ASM
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "xorq	%0, %0\n\t"
		    "decq	%0\n\t"
		    "lock\n\t"
		    "xaddq	%0, %1\n\t"
		    "decq	%0"
		    : "=&r"(i)
		    : "m"(*p)
		);
#endif
	else
		abort();

	return i;
}

static OF_INLINE int32_t
of_atomic_int32_dec(volatile int32_t *_Nonnull p)
{
	int32_t i;

	__asm__ __volatile__ (
	    "xorl	%0, %0\n\t"
	    "decl	%0\n\t"
	    "lock\n\t"
	    "xaddl	%0, %1\n\t"
	    "decl	%0"
	    : "=&r"(i)
	    : "m"(*p)
	);

	return i;
}

static OF_INLINE unsigned int
of_atomic_int_or(volatile unsigned int *_Nonnull p, unsigned int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movl	%2, %0\n\t"
		    "movl	%0, %%eax\n\t"
		    "orl	%1, %0\n\t"
		    "lock\n\t"
		    "cmpxchg	%0, %2\n\t"
		    "jne	0b"
		    : "=&r"(i)
		    : "r"(i), "m"(*p)
		    : "eax", "cc"
		);
#ifdef OF_X86_64_ASM
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movq	%2, %0\n\t"
		    "movq	%0, %%rax\n\t"
		    "orq	%1, %0\n\t"
		    "lock\n\t"

<
<
|
















|









|
















|













|

|









|













|










|

















|














|

|










|














|













|


















|

















|













|


















|

















|














|







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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*


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

OF_ASSUME_NONNULL_BEGIN

static OF_INLINE int
OFAtomicIntAdd(volatile int *_Nonnull p, int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "lock\n\t"
		    "xaddl	%0, %2\n\t"
		    "addl	%1, %0"
		    : "+&r"(i)
		    : "r"(i), "m"(*p)
		);
#ifdef OF_X86_64
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "lock\n\t"
		    "xaddq	%0, %2\n\t"
		    "addq	%1, %0"
		    : "+&r"(i)
		    : "r"(i), "m"(*p)
		);
#endif
	else
		abort();

	return i;
}

static OF_INLINE int32_t
OFAtomicInt32Add(volatile int32_t *_Nonnull p, int32_t i)
{
	__asm__ __volatile__ (
	    "lock\n\t"
	    "xaddl	%0, %2\n\t"
	    "addl	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return i;
}

static OF_INLINE void *_Nullable
OFAtomicPointerAdd(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
#if defined(OF_X86_64)
	__asm__ __volatile__ (
	    "lock\n\t"
	    "xaddq	%0, %2\n\t"
	    "addq	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return (void *)i;
#elif defined(OF_X86)
	__asm__ __volatile__ (
	    "lock\n\t"
	    "xaddl	%0, %2\n\t"
	    "addl	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return (void *)i;
#endif
}

static OF_INLINE int
OFAtomicIntSubtract(volatile int *_Nonnull p, int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "negl	%0\n\t"
		    "lock\n\t"
		    "xaddl	%0, %2\n\t"
		    "subl	%1, %0"
		    : "+&r"(i)
		    : "r"(i), "m"(*p)
		);
#ifdef OF_X86_64
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "negq	%0\n\t"
		    "lock\n\t"
		    "xaddq	%0, %2\n\t"
		    "subq	%1, %0"
		    : "+&r"(i)
		    : "r"(i), "m"(*p)
		);
#endif
	else
		abort();

	return i;
}

static OF_INLINE int32_t
OFAtomicInt32Subtract(volatile int32_t *_Nonnull p, int32_t i)
{
	__asm__ __volatile__ (
	    "negl	%0\n\t"
	    "lock\n\t"
	    "xaddl	%0, %2\n\t"
	    "subl	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return i;
}

static OF_INLINE void *_Nullable
OFAtomicPointerSubtract(void *volatile _Nullable *_Nonnull p, intptr_t i)
{
#if defined(OF_X86_64)
	__asm__ __volatile__ (
	    "negq	%0\n\t"
	    "lock\n\t"
	    "xaddq	%0, %2\n\t"
	    "subq	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return (void *)i;
#elif defined(OF_X86)
	__asm__ __volatile__ (
	    "negl	%0\n\t"
	    "lock\n\t"
	    "xaddl	%0, %2\n\t"
	    "subl	%1, %0"
	    : "+&r"(i)
	    : "r"(i), "m"(*p)
	);

	return (void *)i;
#endif
}

static OF_INLINE int
OFAtomicIntIncrease(volatile int *_Nonnull p)
{
	int i;

	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "xorl	%0, %0\n\t"
		    "incl	%0\n\t"
		    "lock\n\t"
		    "xaddl	%0, %1\n\t"
		    "incl	%0"
		    : "=&r"(i)
		    : "m"(*p)
		);
#ifdef OF_X86_64
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "xorq	%0, %0\n\t"
		    "incq	%0\n\t"
		    "lock\n\t"
		    "xaddq	%0, %1\n\t"
		    "incq	%0"
		    : "=&r"(i)
		    : "m"(*p)
		);
#endif
	else
		abort();

	return i;
}

static OF_INLINE int32_t
OFAtomicInt32Increase(volatile int32_t *_Nonnull p)
{
	int32_t i;

	__asm__ __volatile__ (
	    "xorl	%0, %0\n\t"
	    "incl	%0\n\t"
	    "lock\n\t"
	    "xaddl	%0, %1\n\t"
	    "incl	%0"
	    : "=&r"(i)
	    : "m"(*p)
	);

	return i;
}

static OF_INLINE int
OFAtomicIntDecrease(volatile int *_Nonnull p)
{
	int i;

	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "xorl	%0, %0\n\t"
		    "decl	%0\n\t"
		    "lock\n\t"
		    "xaddl	%0, %1\n\t"
		    "decl	%0"
		    : "=&r"(i)
		    : "m"(*p)
		);
#ifdef OF_X86_64
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "xorq	%0, %0\n\t"
		    "decq	%0\n\t"
		    "lock\n\t"
		    "xaddq	%0, %1\n\t"
		    "decq	%0"
		    : "=&r"(i)
		    : "m"(*p)
		);
#endif
	else
		abort();

	return i;
}

static OF_INLINE int32_t
OFAtomicInt32Decrease(volatile int32_t *_Nonnull p)
{
	int32_t i;

	__asm__ __volatile__ (
	    "xorl	%0, %0\n\t"
	    "decl	%0\n\t"
	    "lock\n\t"
	    "xaddl	%0, %1\n\t"
	    "decl	%0"
	    : "=&r"(i)
	    : "m"(*p)
	);

	return i;
}

static OF_INLINE unsigned int
OFAtomicIntOr(volatile unsigned int *_Nonnull p, unsigned int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movl	%2, %0\n\t"
		    "movl	%0, %%eax\n\t"
		    "orl	%1, %0\n\t"
		    "lock\n\t"
		    "cmpxchg	%0, %2\n\t"
		    "jne	0b"
		    : "=&r"(i)
		    : "r"(i), "m"(*p)
		    : "eax", "cc"
		);
#ifdef OF_X86_64
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movq	%2, %0\n\t"
		    "movq	%0, %%rax\n\t"
		    "orq	%1, %0\n\t"
		    "lock\n\t"
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
	else
		abort();

	return i;
}

static OF_INLINE uint32_t
of_atomic_int32_or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "movl	%2, %0\n\t"
	    "movl	%0, %%eax\n\t"
	    "orl	%1, %0\n\t"
	    "lock\n\t"
	    "cmpxchg	%0, %2\n\t"
	    "jne	0b"
	    : "=&r"(i)
	    : "r"(i), "m"(*p)
	    : "eax", "cc"
	);

	return i;
}

static OF_INLINE unsigned int
of_atomic_int_and(volatile unsigned int *_Nonnull p, unsigned int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movl	%2, %0\n\t"
		    "movl	%0, %%eax\n\t"
		    "andl	%1, %0\n\t"
		    "lock\n\t"
		    "cmpxchg	%0, %2\n\t"
		    "jne	0b"
		    : "=&r"(i)
		    : "r"(i), "m"(*p)
		    : "eax", "cc"
		);
#ifdef OF_X86_64_ASM
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movq	%2, %0\n\t"
		    "movq	%0, %%rax\n\t"
		    "andq	%1, %0\n\t"
		    "lock\n\t"







|


















|














|







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
	else
		abort();

	return i;
}

static OF_INLINE uint32_t
OFAtomicInt32Or(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "movl	%2, %0\n\t"
	    "movl	%0, %%eax\n\t"
	    "orl	%1, %0\n\t"
	    "lock\n\t"
	    "cmpxchg	%0, %2\n\t"
	    "jne	0b"
	    : "=&r"(i)
	    : "r"(i), "m"(*p)
	    : "eax", "cc"
	);

	return i;
}

static OF_INLINE unsigned int
OFAtomicIntAnd(volatile unsigned int *_Nonnull p, unsigned int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movl	%2, %0\n\t"
		    "movl	%0, %%eax\n\t"
		    "andl	%1, %0\n\t"
		    "lock\n\t"
		    "cmpxchg	%0, %2\n\t"
		    "jne	0b"
		    : "=&r"(i)
		    : "r"(i), "m"(*p)
		    : "eax", "cc"
		);
#ifdef OF_X86_64
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movq	%2, %0\n\t"
		    "movq	%0, %%rax\n\t"
		    "andq	%1, %0\n\t"
		    "lock\n\t"
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
	else
		abort();

	return i;
}

static OF_INLINE uint32_t
of_atomic_int32_and(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "movl	%2, %0\n\t"
	    "movl	%0, %%eax\n\t"
	    "andl	%1, %0\n\t"
	    "lock\n\t"
	    "cmpxchg	%0, %2\n\t"
	    "jne	0b"
	    : "=&r"(i)
	    : "r"(i), "m"(*p)
	    : "eax", "cc"
	);

	return i;
}

static OF_INLINE unsigned int
of_atomic_int_xor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movl	%2, %0\n\t"
		    "movl	%0, %%eax\n\t"
		    "xorl	%1, %0\n\t"
		    "lock\n\t"
		    "cmpxchg	%0, %2\n\t"
		    "jne	0b"
		    : "=&r"(i)
		    : "r"(i), "m"(*p)
		    : "eax", "cc"
		);
#ifdef OF_X86_64_ASM
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movq	%2, %0\n\t"
		    "movq	%0, %%rax\n\t"
		    "xorq	%1, %0\n\t"
		    "lock\n\t"







|


















|














|







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
	else
		abort();

	return i;
}

static OF_INLINE uint32_t
OFAtomicInt32And(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "movl	%2, %0\n\t"
	    "movl	%0, %%eax\n\t"
	    "andl	%1, %0\n\t"
	    "lock\n\t"
	    "cmpxchg	%0, %2\n\t"
	    "jne	0b"
	    : "=&r"(i)
	    : "r"(i), "m"(*p)
	    : "eax", "cc"
	);

	return i;
}

static OF_INLINE unsigned int
OFAtomicIntXor(volatile unsigned int *_Nonnull p, unsigned int i)
{
	if (sizeof(int) == 4)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movl	%2, %0\n\t"
		    "movl	%0, %%eax\n\t"
		    "xorl	%1, %0\n\t"
		    "lock\n\t"
		    "cmpxchg	%0, %2\n\t"
		    "jne	0b"
		    : "=&r"(i)
		    : "r"(i), "m"(*p)
		    : "eax", "cc"
		);
#ifdef OF_X86_64
	else if (sizeof(int) == 8)
		__asm__ __volatile__ (
		    "0:\n\t"
		    "movq	%2, %0\n\t"
		    "movq	%0, %%rax\n\t"
		    "xorq	%1, %0\n\t"
		    "lock\n\t"
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
	else
		abort();

	return i;
}

static OF_INLINE uint32_t
of_atomic_int32_xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "movl	%2, %0\n\t"
	    "movl	%0, %%eax\n\t"
	    "xorl	%1, %0\n\t"
	    "lock\n\t"
	    "cmpxchgl	%0, %2\n\t"
	    "jne	0b"
	    : "=&r"(i)
	    : "r"(i), "m"(*p)
	    : "eax", "cc"
	);

	return i;
}

static OF_INLINE bool
of_atomic_int_cmpswap(volatile int *_Nonnull p, int o, int n)
{
	int r;

	__asm__ __volatile__ (
	    "lock\n\t"
	    "cmpxchg	%2, %3\n\t"
	    "sete	%b0\n\t"
	    "movzbl	%b0, %0"
	    : "=&d"(r), "+a"(o)	/* use d instead of r to avoid a gcc bug */
	    : "r"(n), "m"(*p)
	    : "cc"
	);

	return r;
}

static OF_INLINE bool
of_atomic_int32_cmpswap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	int r;

	__asm__ __volatile__ (
	    "lock\n\t"
	    "cmpxchg	%2, %3\n\t"
	    "sete	%b0\n\t"
	    "movzbl	%b0, %0"
	    : "=&d"(r), "+a"(o)	/* use d instead of r to avoid a gcc bug */
	    : "r"(n), "m"(*p)
	    : "cc"
	);

	return r;
}

static OF_INLINE bool
of_atomic_ptr_cmpswap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	int r;

	__asm__ __volatile__ (
	    "lock\n\t"
	    "cmpxchg	%2, %3\n\t"
	    "sete	%b0\n\t"
	    "movzbl	%b0, %0"
	    : "=&d"(r), "+a"(o)	/* use d instead of r to avoid a gcc bug */
	    : "r"(n), "m"(*p)
	    : "cc"
	);

	return r;
}

static OF_INLINE void
of_memory_barrier(void)
{
	__asm__ __volatile__ (
	    "mfence" ::: "memory"
	);
}

static OF_INLINE void
of_memory_barrier_acquire(void)
{
	__asm__ __volatile__ ("" ::: "memory");
}

static OF_INLINE void
of_memory_barrier_release(void)
{
	__asm__ __volatile__ ("" ::: "memory");
}

OF_ASSUME_NONNULL_END







|


















|

















|

















|


















|







|





|





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
	else
		abort();

	return i;
}

static OF_INLINE uint32_t
OFAtomicInt32Xor(volatile uint32_t *_Nonnull p, uint32_t i)
{
	__asm__ __volatile__ (
	    "0:\n\t"
	    "movl	%2, %0\n\t"
	    "movl	%0, %%eax\n\t"
	    "xorl	%1, %0\n\t"
	    "lock\n\t"
	    "cmpxchgl	%0, %2\n\t"
	    "jne	0b"
	    : "=&r"(i)
	    : "r"(i), "m"(*p)
	    : "eax", "cc"
	);

	return i;
}

static OF_INLINE bool
OFAtomicIntCompareAndSwap(volatile int *_Nonnull p, int o, int n)
{
	int r;

	__asm__ __volatile__ (
	    "lock\n\t"
	    "cmpxchg	%2, %3\n\t"
	    "sete	%b0\n\t"
	    "movzbl	%b0, %0"
	    : "=&d"(r), "+a"(o)	/* use d instead of r to avoid a gcc bug */
	    : "r"(n), "m"(*p)
	    : "cc"
	);

	return r;
}

static OF_INLINE bool
OFAtomicInt32CompareAndSwap(volatile int32_t *_Nonnull p, int32_t o, int32_t n)
{
	int r;

	__asm__ __volatile__ (
	    "lock\n\t"
	    "cmpxchg	%2, %3\n\t"
	    "sete	%b0\n\t"
	    "movzbl	%b0, %0"
	    : "=&d"(r), "+a"(o)	/* use d instead of r to avoid a gcc bug */
	    : "r"(n), "m"(*p)
	    : "cc"
	);

	return r;
}

static OF_INLINE bool
OFAtomicPointerCompareAndSwap(void *volatile _Nullable *_Nonnull p,
    void *_Nullable o, void *_Nullable n)
{
	int r;

	__asm__ __volatile__ (
	    "lock\n\t"
	    "cmpxchg	%2, %3\n\t"
	    "sete	%b0\n\t"
	    "movzbl	%b0, %0"
	    : "=&d"(r), "+a"(o)	/* use d instead of r to avoid a gcc bug */
	    : "r"(n), "m"(*p)
	    : "cc"
	);

	return r;
}

static OF_INLINE void
OFMemoryBarrier(void)
{
	__asm__ __volatile__ (
	    "mfence" ::: "memory"
	);
}

static OF_INLINE void
OFAcquireMemoryBarrier(void)
{
	__asm__ __volatile__ ("" ::: "memory");
}

static OF_INLINE void
OFReleaseMemoryBarrier(void)
{
	__asm__ __volatile__ ("" ::: "memory");
}

OF_ASSUME_NONNULL_END

Modified src/runtime/Makefile from [0941bab434] to [6dd30f7bce].

1
2
3
4
5
6
7
8
9
10
11
12
include ../../extra.mk

SUBDIRS = lookup-asm
SUBDIRS_AFTER = ${LINKLIB}
CLEAN = amiga-library-functable.inc inline.h
DISTCLEAN = Info.plist

SHARED_LIB = ${OBJFWRT_SHARED_LIB}
STATIC_LIB = ${OBJFWRT_STATIC_LIB}
FRAMEWORK = ${OBJFWRT_FRAMEWORK}
AMIGA_LIB = ${OBJFWRT_AMIGA_LIB}
LIB_MAJOR = ${OBJFWRT_LIB_MAJOR}




<







1
2
3
4

5
6
7
8
9
10
11
include ../../extra.mk

SUBDIRS = lookup-asm
SUBDIRS_AFTER = ${LINKLIB}

DISTCLEAN = Info.plist

SHARED_LIB = ${OBJFWRT_SHARED_LIB}
STATIC_LIB = ${OBJFWRT_STATIC_LIB}
FRAMEWORK = ${OBJFWRT_FRAMEWORK}
AMIGA_LIB = ${OBJFWRT_AMIGA_LIB}
LIB_MAJOR = ${OBJFWRT_LIB_MAJOR}
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
       protocol.m		\
       selector.m		\
       sparsearray.m		\
       static-instances.m	\
       synchronized.m		\
       tagged-pointer.m		\
       ${USE_SRCS_THREADS}
SRCS_THREADS = threading.m	\
	       ../mutex.m	\
	       ../once.m	\
	       ../tlskey.m
INCLUDES = ObjFWRT.h
includesubdir = ObjFWRT

OBJS_EXTRA = ${LOOKUP_ASM_LOOKUP_ASM_A}
LIB_OBJS_EXTRA = ${LOOKUP_ASM_LOOKUP_ASM_LIB_A}
AMIGA_LIB_OBJS_START = amiga-library.amigalib.o
AMIGA_LIB_OBJS_EXTRA = amiga-glue.amigalib.o		\
		       ${LOOKUP_ASM_LOOKUP_ASM_A}	\
		       amiga-end.amigalib.o

include ../../buildsys.mk

${OBJFWRT_AMIGA_LIB}: inline.h

${SFDC_INLINE_H}: ${SFD_FILE}
	sfdc -q --target=${SFDC_TARGET} --mode=macros -o $@ $<; \

${CVINCLUDE_INLINE_H}: morphos.fd morphos-clib.h
	cvinclude.pl --quiet --fd=morphos.fd --clib=morphos-clib.h --inlines=$@

CPPFLAGS += -I. -I.. -I../..					\

	    -DOBJFWRT_AMIGA_LIB=\"${OBJFWRT_AMIGA_LIB}\"	\
	    -DOBJFWRT_LIB_MAJOR=${OBJFWRT_LIB_MAJOR}		\
	    -DOBJFWRT_LIB_MINOR=${OBJFWRT_LIB_MINOR}
AMIGA_LIB_CFLAGS += -DOBJC_COMPILING_AMIGA_LIBRARY
LD = ${OBJC}
FRAMEWORK_LIBS = ${LIBS}







|
|
|
|



|
|


|




<
<
<
<
<
<
<
<

>






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
       protocol.m		\
       selector.m		\
       sparsearray.m		\
       static-instances.m	\
       synchronized.m		\
       tagged-pointer.m		\
       ${USE_SRCS_THREADS}
SRCS_THREADS = OFOnce.m		\
	       OFPlainMutex.m	\
	       OFTLSKey.m	\
	       threading.m
INCLUDES = ObjFWRT.h
includesubdir = ObjFWRT

OBJS_EXTRA = lookup-asm/lookup-asm.a
LIB_OBJS_EXTRA = lookup-asm/lookup-asm.lib.a
AMIGA_LIB_OBJS_START = amiga-library.amigalib.o
AMIGA_LIB_OBJS_EXTRA = amiga-glue.amigalib.o		\
		       lookup-asm/lookup-asm.amigalib.a	\
		       amiga-end.amigalib.o

include ../../buildsys.mk









CPPFLAGS += -I. -I.. -I../..					\
	    -DOBJC_COMPILING_RUNTIME				\
	    -DOBJFWRT_AMIGA_LIB=\"${OBJFWRT_AMIGA_LIB}\"	\
	    -DOBJFWRT_LIB_MAJOR=${OBJFWRT_LIB_MAJOR}		\
	    -DOBJFWRT_LIB_MINOR=${OBJFWRT_LIB_MINOR}
AMIGA_LIB_CFLAGS += -DOBJC_COMPILING_AMIGA_LIBRARY
LD = ${OBJC}
FRAMEWORK_LIBS = ${LIBS}

Added src/runtime/OFOnce.m version [9cc54fa246].











































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

#import "ObjFWRT.h"
#import "private.h"

#include "../OFOnce.m"

Added src/runtime/OFPlainMutex.m version [1b4abcc3c8].











































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

#import "ObjFWRT.h"
#import "private.h"

#include "../OFPlainMutex.m"

Added src/runtime/OFTLSKey.m version [c512b1fdd8].











































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

#import "ObjFWRT.h"
#import "private.h"

#include "../OFTLSKey.m"

Modified src/runtime/ObjFWRT.h from [1dfaeda4bc] to [87156bda73].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
typedef id _Nullable (*IMP)(id _Nonnull object, SEL _Nonnull selector, ...);

/**
 * @brief A handler for uncaught exceptions.
 *
 * @param exception The exception which was not caught.
 */
typedef void (*objc_uncaught_exception_handler_t)(id _Nullable exception);

/**
 * @brief A handler for mutation during enumeration.
 *
 * @param object The object that was mutated during enumeration
 */
typedef void (*objc_enumeration_mutation_handler_t)(id _Nonnull object);

/**
 * @brief A struct representing a call to super.
 */
struct objc_super {
	/**
	 * @brief The object on which to perform the super call.







|






|







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
typedef id _Nullable (*IMP)(id _Nonnull object, SEL _Nonnull selector, ...);

/**
 * @brief A handler for uncaught exceptions.
 *
 * @param exception The exception which was not caught.
 */
typedef void (*objc_uncaught_exception_handler)(id _Nullable exception);

/**
 * @brief A handler for mutation during enumeration.
 *
 * @param object The object that was mutated during enumeration
 */
typedef void (*objc_enumeration_mutation_handler)(id _Nonnull object);

/**
 * @brief A struct representing a call to super.
 */
struct objc_super {
	/**
	 * @brief The object on which to perform the super call.
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
 * @return A copy of the attribute value. You need to call `free()` on it when
 *	   done.
 */
extern char *_Nullable property_copyAttributeValue(
    objc_property_t _Nonnull property, const char *_Nonnull name);

/**
 * @brief Exits the Objective-C runtime.
 *
 * This frees all data structures used by the runtime, after which Objective-C
 * can no longer be used inside the current process. This is only useful for
 * debugging.
 */
extern void objc_exit(void);

/**
 * @brief Sets the handler for uncaught exceptions.
 *
 * @param handler The new handler for uncaught exceptions
 * @return The old handler for uncaught exceptions
 */
extern _Nullable objc_uncaught_exception_handler_t
    objc_setUncaughtExceptionHandler(
    objc_uncaught_exception_handler_t _Nullable handler);

/**
 * @brief Sets the forwarding handler for unimplemented methods.
 *
 * @param forward The forwarding handler for regular methods
 * @param stretForward The forwarding handler for methods using the struct
 *		       return ABI
 */
extern void objc_setForwardHandler(IMP _Nullable forward,
    IMP _Nullable stretForward);

/**
 * @brief Sets the handler for mutations during enumeration.
 *
 * @param handler The handler for mutations during enumeration
 */
extern void objc_setEnumerationMutationHandler(
    objc_enumeration_mutation_handler_t _Nullable handler);

/**
 * @brief Constructs an instance of the specified class in the specified array
 *	  of bytes.
 *
 * @param class_ The class of which to construct an instance
 * @param bytes An array of bytes of at least the length of the instance size.







|



|

|







|

|

















|







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
 * @return A copy of the attribute value. You need to call `free()` on it when
 *	   done.
 */
extern char *_Nullable property_copyAttributeValue(
    objc_property_t _Nonnull property, const char *_Nonnull name);

/**
 * @brief Deinitializes the Objective-C runtime.
 *
 * This frees all data structures used by the runtime, after which Objective-C
 * can no longer be used inside the current process. This is only useful for
 * debugging and tests.
 */
extern void objc_deinit(void);

/**
 * @brief Sets the handler for uncaught exceptions.
 *
 * @param handler The new handler for uncaught exceptions
 * @return The old handler for uncaught exceptions
 */
extern _Nullable objc_uncaught_exception_handler
    objc_setUncaughtExceptionHandler(
    objc_uncaught_exception_handler _Nullable handler);

/**
 * @brief Sets the forwarding handler for unimplemented methods.
 *
 * @param forward The forwarding handler for regular methods
 * @param stretForward The forwarding handler for methods using the struct
 *		       return ABI
 */
extern void objc_setForwardHandler(IMP _Nullable forward,
    IMP _Nullable stretForward);

/**
 * @brief Sets the handler for mutations during enumeration.
 *
 * @param handler The handler for mutations during enumeration
 */
extern void objc_setEnumerationMutationHandler(
    objc_enumeration_mutation_handler _Nullable handler);

/**
 * @brief Constructs an instance of the specified class in the specified array
 *	  of bytes.
 *
 * @param class_ The class of which to construct an instance
 * @param bytes An array of bytes of at least the length of the instance size.

Modified src/runtime/amiga-end.m from [0d112ca98e] to [1fee00da70].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "platform.h"

#ifdef OF_MORPHOS
__asm__ (


    ".section .ctors, \"aw\", @progbits\n"
    "	.long 0\n"
);
#else
__asm__ (
    ""
);
#endif

<
<
|



















>
>
|
|






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


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

#import "platform.h"

#ifdef OF_MORPHOS
__asm__ (
    ".section .eh_frame, \"aw\"\n"
    "	.long 0\n"
    ".section .ctors, \"aw\"\n"
    "	.long 0"
);
#else
__asm__ (
    ""
);
#endif

Added src/runtime/amiga-funcarray.inc version [820bef97bb].





















































































































































































































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

/* This file is automatically generated from amiga-library.xml */

(CONST_APTR)glue_objc_init,
(CONST_APTR)glue___objc_exec_class,
(CONST_APTR)glue_objc_msg_lookup,
(CONST_APTR)glue_objc_msg_lookup_stret,
(CONST_APTR)glue_objc_msg_lookup_super,
(CONST_APTR)glue_objc_msg_lookup_super_stret,
(CONST_APTR)glue_objc_lookUpClass,
(CONST_APTR)glue_objc_getClass,
(CONST_APTR)glue_objc_getRequiredClass,
(CONST_APTR)glue_objc_lookup_class,
(CONST_APTR)glue_objc_get_class,
(CONST_APTR)glue_objc_exception_throw,
(CONST_APTR)glue_objc_sync_enter,
(CONST_APTR)glue_objc_sync_exit,
(CONST_APTR)glue_objc_getProperty,
(CONST_APTR)glue_objc_setProperty,
(CONST_APTR)glue_objc_getPropertyStruct,
(CONST_APTR)glue_objc_setPropertyStruct,
(CONST_APTR)glue_objc_enumerationMutation,
(CONST_APTR)glue___gnu_objc_personality,
(CONST_APTR)glue_objc_retain,
(CONST_APTR)glue_objc_retainBlock,
(CONST_APTR)glue_objc_retainAutorelease,
(CONST_APTR)glue_objc_release,
(CONST_APTR)glue_objc_autorelease,
(CONST_APTR)glue_objc_autoreleaseReturnValue,
(CONST_APTR)glue_objc_retainAutoreleaseReturnValue,
(CONST_APTR)glue_objc_retainAutoreleasedReturnValue,
(CONST_APTR)glue_objc_storeStrong,
(CONST_APTR)glue_objc_storeWeak,
(CONST_APTR)glue_objc_loadWeakRetained,
(CONST_APTR)glue_objc_initWeak,
(CONST_APTR)glue_objc_destroyWeak,
(CONST_APTR)glue_objc_loadWeak,
(CONST_APTR)glue_objc_copyWeak,
(CONST_APTR)glue_objc_moveWeak,
(CONST_APTR)glue_sel_registerName,
(CONST_APTR)glue_sel_getName,
(CONST_APTR)glue_sel_isEqual,
(CONST_APTR)glue_objc_allocateClassPair,
(CONST_APTR)glue_objc_registerClassPair,
(CONST_APTR)glue_objc_getClassList,
(CONST_APTR)glue_objc_copyClassList,
(CONST_APTR)glue_class_isMetaClass,
(CONST_APTR)glue_class_getName,
(CONST_APTR)glue_class_getSuperclass,
(CONST_APTR)glue_class_getInstanceSize,
(CONST_APTR)glue_class_respondsToSelector,
(CONST_APTR)glue_class_conformsToProtocol,
(CONST_APTR)glue_class_getMethodImplementation,
(CONST_APTR)glue_class_getMethodImplementation_stret,
(CONST_APTR)glue_class_getInstanceMethod,
(CONST_APTR)glue_class_addMethod,
(CONST_APTR)glue_class_replaceMethod,
(CONST_APTR)glue_object_getClass,
(CONST_APTR)glue_object_setClass,
(CONST_APTR)glue_object_getClassName,
(CONST_APTR)glue_protocol_getName,
(CONST_APTR)glue_protocol_isEqual,
(CONST_APTR)glue_protocol_conformsToProtocol,
(CONST_APTR)glue_objc_setUncaughtExceptionHandler,
(CONST_APTR)glue_objc_setForwardHandler,
(CONST_APTR)glue_objc_setEnumerationMutationHandler,
(CONST_APTR)glue_objc_constructInstance,
(CONST_APTR)glue_objc_deinit,
(CONST_APTR)glue_class_copyIvarList,
(CONST_APTR)glue_ivar_getName,
(CONST_APTR)glue_ivar_getTypeEncoding,
(CONST_APTR)glue_ivar_getOffset,
(CONST_APTR)glue_class_copyMethodList,
(CONST_APTR)glue_method_getName,
(CONST_APTR)glue_method_getTypeEncoding,
(CONST_APTR)glue_class_copyPropertyList,
(CONST_APTR)glue_property_getName,
(CONST_APTR)glue_property_copyAttributeValue,
(CONST_APTR)glue_objc_destructInstance,
(CONST_APTR)glue_objc_autoreleasePoolPush,
(CONST_APTR)glue_objc_autoreleasePoolPop,
(CONST_APTR)glue__objc_rootAutorelease,
(CONST_APTR)glue_objc_hashtable_new,
(CONST_APTR)glue_objc_hashtable_set,
(CONST_APTR)glue_objc_hashtable_get,
(CONST_APTR)glue_objc_hashtable_delete,
(CONST_APTR)glue_objc_hashtable_free,
(CONST_APTR)glue_objc_setTaggedPointerSecret,
(CONST_APTR)glue_objc_registerTaggedPointerClass,
(CONST_APTR)glue_object_isTaggedPointer,
(CONST_APTR)glue_object_getTaggedPointerValue,
(CONST_APTR)glue_objc_createTaggedPointer,

Added src/runtime/amiga-glue.h version [dc66f09f8a].















































































































































































































































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

/* This file is automatically generated from amiga-library.xml */

#import "ObjFWRT.h"
#import "private.h"

#ifdef OF_AMIGAOS_M68K
# define PPC_PARAMS(...) (void)
# define M68K_ARG(type, name, reg)		\
	register type reg##name __asm__(#reg);	\
	type name = reg##name;
#else
# define PPC_PARAMS(...) (__VA_ARGS__)
# define M68K_ARG(...)
#endif

extern bool glue_objc_init PPC_PARAMS(unsigned int version, struct objc_libc *libc);
extern void glue___objc_exec_class PPC_PARAMS(struct objc_module *_Nonnull module);
extern IMP _Nonnull glue_objc_msg_lookup PPC_PARAMS(id _Nullable object, SEL _Nonnull selector);
extern IMP _Nonnull glue_objc_msg_lookup_stret PPC_PARAMS(id _Nullable object, SEL _Nonnull selector);
extern IMP _Nonnull glue_objc_msg_lookup_super PPC_PARAMS(struct objc_super *_Nonnull super, SEL _Nonnull selector);
extern IMP _Nonnull glue_objc_msg_lookup_super_stret PPC_PARAMS(struct objc_super *_Nonnull super, SEL _Nonnull selector);
extern Class _Nullable glue_objc_lookUpClass PPC_PARAMS(const char *_Nonnull name);
extern Class _Nullable glue_objc_getClass PPC_PARAMS(const char *_Nonnull name);
extern Class _Nonnull glue_objc_getRequiredClass PPC_PARAMS(const char *_Nonnull name);
extern Class _Nullable glue_objc_lookup_class PPC_PARAMS(const char *_Nonnull name);
extern Class _Nonnull glue_objc_get_class PPC_PARAMS(const char *_Nonnull name);
extern void glue_objc_exception_throw PPC_PARAMS(id _Nonnull object);
extern int glue_objc_sync_enter PPC_PARAMS(id _Nullable object);
extern int glue_objc_sync_exit PPC_PARAMS(id _Nullable object);
extern id _Nullable glue_objc_getProperty PPC_PARAMS(id _Nonnull self, SEL _Nonnull _cmd, ptrdiff_t offset, bool atomic);
extern void glue_objc_setProperty PPC_PARAMS(id _Nonnull self, SEL _Nonnull _cmd, ptrdiff_t offset, id _Nullable value, bool atomic, signed char copy);
extern void glue_objc_getPropertyStruct PPC_PARAMS(void *_Nonnull dest, const void *_Nonnull src, ptrdiff_t size, bool atomic, bool strong);
extern void glue_objc_setPropertyStruct PPC_PARAMS(void *_Nonnull dest, const void *_Nonnull src, ptrdiff_t size, bool atomic, bool strong);
extern void glue_objc_enumerationMutation PPC_PARAMS(id _Nonnull object);
extern int glue___gnu_objc_personality PPC_PARAMS(int version, int actions, uint64_t *_Nonnull exClass, void *_Nonnull ex, void *_Nonnull ctx);
extern id _Nullable glue_objc_retain PPC_PARAMS(id _Nullable object);
extern id _Nullable glue_objc_retainBlock PPC_PARAMS(id _Nullable block);
extern id _Nullable glue_objc_retainAutorelease PPC_PARAMS(id _Nullable object);
extern void glue_objc_release PPC_PARAMS(id _Nullable object);
extern id _Nullable glue_objc_autorelease PPC_PARAMS(id _Nullable object);
extern id _Nullable glue_objc_autoreleaseReturnValue PPC_PARAMS(id _Nullable object);
extern id _Nullable glue_objc_retainAutoreleaseReturnValue PPC_PARAMS(id _Nullable object);
extern id _Nullable glue_objc_retainAutoreleasedReturnValue PPC_PARAMS(id _Nullable object);
extern id _Nullable glue_objc_storeStrong PPC_PARAMS(id _Nullable *_Nonnull object, id _Nullable value);
extern id _Nullable glue_objc_storeWeak PPC_PARAMS(id _Nullable *_Nonnull object, id _Nullable value);
extern id _Nullable glue_objc_loadWeakRetained PPC_PARAMS(id _Nullable *_Nonnull object);
extern id _Nullable glue_objc_initWeak PPC_PARAMS(id _Nullable *_Nonnull object, id _Nullable value);
extern void glue_objc_destroyWeak PPC_PARAMS(id _Nullable *_Nonnull object);
extern id _Nullable glue_objc_loadWeak PPC_PARAMS(id _Nullable *_Nonnull object);
extern void glue_objc_copyWeak PPC_PARAMS(id _Nullable *_Nonnull dest, id _Nullable *_Nonnull src);
extern void glue_objc_moveWeak PPC_PARAMS(id _Nullable *_Nonnull dest, id _Nullable *_Nonnull src);
extern SEL _Nonnull glue_sel_registerName PPC_PARAMS(const char *_Nonnull name);
extern const char *_Nonnull glue_sel_getName PPC_PARAMS(SEL _Nonnull selector);
extern bool glue_sel_isEqual PPC_PARAMS(SEL _Nonnull selector1, SEL _Nonnull selector2);
extern Class _Nonnull glue_objc_allocateClassPair PPC_PARAMS(Class _Nullable superclass, const char *_Nonnull name, size_t extraBytes);
extern void glue_objc_registerClassPair PPC_PARAMS(Class _Nonnull class);
extern unsigned int glue_objc_getClassList PPC_PARAMS(Class _Nonnull *_Nullable buffer, unsigned int count);
extern Class _Nonnull *_Nonnull glue_objc_copyClassList PPC_PARAMS(unsigned int *_Nullable length);
extern bool glue_class_isMetaClass PPC_PARAMS(Class _Nullable class);
extern const char *_Nullable glue_class_getName PPC_PARAMS(Class _Nullable class);
extern Class _Nullable glue_class_getSuperclass PPC_PARAMS(Class _Nullable class);
extern unsigned long glue_class_getInstanceSize PPC_PARAMS(Class _Nullable class);
extern bool glue_class_respondsToSelector PPC_PARAMS(Class _Nullable class, SEL _Nonnull selector);
extern bool glue_class_conformsToProtocol PPC_PARAMS(Class _Nullable class, Protocol *_Nonnull p);
extern IMP _Nullable glue_class_getMethodImplementation PPC_PARAMS(Class _Nullable class, SEL _Nonnull selector);
extern IMP _Nullable glue_class_getMethodImplementation_stret PPC_PARAMS(Class _Nullable class, SEL _Nonnull selector);
extern Method _Nullable glue_class_getInstanceMethod PPC_PARAMS(Class _Nullable class, SEL _Nonnull selector);
extern bool glue_class_addMethod PPC_PARAMS(Class _Nonnull class, SEL _Nonnull selector, IMP _Nonnull implementation, const char *_Nullable typeEncoding);
extern IMP _Nullable glue_class_replaceMethod PPC_PARAMS(Class _Nonnull class, SEL _Nonnull selector, IMP _Nonnull implementation, const char *_Nullable typeEncoding);
extern Class _Nullable glue_object_getClass PPC_PARAMS(id _Nullable object);
extern Class _Nullable glue_object_setClass PPC_PARAMS(id _Nullable object, Class _Nonnull class);
extern const char *_Nullable glue_object_getClassName PPC_PARAMS(id _Nullable object);
extern const char *_Nonnull glue_protocol_getName PPC_PARAMS(Protocol *_Nonnull protocol);
extern bool glue_protocol_isEqual PPC_PARAMS(Protocol *_Nonnull protocol1, Protocol *_Nonnull protocol2);
extern bool glue_protocol_conformsToProtocol PPC_PARAMS(Protocol *_Nonnull protocol1, Protocol *_Nonnull protocol2);
extern _Nullable objc_uncaught_exception_handler glue_objc_setUncaughtExceptionHandler PPC_PARAMS(objc_uncaught_exception_handler _Nullable handler);
extern void glue_objc_setForwardHandler PPC_PARAMS(IMP _Nullable forward, IMP _Nullable stretForward);
extern void glue_objc_setEnumerationMutationHandler PPC_PARAMS(objc_enumeration_mutation_handler _Nullable hadler);
extern id _Nullable glue_objc_constructInstance PPC_PARAMS(Class _Nullable class, void *_Nullable bytes);
extern void glue_objc_deinit(void);
extern Ivar _Nullable *_Nullable glue_class_copyIvarList PPC_PARAMS(Class _Nullable class, unsigned int *_Nullable outCount);
extern const char *_Nonnull glue_ivar_getName PPC_PARAMS(Ivar _Nonnull ivar);
extern const char *_Nonnull glue_ivar_getTypeEncoding PPC_PARAMS(Ivar _Nonnull ivar);
extern ptrdiff_t glue_ivar_getOffset PPC_PARAMS(Ivar _Nonnull ivar);
extern Method _Nullable *_Nullable glue_class_copyMethodList PPC_PARAMS(Class _Nullable class, unsigned int *_Nullable outCount);
extern SEL _Nonnull glue_method_getName PPC_PARAMS(Method _Nonnull method);
extern const char *_Nullable glue_method_getTypeEncoding PPC_PARAMS(Method _Nonnull method);
extern objc_property_t _Nullable *_Nullable glue_class_copyPropertyList PPC_PARAMS(Class _Nullable class, unsigned int *_Nullable outCount);
extern const char *_Nonnull glue_property_getName PPC_PARAMS(objc_property_t _Nonnull property);
extern char *_Nullable glue_property_copyAttributeValue PPC_PARAMS(objc_property_t _Nonnull property, const char *_Nonnull name);
extern void *_Nullable glue_objc_destructInstance PPC_PARAMS(id _Nullable object);
extern void *_Null_unspecified glue_objc_autoreleasePoolPush(void);
extern void glue_objc_autoreleasePoolPop PPC_PARAMS(void *_Null_unspecified pool);
extern id _Nullable glue__objc_rootAutorelease PPC_PARAMS(id _Nullable object);
extern struct objc_hashtable *_Nonnull glue_objc_hashtable_new PPC_PARAMS(objc_hashtable_hash_func hash, objc_hashtable_equal_func equal, uint32_t size);
extern void glue_objc_hashtable_set PPC_PARAMS(struct objc_hashtable *_Nonnull table, const void *_Nonnull key, const void *_Nonnull object);
extern void *_Nullable glue_objc_hashtable_get PPC_PARAMS(struct objc_hashtable *_Nonnull table, const void *_Nonnull key);
extern void glue_objc_hashtable_delete PPC_PARAMS(struct objc_hashtable *_Nonnull table, const void *_Nonnull key);
extern void glue_objc_hashtable_free PPC_PARAMS(struct objc_hashtable *_Nonnull table);
extern void glue_objc_setTaggedPointerSecret PPC_PARAMS(uintptr_t secret);
extern int glue_objc_registerTaggedPointerClass PPC_PARAMS(Class _Nonnull class);
extern bool glue_object_isTaggedPointer PPC_PARAMS(id _Nullable object);
extern uintptr_t glue_object_getTaggedPointerValue PPC_PARAMS(id _Nonnull object);
extern id _Nullable glue_objc_createTaggedPointer PPC_PARAMS(int class, uintptr_t value);

Modified src/runtime/amiga-glue.m from [896aceecb7] to [8e4bfceffc].

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
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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "ObjFWRT.h"
#import "private.h"
#import "macros.h"

#ifdef OF_AMIGAOS_M68K
# define PPC_PARAMS(...) (void)
# define M68K_ARG OBJC_M68K_ARG
#else
# define PPC_PARAMS(...) (__VA_ARGS__)
# define M68K_ARG(...)
#endif

extern bool objc_init(unsigned int, struct objc_libc *, FILE *, FILE *);

#ifdef OF_MORPHOS
/* All __saveds functions in this file need to use the SysV ABI */
__asm__ (
    ".section .text\n"
    ".align 2\n"
    "__restore_r13:\n"
    "	lwz	%r13, 44(%r12)\n"
    "	blr\n"
);
#endif

bool __saveds
glue_objc_init PPC_PARAMS(unsigned int version, struct objc_libc *libc,
    FILE *stdout_, FILE *stderr_)
{
	M68K_ARG(unsigned int, version, d0)
	M68K_ARG(struct objc_libc *, libc, a0)
	M68K_ARG(FILE *, stdout_, a1)
	M68K_ARG(FILE *, stderr_, a2)

	return objc_init(version, libc, stdout_, stderr_);
}

void __saveds
glue___objc_exec_class PPC_PARAMS(struct objc_module *module)
{
	M68K_ARG(struct objc_module *, module, a0)

	__objc_exec_class(module);
}

IMP __saveds
glue_objc_msg_lookup PPC_PARAMS(id object, SEL selector)
{
	M68K_ARG(id, object, a0)
	M68K_ARG(SEL, selector, a1)

	return objc_msg_lookup(object, selector);
}

IMP __saveds
glue_objc_msg_lookup_stret PPC_PARAMS(id object, SEL selector)
{
	M68K_ARG(id, object, a0)
	M68K_ARG(SEL, selector, a1)

	return objc_msg_lookup_stret(object, selector);
}

IMP __saveds
glue_objc_msg_lookup_super PPC_PARAMS(struct objc_super *super, SEL selector)
{
	M68K_ARG(struct objc_super *, super, a0)
	M68K_ARG(SEL, selector, a1)

	return objc_msg_lookup_super(super, selector);
}

IMP __saveds
glue_objc_msg_lookup_super_stret PPC_PARAMS(struct objc_super *super,
    SEL selector)
{
	M68K_ARG(struct objc_super *, super, a0)
	M68K_ARG(SEL, selector, a1)

	return objc_msg_lookup_super_stret(super, selector);
}

Class __saveds
glue_objc_lookUpClass PPC_PARAMS(const char *name)
{
	M68K_ARG(const char *, name, a0)

	return objc_lookUpClass(name);
}

Class __saveds
glue_objc_getClass PPC_PARAMS(const char *name)
{
	M68K_ARG(const char *, name, a0)

	return objc_getClass(name);
}

Class __saveds
glue_objc_getRequiredClass PPC_PARAMS(const char *name)
{
	M68K_ARG(const char *, name, a0)

	return objc_getRequiredClass(name);
}

Class __saveds
glue_objc_lookup_class PPC_PARAMS(const char *name)
{
	M68K_ARG(const char *, name, a0)

	return objc_lookup_class(name);
}

Class __saveds
glue_objc_get_class PPC_PARAMS(const char *name)
{
	M68K_ARG(const char *, name, a0)

	return objc_get_class(name);
}

void __saveds
glue_objc_exception_throw PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	objc_exception_throw(object);

	OF_UNREACHABLE
}

int __saveds
glue_objc_sync_enter PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_sync_enter(object);
}

int __saveds
glue_objc_sync_exit PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_sync_exit(object);
}

id __saveds
glue_objc_getProperty PPC_PARAMS(id self, SEL _cmd, ptrdiff_t offset,
    bool atomic)
{
	M68K_ARG(id, self, a0)
	M68K_ARG(SEL, _cmd, a1)
	M68K_ARG(ptrdiff_t, offset, d0)
	M68K_ARG(bool, atomic, d1)

	return objc_getProperty(self, _cmd, offset, atomic);
}

void __saveds
glue_objc_setProperty PPC_PARAMS(id self, SEL _cmd, ptrdiff_t offset, id value,
    bool atomic, signed char copy)
{
	M68K_ARG(id, self, a0)
	M68K_ARG(SEL, _cmd, a1)
	M68K_ARG(ptrdiff_t, offset, d0)
	M68K_ARG(id, value, a2)
	M68K_ARG(bool, atomic, d1)
	M68K_ARG(signed char, copy, d2)

	objc_setProperty(self, _cmd, offset, value, atomic, copy);
}

void __saveds
glue_objc_getPropertyStruct PPC_PARAMS(void *dest, const void *src,
    ptrdiff_t size, bool atomic, bool strong)
{
	M68K_ARG(void *, dest, a0)
	M68K_ARG(const void *, src, a1)
	M68K_ARG(ptrdiff_t, size, d0)
	M68K_ARG(bool, atomic, d1)
	M68K_ARG(bool, strong, d2)

	objc_getPropertyStruct(dest, src, size, atomic, strong);
}

void __saveds
glue_objc_setPropertyStruct PPC_PARAMS(void *dest, const void *src,
    ptrdiff_t size, bool atomic, bool strong)
{
	M68K_ARG(void *, dest, a0)
	M68K_ARG(const void *, src, a1)
	M68K_ARG(ptrdiff_t, size, d0)
	M68K_ARG(bool, atomic, d1)
	M68K_ARG(bool, strong, d2)

	objc_setPropertyStruct(dest, src, size, atomic, strong);
}

void __saveds
glue_objc_enumerationMutation PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	objc_enumerationMutation(object);
}

int __saveds
glue___gnu_objc_personality PPC_PARAMS(int version, int actions,
    uint64_t exClass, void *ex, void *ctx)
{
	M68K_ARG(int, version, d0)
	M68K_ARG(int, actions, d1)
	M68K_ARG(uint64_t *, exClassPtr, d2)
	M68K_ARG(void *, ex, a0)
	M68K_ARG(void *, ctx, a1)
#ifdef OF_AMIGAOS_M68K
	uint64_t exClass = *exClassPtr;
#endif

#ifdef HAVE_SJLJ_EXCEPTIONS
	return __gnu_objc_personality_sj0(version, actions, exClass, ex, ctx);
#else
	return __gnu_objc_personality_v0(version, actions, exClass, ex, ctx);
#endif
}

id __saveds
glue_objc_retain PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_retain(object);
}

id __saveds
glue_objc_retainBlock PPC_PARAMS(id block)
{
	M68K_ARG(id, block, a0)

	return objc_retainBlock(block);
}

id __saveds
glue_objc_retainAutorelease PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_retainAutorelease(object);
}

void __saveds
glue_objc_release PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	objc_release(object);
}

id __saveds
glue_objc_autorelease PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_autorelease(object);
}

id __saveds
glue_objc_autoreleaseReturnValue PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_autoreleaseReturnValue(object);
}

id __saveds
glue_objc_retainAutoreleaseReturnValue PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_retainAutoreleaseReturnValue(object);
}

id __saveds
glue_objc_retainAutoreleasedReturnValue PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_retainAutoreleasedReturnValue(object);
}

id __saveds
glue_objc_storeStrong PPC_PARAMS(id *object, id value)
{
	M68K_ARG(id *, object, a0)
	M68K_ARG(id, value, a1)

	return objc_storeStrong(object, value);
}

id __saveds
glue_objc_storeWeak PPC_PARAMS(id *object, id value)
{
	M68K_ARG(id *, object, a0)
	M68K_ARG(id, value, a1)

	return objc_storeWeak(object, value);
}

id __saveds
glue_objc_loadWeakRetained PPC_PARAMS(id *object)
{
	M68K_ARG(id *, object, a0)

	return objc_loadWeakRetained(object);
}

id __saveds
glue_objc_initWeak PPC_PARAMS(id *object, id value)
{
	M68K_ARG(id *, object, a0)
	M68K_ARG(id, value, a1)

	return objc_initWeak(object, value);
}

void __saveds
glue_objc_destroyWeak PPC_PARAMS(id *object)
{
	M68K_ARG(id *, object, a0)

	objc_destroyWeak(object);
}

id __saveds
glue_objc_loadWeak PPC_PARAMS(id *object)
{
	M68K_ARG(id *, object, a0)

	return objc_loadWeak(object);
}

void __saveds
glue_objc_copyWeak PPC_PARAMS(id *dest, id *src)
{
	M68K_ARG(id *, dest, a0)
	M68K_ARG(id *, src, a1)

	objc_copyWeak(dest, src);
}

void __saveds
glue_objc_moveWeak PPC_PARAMS(id *dest, id *src)
{
	M68K_ARG(id *, dest, a0)
	M68K_ARG(id *, src, a1)

	objc_moveWeak(dest, src);
}

SEL __saveds
glue_sel_registerName PPC_PARAMS(const char *name)
{
	M68K_ARG(const char *, name, a0)

	return sel_registerName(name);
}

const char *__saveds
glue_sel_getName PPC_PARAMS(SEL selector)
{
	M68K_ARG(SEL, selector, a0)

	return sel_getName(selector);
}

bool __saveds
glue_sel_isEqual PPC_PARAMS(SEL selector1, SEL selector2)
{
	M68K_ARG(SEL, selector1, a0)
	M68K_ARG(SEL, selector2, a1)

	return sel_isEqual(selector1, selector2);
}

Class __saveds
glue_objc_allocateClassPair PPC_PARAMS(Class superclass, const char *name,
    size_t extraBytes)
{
	M68K_ARG(Class, superclass, a0)
	M68K_ARG(const char *, name, a1)
	M68K_ARG(size_t, extraBytes, d0)

	return objc_allocateClassPair(superclass, name, extraBytes);
}

void __saveds
glue_objc_registerClassPair PPC_PARAMS(Class class)
{
	M68K_ARG(Class, class, a0)

	objc_registerClassPair(class);
}

unsigned int __saveds
glue_objc_getClassList PPC_PARAMS(Class *buffer, unsigned int count)
{
	M68K_ARG(Class *, buffer, a0)
	M68K_ARG(unsigned int, count, d0)

	return objc_getClassList(buffer, count);
}

Class *__saveds
glue_objc_copyClassList PPC_PARAMS(unsigned int *length)
{
	M68K_ARG(unsigned int *, length, a0)

	return objc_copyClassList(length);
}

bool __saveds
glue_class_isMetaClass PPC_PARAMS(Class class)
{
	M68K_ARG(Class, class, a0)

	return class_isMetaClass(class);
}

const char *__saveds
glue_class_getName PPC_PARAMS(Class class)
{
	M68K_ARG(Class, class, a0)

	return class_getName(class);
}

Class __saveds
glue_class_getSuperclass PPC_PARAMS(Class class)
{
	M68K_ARG(Class, class, a0)

	return class_getSuperclass(class);
}

unsigned long __saveds
glue_class_getInstanceSize PPC_PARAMS(Class class)
{
	M68K_ARG(Class, class, a0)

	return class_getInstanceSize(class);
}

bool __saveds
glue_class_respondsToSelector PPC_PARAMS(Class class, SEL selector)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(SEL, selector, a1)

	return class_respondsToSelector(class, selector);
}

bool __saveds
glue_class_conformsToProtocol PPC_PARAMS(Class class, Protocol *protocol)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(Protocol *, protocol, a1)

	return class_conformsToProtocol(class, protocol);
}

IMP __saveds
glue_class_getMethodImplementation PPC_PARAMS(Class class, SEL selector)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(SEL, selector, a1)

	return class_getMethodImplementation(class, selector);
}

IMP __saveds
glue_class_getMethodImplementation_stret PPC_PARAMS(Class class, SEL selector)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(SEL, selector, a1)

	return class_getMethodImplementation_stret(class, selector);
}

Method __saveds
glue_class_getInstanceMethod PPC_PARAMS(Class class, SEL selector)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(SEL, selector, a1)

	return class_getInstanceMethod(class, selector);
}

bool __saveds
glue_class_addMethod PPC_PARAMS(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(SEL, selector, a1)
	M68K_ARG(IMP, implementation, a2)
	M68K_ARG(const char *, typeEncoding, a3)

	return class_addMethod(class, selector, implementation, typeEncoding);
}

IMP __saveds
glue_class_replaceMethod PPC_PARAMS(Class class, SEL selector,
    IMP implementation, const char *typeEncoding)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(SEL, selector, a1)
	M68K_ARG(IMP, implementation, a2)
	M68K_ARG(const char *, typeEncoding, a3)

	return class_replaceMethod(class, selector, implementation,
	    typeEncoding);
}

Class __saveds
glue_object_getClass PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return object_getClass(object);
}

Class __saveds
glue_object_setClass PPC_PARAMS(id object, Class class)
{
	M68K_ARG(id, object, a0)
	M68K_ARG(Class, class, a1)

	return object_setClass(object, class);
}

const char *__saveds
glue_object_getClassName PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return object_getClassName(object);
}

const char *__saveds
glue_protocol_getName PPC_PARAMS(Protocol *protocol)
{
	M68K_ARG(Protocol *, protocol, a0)

	return protocol_getName(protocol);
}

bool __saveds
glue_protocol_isEqual PPC_PARAMS(Protocol *protocol1, Protocol *protocol2)
{
	M68K_ARG(Protocol *, protocol1, a0)
	M68K_ARG(Protocol *, protocol2, a1)

	return protocol_isEqual(protocol1, protocol2);
}

bool __saveds
glue_protocol_conformsToProtocol PPC_PARAMS(Protocol *protocol1,
    Protocol *protocol2)
{
	M68K_ARG(Protocol *, protocol1, a0)
	M68K_ARG(Protocol *, protocol2, a1)

	return protocol_conformsToProtocol(protocol1, protocol2);
}

objc_uncaught_exception_handler_t __saveds
glue_objc_setUncaughtExceptionHandler PPC_PARAMS(
    objc_uncaught_exception_handler_t handler)
{
	M68K_ARG(objc_uncaught_exception_handler_t, handler, a0)

	return objc_setUncaughtExceptionHandler(handler);
}

void __saveds
glue_objc_setForwardHandler PPC_PARAMS(IMP forward, IMP stretForward)
{
	M68K_ARG(IMP, forward, a0)
	M68K_ARG(IMP, stretForward, a1)

	objc_setForwardHandler(forward, stretForward);
}

void __saveds
glue_objc_setEnumerationMutationHandler PPC_PARAMS(
    objc_enumeration_mutation_handler_t handler)
{
	M68K_ARG(objc_enumeration_mutation_handler_t, handler, a0)

	objc_setEnumerationMutationHandler(handler);
}

id __saveds
glue_objc_constructInstance PPC_PARAMS(Class class, void *bytes)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(void *, bytes, a1)

	return objc_constructInstance(class, bytes);
}

void __saveds
glue_objc_exit(void)
{
	objc_exit();
}

Ivar *__saveds
glue_class_copyIvarList PPC_PARAMS(Class class, unsigned int *outCount)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(unsigned int *, outCount, a1)

	return class_copyIvarList(class, outCount);
}

const char *__saveds
glue_ivar_getName PPC_PARAMS(Ivar ivar)
{
	M68K_ARG(Ivar, ivar, a0)

	return ivar_getName(ivar);
}

const char *__saveds
glue_ivar_getTypeEncoding PPC_PARAMS(Ivar ivar)
{
	M68K_ARG(Ivar, ivar, a0)

	return ivar_getTypeEncoding(ivar);
}

ptrdiff_t __saveds
glue_ivar_getOffset PPC_PARAMS(Ivar ivar)
{
	M68K_ARG(Ivar, ivar, a0)

	return ivar_getOffset(ivar);
}

Method *__saveds
glue_class_copyMethodList PPC_PARAMS(Class class, unsigned int *outCount)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(unsigned int *, outCount, a1)

	return class_copyMethodList(class, outCount);
}

SEL __saveds
glue_method_getName PPC_PARAMS(Method method)
{
	M68K_ARG(Method, method, a0)

	return method_getName(method);
}

const char *__saveds
glue_method_getTypeEncoding PPC_PARAMS(Method method)
{
	M68K_ARG(Method, method, a0)

	return method_getTypeEncoding(method);
}

objc_property_t *__saveds
glue_class_copyPropertyList PPC_PARAMS(Class class, unsigned int *outCount)
{
	M68K_ARG(Class, class, a0)
	M68K_ARG(unsigned int *, outCount, a1)

	return class_copyPropertyList(class, outCount);
}

const char *__saveds
glue_property_getName PPC_PARAMS(objc_property_t property)
{
	M68K_ARG(objc_property_t, property, a0)

	return property_getName(property);
}

char *__saveds
glue_property_copyAttributeValue PPC_PARAMS(objc_property_t property,
    const char *name)
{
	M68K_ARG(objc_property_t, property, a0)
	M68K_ARG(const char *, name, a1)

	return property_copyAttributeValue(property, name);
}

void *__saveds
glue_objc_destructInstance PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return objc_destructInstance(object);
}

void *__saveds
glue_objc_autoreleasePoolPush(void)
{
	return objc_autoreleasePoolPush();
}

void __saveds
glue_objc_autoreleasePoolPop PPC_PARAMS(void *pool)
{
	M68K_ARG(void *, pool, a0)

	objc_autoreleasePoolPop(pool);
}

id __saveds
glue__objc_rootAutorelease PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return _objc_rootAutorelease(object);
}

struct objc_hashtable *__saveds
glue_objc_hashtable_new PPC_PARAMS(objc_hashtable_hash_func hash,
    objc_hashtable_equal_func equal, uint32_t size)
{
	M68K_ARG(objc_hashtable_hash_func, hash, a0)
	M68K_ARG(objc_hashtable_equal_func, equal, a1)
	M68K_ARG(uint32_t, size, d0)

	return objc_hashtable_new(hash, equal, size);
}

void __saveds
glue_objc_hashtable_set PPC_PARAMS(struct objc_hashtable *table,
    const void *key, const void *object)
{
	M68K_ARG(struct objc_hashtable *, table, a0)
	M68K_ARG(const void *, key, a1)
	M68K_ARG(const void *, object, a2)

	objc_hashtable_set(table, key, object);
}

void *__saveds
glue_objc_hashtable_get PPC_PARAMS(struct objc_hashtable *table,
    const void *key)
{
	M68K_ARG(struct objc_hashtable *, table, a0)
	M68K_ARG(const void *, key, a1)

	return objc_hashtable_get(table, key);
}

void __saveds
glue_objc_hashtable_delete PPC_PARAMS(struct objc_hashtable *table,
    const void *key)
{
	M68K_ARG(struct objc_hashtable *, table, a0)
	M68K_ARG(const void *, key, a1)

	objc_hashtable_delete(table, key);
}

void __saveds
glue_objc_hashtable_free PPC_PARAMS(struct objc_hashtable *table)
{
	M68K_ARG(struct objc_hashtable *, table, a0)

	objc_hashtable_free(table);
}

void __saveds
glue_objc_setTaggedPointerSecret PPC_PARAMS(uintptr_t secret)
{
	M68K_ARG(uintptr_t, secret, d0)

	objc_setTaggedPointerSecret(secret);
}

int __saveds
glue_objc_registerTaggedPointerClass PPC_PARAMS(Class class)
{
	M68K_ARG(Class, class, a0)

	return objc_registerTaggedPointerClass(class);
}

bool __saveds
glue_object_isTaggedPointer PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return object_isTaggedPointer(object);
}

Class __saveds
glue_object_getTaggedPointerClass PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return object_getTaggedPointerClass(object);
}

uintptr_t __saveds
glue_object_getTaggedPointerValue PPC_PARAMS(id object)
{
	M68K_ARG(id, object, a0)

	return object_getTaggedPointerValue(object);
}

id __saveds
glue_objc_createTaggedPointer PPC_PARAMS(int class, uintptr_t value)
{
	M68K_ARG(int, class, d0)
	M68K_ARG(uintptr_t, value, d1)

	return objc_createTaggedPointer(class, value);
}

<
<
|













|

<
<
|

<
<
<
<
<
<
<
|
<













|
<



<
<

|



|

|




|
|

|
|




|
|

|
|




|
|

|
|




|
|
<

|
|




|
|

|




|
|

|




|
|

|




|
|

|




|
|

|





|

|


<
<



|

|





|

|




|
|
<

|
|







<
|

|
|

|







|
<

|
|








|
<

|
|








|

|





|
<



|
|
|
<
<
<

<
<
<
|
<


|
|

|




|
|

|




|
|

|





|

|




|
|

|




|
|

|




|
|

|




|
|

|




|
|

|
|




|
|

|
|




|
|

|




|
|

|
|





|

|




|
|

|





|

|
|





|

|
|




|
|

|




|
|

|





|

|
|




|
|
<

|
|






|

|





|

|





|
|

|





|

|




|
|

|




|
|

|





|

|





|

|
|





|

|
|

|


|
|

|
|




|
|

|
|




|
|

|
|





|
<

|
|
|
|




|
|
<

|
|
|
|

|
<


|
|

|




|
|

|
|




|
|

|




|
|

|





|

|
|





|
<

|
|




|
|
<

|





|

|
|





|
<

|

|


|
|

|
|





|

|


|
|

|
|




|
|

|




|
|

|





|

|




|
|

|
|




|
|

|




|
|

|




|
|

|
|




|
|

|




|
|
<

|
|




|
|

|




|






|

|




|
|

|




|
|
<









|
<

|
|
|




|
|
<

|
|





|
<

|
|





|

|













|

|





|

|




<
<
<
<
<
<
<
<

|

|




|







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


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

/* This file is automatically generated from amiga-library.xml */



#include "config.h"








#import "amiga-glue.h"


#ifdef OF_MORPHOS
/* All __saveds functions in this file need to use the SysV ABI */
__asm__ (
    ".section .text\n"
    ".align 2\n"
    "__restore_r13:\n"
    "	lwz	%r13, 44(%r12)\n"
    "	blr\n"
);
#endif

bool __saveds
glue_objc_init PPC_PARAMS(unsigned int version, struct objc_libc *libc)

{
	M68K_ARG(unsigned int, version, d0)
	M68K_ARG(struct objc_libc *, libc, a0)



	return objc_init(version, libc);
}

void __saveds
glue___objc_exec_class PPC_PARAMS(struct objc_module *_Nonnull module)
{
	M68K_ARG(struct objc_module *_Nonnull, module, a0)

	__objc_exec_class(module);
}

IMP _Nonnull __saveds
glue_objc_msg_lookup PPC_PARAMS(id _Nullable object, SEL _Nonnull selector)
{
	M68K_ARG(id _Nullable, object, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)

	return objc_msg_lookup(object, selector);
}

IMP _Nonnull __saveds
glue_objc_msg_lookup_stret PPC_PARAMS(id _Nullable object, SEL _Nonnull selector)
{
	M68K_ARG(id _Nullable, object, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)

	return objc_msg_lookup_stret(object, selector);
}

IMP _Nonnull __saveds
glue_objc_msg_lookup_super PPC_PARAMS(struct objc_super *_Nonnull super, SEL _Nonnull selector)
{
	M68K_ARG(struct objc_super *_Nonnull, super, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)

	return objc_msg_lookup_super(super, selector);
}

IMP _Nonnull __saveds
glue_objc_msg_lookup_super_stret PPC_PARAMS(struct objc_super *_Nonnull super, SEL _Nonnull selector)

{
	M68K_ARG(struct objc_super *_Nonnull, super, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)

	return objc_msg_lookup_super_stret(super, selector);
}

Class _Nullable __saveds
glue_objc_lookUpClass PPC_PARAMS(const char *_Nonnull name)
{
	M68K_ARG(const char *_Nonnull, name, a0)

	return objc_lookUpClass(name);
}

Class _Nullable __saveds
glue_objc_getClass PPC_PARAMS(const char *_Nonnull name)
{
	M68K_ARG(const char *_Nonnull, name, a0)

	return objc_getClass(name);
}

Class _Nonnull __saveds
glue_objc_getRequiredClass PPC_PARAMS(const char *_Nonnull name)
{
	M68K_ARG(const char *_Nonnull, name, a0)

	return objc_getRequiredClass(name);
}

Class _Nullable __saveds
glue_objc_lookup_class PPC_PARAMS(const char *_Nonnull name)
{
	M68K_ARG(const char *_Nonnull, name, a0)

	return objc_lookup_class(name);
}

Class _Nonnull __saveds
glue_objc_get_class PPC_PARAMS(const char *_Nonnull name)
{
	M68K_ARG(const char *_Nonnull, name, a0)

	return objc_get_class(name);
}

void __saveds
glue_objc_exception_throw PPC_PARAMS(id _Nonnull object)
{
	M68K_ARG(id _Nonnull, object, a0)

	objc_exception_throw(object);


}

int __saveds
glue_objc_sync_enter PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_sync_enter(object);
}

int __saveds
glue_objc_sync_exit PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_sync_exit(object);
}

id _Nullable __saveds
glue_objc_getProperty PPC_PARAMS(id _Nonnull self, SEL _Nonnull _cmd, ptrdiff_t offset, bool atomic)

{
	M68K_ARG(id _Nonnull, self, a0)
	M68K_ARG(SEL _Nonnull, _cmd, a1)
	M68K_ARG(ptrdiff_t, offset, d0)
	M68K_ARG(bool, atomic, d1)

	return objc_getProperty(self, _cmd, offset, atomic);
}

void __saveds

glue_objc_setProperty PPC_PARAMS(id _Nonnull self, SEL _Nonnull _cmd, ptrdiff_t offset, id _Nullable value, bool atomic, signed char copy)
{
	M68K_ARG(id _Nonnull, self, a0)
	M68K_ARG(SEL _Nonnull, _cmd, a1)
	M68K_ARG(ptrdiff_t, offset, d0)
	M68K_ARG(id _Nullable, value, a2)
	M68K_ARG(bool, atomic, d1)
	M68K_ARG(signed char, copy, d2)

	objc_setProperty(self, _cmd, offset, value, atomic, copy);
}

void __saveds
glue_objc_getPropertyStruct PPC_PARAMS(void *_Nonnull dest, const void *_Nonnull src, ptrdiff_t size, bool atomic, bool strong)

{
	M68K_ARG(void *_Nonnull, dest, a0)
	M68K_ARG(const void *_Nonnull, src, a1)
	M68K_ARG(ptrdiff_t, size, d0)
	M68K_ARG(bool, atomic, d1)
	M68K_ARG(bool, strong, d2)

	objc_getPropertyStruct(dest, src, size, atomic, strong);
}

void __saveds
glue_objc_setPropertyStruct PPC_PARAMS(void *_Nonnull dest, const void *_Nonnull src, ptrdiff_t size, bool atomic, bool strong)

{
	M68K_ARG(void *_Nonnull, dest, a0)
	M68K_ARG(const void *_Nonnull, src, a1)
	M68K_ARG(ptrdiff_t, size, d0)
	M68K_ARG(bool, atomic, d1)
	M68K_ARG(bool, strong, d2)

	objc_setPropertyStruct(dest, src, size, atomic, strong);
}

void __saveds
glue_objc_enumerationMutation PPC_PARAMS(id _Nonnull object)
{
	M68K_ARG(id _Nonnull, object, a0)

	objc_enumerationMutation(object);
}

int __saveds
glue___gnu_objc_personality PPC_PARAMS(int version, int actions, uint64_t *_Nonnull exClass, void *_Nonnull ex, void *_Nonnull ctx)

{
	M68K_ARG(int, version, d0)
	M68K_ARG(int, actions, d1)
	M68K_ARG(uint64_t *_Nonnull, exClass, d2)
	M68K_ARG(void *_Nonnull, ex, a0)
	M68K_ARG(void *_Nonnull, ctx, a1)







	return __gnu_objc_personality(version, actions, exClass, ex, ctx);

}

id _Nullable __saveds
glue_objc_retain PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_retain(object);
}

id _Nullable __saveds
glue_objc_retainBlock PPC_PARAMS(id _Nullable block)
{
	M68K_ARG(id _Nullable, block, a0)

	return objc_retainBlock(block);
}

id _Nullable __saveds
glue_objc_retainAutorelease PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_retainAutorelease(object);
}

void __saveds
glue_objc_release PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	objc_release(object);
}

id _Nullable __saveds
glue_objc_autorelease PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_autorelease(object);
}

id _Nullable __saveds
glue_objc_autoreleaseReturnValue PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_autoreleaseReturnValue(object);
}

id _Nullable __saveds
glue_objc_retainAutoreleaseReturnValue PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_retainAutoreleaseReturnValue(object);
}

id _Nullable __saveds
glue_objc_retainAutoreleasedReturnValue PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_retainAutoreleasedReturnValue(object);
}

id _Nullable __saveds
glue_objc_storeStrong PPC_PARAMS(id _Nullable *_Nonnull object, id _Nullable value)
{
	M68K_ARG(id _Nullable *_Nonnull, object, a0)
	M68K_ARG(id _Nullable, value, a1)

	return objc_storeStrong(object, value);
}

id _Nullable __saveds
glue_objc_storeWeak PPC_PARAMS(id _Nullable *_Nonnull object, id _Nullable value)
{
	M68K_ARG(id _Nullable *_Nonnull, object, a0)
	M68K_ARG(id _Nullable, value, a1)

	return objc_storeWeak(object, value);
}

id _Nullable __saveds
glue_objc_loadWeakRetained PPC_PARAMS(id _Nullable *_Nonnull object)
{
	M68K_ARG(id _Nullable *_Nonnull, object, a0)

	return objc_loadWeakRetained(object);
}

id _Nullable __saveds
glue_objc_initWeak PPC_PARAMS(id _Nullable *_Nonnull object, id _Nullable value)
{
	M68K_ARG(id _Nullable *_Nonnull, object, a0)
	M68K_ARG(id _Nullable, value, a1)

	return objc_initWeak(object, value);
}

void __saveds
glue_objc_destroyWeak PPC_PARAMS(id _Nullable *_Nonnull object)
{
	M68K_ARG(id _Nullable *_Nonnull, object, a0)

	objc_destroyWeak(object);
}

id _Nullable __saveds
glue_objc_loadWeak PPC_PARAMS(id _Nullable *_Nonnull object)
{
	M68K_ARG(id _Nullable *_Nonnull, object, a0)

	return objc_loadWeak(object);
}

void __saveds
glue_objc_copyWeak PPC_PARAMS(id _Nullable *_Nonnull dest, id _Nullable *_Nonnull src)
{
	M68K_ARG(id _Nullable *_Nonnull, dest, a0)
	M68K_ARG(id _Nullable *_Nonnull, src, a1)

	objc_copyWeak(dest, src);
}

void __saveds
glue_objc_moveWeak PPC_PARAMS(id _Nullable *_Nonnull dest, id _Nullable *_Nonnull src)
{
	M68K_ARG(id _Nullable *_Nonnull, dest, a0)
	M68K_ARG(id _Nullable *_Nonnull, src, a1)

	objc_moveWeak(dest, src);
}

SEL _Nonnull __saveds
glue_sel_registerName PPC_PARAMS(const char *_Nonnull name)
{
	M68K_ARG(const char *_Nonnull, name, a0)

	return sel_registerName(name);
}

const char *_Nonnull __saveds
glue_sel_getName PPC_PARAMS(SEL _Nonnull selector)
{
	M68K_ARG(SEL _Nonnull, selector, a0)

	return sel_getName(selector);
}

bool __saveds
glue_sel_isEqual PPC_PARAMS(SEL _Nonnull selector1, SEL _Nonnull selector2)
{
	M68K_ARG(SEL _Nonnull, selector1, a0)
	M68K_ARG(SEL _Nonnull, selector2, a1)

	return sel_isEqual(selector1, selector2);
}

Class _Nonnull __saveds
glue_objc_allocateClassPair PPC_PARAMS(Class _Nullable superclass, const char *_Nonnull name, size_t extraBytes)

{
	M68K_ARG(Class _Nullable, superclass, a0)
	M68K_ARG(const char *_Nonnull, name, a1)
	M68K_ARG(size_t, extraBytes, d0)

	return objc_allocateClassPair(superclass, name, extraBytes);
}

void __saveds
glue_objc_registerClassPair PPC_PARAMS(Class _Nonnull class)
{
	M68K_ARG(Class _Nonnull, class, a0)

	objc_registerClassPair(class);
}

unsigned int __saveds
glue_objc_getClassList PPC_PARAMS(Class _Nonnull *_Nullable buffer, unsigned int count)
{
	M68K_ARG(Class _Nonnull *_Nullable, buffer, a0)
	M68K_ARG(unsigned int, count, d0)

	return objc_getClassList(buffer, count);
}

Class _Nonnull *_Nonnull __saveds
glue_objc_copyClassList PPC_PARAMS(unsigned int *_Nullable length)
{
	M68K_ARG(unsigned int *_Nullable, length, a0)

	return objc_copyClassList(length);
}

bool __saveds
glue_class_isMetaClass PPC_PARAMS(Class _Nullable class)
{
	M68K_ARG(Class _Nullable, class, a0)

	return class_isMetaClass(class);
}

const char *_Nullable __saveds
glue_class_getName PPC_PARAMS(Class _Nullable class)
{
	M68K_ARG(Class _Nullable, class, a0)

	return class_getName(class);
}

Class _Nullable __saveds
glue_class_getSuperclass PPC_PARAMS(Class _Nullable class)
{
	M68K_ARG(Class _Nullable, class, a0)

	return class_getSuperclass(class);
}

unsigned long __saveds
glue_class_getInstanceSize PPC_PARAMS(Class _Nullable class)
{
	M68K_ARG(Class _Nullable, class, a0)

	return class_getInstanceSize(class);
}

bool __saveds
glue_class_respondsToSelector PPC_PARAMS(Class _Nullable class, SEL _Nonnull selector)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)

	return class_respondsToSelector(class, selector);
}

bool __saveds
glue_class_conformsToProtocol PPC_PARAMS(Class _Nullable class, Protocol *_Nonnull p)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(Protocol *_Nonnull, p, a1)

	return class_conformsToProtocol(class, p);
}

IMP _Nullable __saveds
glue_class_getMethodImplementation PPC_PARAMS(Class _Nullable class, SEL _Nonnull selector)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)

	return class_getMethodImplementation(class, selector);
}

IMP _Nullable __saveds
glue_class_getMethodImplementation_stret PPC_PARAMS(Class _Nullable class, SEL _Nonnull selector)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)

	return class_getMethodImplementation_stret(class, selector);
}

Method _Nullable __saveds
glue_class_getInstanceMethod PPC_PARAMS(Class _Nullable class, SEL _Nonnull selector)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)

	return class_getInstanceMethod(class, selector);
}

bool __saveds
glue_class_addMethod PPC_PARAMS(Class _Nonnull class, SEL _Nonnull selector, IMP _Nonnull implementation, const char *_Nullable typeEncoding)

{
	M68K_ARG(Class _Nonnull, class, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)
	M68K_ARG(IMP _Nonnull, implementation, a2)
	M68K_ARG(const char *_Nullable, typeEncoding, a3)

	return class_addMethod(class, selector, implementation, typeEncoding);
}

IMP _Nullable __saveds
glue_class_replaceMethod PPC_PARAMS(Class _Nonnull class, SEL _Nonnull selector, IMP _Nonnull implementation, const char *_Nullable typeEncoding)

{
	M68K_ARG(Class _Nonnull, class, a0)
	M68K_ARG(SEL _Nonnull, selector, a1)
	M68K_ARG(IMP _Nonnull, implementation, a2)
	M68K_ARG(const char *_Nullable, typeEncoding, a3)

	return class_replaceMethod(class, selector, implementation, typeEncoding);

}

Class _Nullable __saveds
glue_object_getClass PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return object_getClass(object);
}

Class _Nullable __saveds
glue_object_setClass PPC_PARAMS(id _Nullable object, Class _Nonnull class)
{
	M68K_ARG(id _Nullable, object, a0)
	M68K_ARG(Class _Nonnull, class, a1)

	return object_setClass(object, class);
}

const char *_Nullable __saveds
glue_object_getClassName PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return object_getClassName(object);
}

const char *_Nonnull __saveds
glue_protocol_getName PPC_PARAMS(Protocol *_Nonnull protocol)
{
	M68K_ARG(Protocol *_Nonnull, protocol, a0)

	return protocol_getName(protocol);
}

bool __saveds
glue_protocol_isEqual PPC_PARAMS(Protocol *_Nonnull protocol1, Protocol *_Nonnull protocol2)
{
	M68K_ARG(Protocol *_Nonnull, protocol1, a0)
	M68K_ARG(Protocol *_Nonnull, protocol2, a1)

	return protocol_isEqual(protocol1, protocol2);
}

bool __saveds
glue_protocol_conformsToProtocol PPC_PARAMS(Protocol *_Nonnull protocol1, Protocol *_Nonnull protocol2)

{
	M68K_ARG(Protocol *_Nonnull, protocol1, a0)
	M68K_ARG(Protocol *_Nonnull, protocol2, a1)

	return protocol_conformsToProtocol(protocol1, protocol2);
}

_Nullable objc_uncaught_exception_handler __saveds
glue_objc_setUncaughtExceptionHandler PPC_PARAMS(objc_uncaught_exception_handler _Nullable handler)

{
	M68K_ARG(objc_uncaught_exception_handler _Nullable, handler, a0)

	return objc_setUncaughtExceptionHandler(handler);
}

void __saveds
glue_objc_setForwardHandler PPC_PARAMS(IMP _Nullable forward, IMP _Nullable stretForward)
{
	M68K_ARG(IMP _Nullable, forward, a0)
	M68K_ARG(IMP _Nullable, stretForward, a1)

	objc_setForwardHandler(forward, stretForward);
}

void __saveds
glue_objc_setEnumerationMutationHandler PPC_PARAMS(objc_enumeration_mutation_handler _Nullable hadler)

{
	M68K_ARG(objc_enumeration_mutation_handler _Nullable, hadler, a0)

	objc_setEnumerationMutationHandler(hadler);
}

id _Nullable __saveds
glue_objc_constructInstance PPC_PARAMS(Class _Nullable class, void *_Nullable bytes)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(void *_Nullable, bytes, a1)

	return objc_constructInstance(class, bytes);
}

void __saveds
glue_objc_deinit(void)
{
	objc_deinit();
}

Ivar _Nullable *_Nullable __saveds
glue_class_copyIvarList PPC_PARAMS(Class _Nullable class, unsigned int *_Nullable outCount)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(unsigned int *_Nullable, outCount, a1)

	return class_copyIvarList(class, outCount);
}

const char *_Nonnull __saveds
glue_ivar_getName PPC_PARAMS(Ivar _Nonnull ivar)
{
	M68K_ARG(Ivar _Nonnull, ivar, a0)

	return ivar_getName(ivar);
}

const char *_Nonnull __saveds
glue_ivar_getTypeEncoding PPC_PARAMS(Ivar _Nonnull ivar)
{
	M68K_ARG(Ivar _Nonnull, ivar, a0)

	return ivar_getTypeEncoding(ivar);
}

ptrdiff_t __saveds
glue_ivar_getOffset PPC_PARAMS(Ivar _Nonnull ivar)
{
	M68K_ARG(Ivar _Nonnull, ivar, a0)

	return ivar_getOffset(ivar);
}

Method _Nullable *_Nullable __saveds
glue_class_copyMethodList PPC_PARAMS(Class _Nullable class, unsigned int *_Nullable outCount)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(unsigned int *_Nullable, outCount, a1)

	return class_copyMethodList(class, outCount);
}

SEL _Nonnull __saveds
glue_method_getName PPC_PARAMS(Method _Nonnull method)
{
	M68K_ARG(Method _Nonnull, method, a0)

	return method_getName(method);
}

const char *_Nullable __saveds
glue_method_getTypeEncoding PPC_PARAMS(Method _Nonnull method)
{
	M68K_ARG(Method _Nonnull, method, a0)

	return method_getTypeEncoding(method);
}

objc_property_t _Nullable *_Nullable __saveds
glue_class_copyPropertyList PPC_PARAMS(Class _Nullable class, unsigned int *_Nullable outCount)
{
	M68K_ARG(Class _Nullable, class, a0)
	M68K_ARG(unsigned int *_Nullable, outCount, a1)

	return class_copyPropertyList(class, outCount);
}

const char *_Nonnull __saveds
glue_property_getName PPC_PARAMS(objc_property_t _Nonnull property)
{
	M68K_ARG(objc_property_t _Nonnull, property, a0)

	return property_getName(property);
}

char *_Nullable __saveds
glue_property_copyAttributeValue PPC_PARAMS(objc_property_t _Nonnull property, const char *_Nonnull name)

{
	M68K_ARG(objc_property_t _Nonnull, property, a0)
	M68K_ARG(const char *_Nonnull, name, a1)

	return property_copyAttributeValue(property, name);
}

void *_Nullable __saveds
glue_objc_destructInstance PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return objc_destructInstance(object);
}

void *_Null_unspecified __saveds
glue_objc_autoreleasePoolPush(void)
{
	return objc_autoreleasePoolPush();
}

void __saveds
glue_objc_autoreleasePoolPop PPC_PARAMS(void *_Null_unspecified pool)
{
	M68K_ARG(void *_Null_unspecified, pool, a0)

	objc_autoreleasePoolPop(pool);
}

id _Nullable __saveds
glue__objc_rootAutorelease PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return _objc_rootAutorelease(object);
}

struct objc_hashtable *_Nonnull __saveds
glue_objc_hashtable_new PPC_PARAMS(objc_hashtable_hash_func hash, objc_hashtable_equal_func equal, uint32_t size)

{
	M68K_ARG(objc_hashtable_hash_func, hash, a0)
	M68K_ARG(objc_hashtable_equal_func, equal, a1)
	M68K_ARG(uint32_t, size, d0)

	return objc_hashtable_new(hash, equal, size);
}

void __saveds
glue_objc_hashtable_set PPC_PARAMS(struct objc_hashtable *_Nonnull table, const void *_Nonnull key, const void *_Nonnull object)

{
	M68K_ARG(struct objc_hashtable *_Nonnull, table, a0)
	M68K_ARG(const void *_Nonnull, key, a1)
	M68K_ARG(const void *_Nonnull, object, a2)

	objc_hashtable_set(table, key, object);
}

void *_Nullable __saveds
glue_objc_hashtable_get PPC_PARAMS(struct objc_hashtable *_Nonnull table, const void *_Nonnull key)

{
	M68K_ARG(struct objc_hashtable *_Nonnull, table, a0)
	M68K_ARG(const void *_Nonnull, key, a1)

	return objc_hashtable_get(table, key);
}

void __saveds
glue_objc_hashtable_delete PPC_PARAMS(struct objc_hashtable *_Nonnull table, const void *_Nonnull key)

{
	M68K_ARG(struct objc_hashtable *_Nonnull, table, a0)
	M68K_ARG(const void *_Nonnull, key, a1)

	objc_hashtable_delete(table, key);
}

void __saveds
glue_objc_hashtable_free PPC_PARAMS(struct objc_hashtable *_Nonnull table)
{
	M68K_ARG(struct objc_hashtable *_Nonnull, table, a0)

	objc_hashtable_free(table);
}

void __saveds
glue_objc_setTaggedPointerSecret PPC_PARAMS(uintptr_t secret)
{
	M68K_ARG(uintptr_t, secret, d0)

	objc_setTaggedPointerSecret(secret);
}

int __saveds
glue_objc_registerTaggedPointerClass PPC_PARAMS(Class _Nonnull class)
{
	M68K_ARG(Class _Nonnull, class, a0)

	return objc_registerTaggedPointerClass(class);
}

bool __saveds
glue_object_isTaggedPointer PPC_PARAMS(id _Nullable object)
{
	M68K_ARG(id _Nullable, object, a0)

	return object_isTaggedPointer(object);
}









uintptr_t __saveds
glue_object_getTaggedPointerValue PPC_PARAMS(id _Nonnull object)
{
	M68K_ARG(id _Nonnull, object, a0)

	return object_getTaggedPointerValue(object);
}

id _Nullable __saveds
glue_objc_createTaggedPointer PPC_PARAMS(int class, uintptr_t value)
{
	M68K_ARG(int, class, d0)
	M68K_ARG(uintptr_t, value, d1)

	return objc_createTaggedPointer(class, value);
}

Modified src/runtime/amiga-library.m from [3b48513197] to [d177a01899].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "ObjFWRT.h"
#import "private.h"



#include <exec/libraries.h>
#include <exec/nodes.h>
#include <exec/resident.h>
#include <proto/exec.h>

#define CONCAT_VERSION2(major, minor) #major "." #minor

<
<
|

















>
>







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


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

#import "ObjFWRT.h"
#import "private.h"

#import "amiga-glue.h"

#include <exec/libraries.h>
#include <exec/nodes.h>
#include <exec/resident.h>
#include <proto/exec.h>

#define CONCAT_VERSION2(major, minor) #major "." #minor
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

/* This always needs to be the first thing in the file. */
int
_start()
{
	return -1;
}








struct ObjFWRTBase {
	struct Library library;
	void *segList;
	struct ObjFWRTBase *parent;
	char *dataSeg;
	bool initialized;
};

#ifdef OF_AMIGAOS_M68K
extern uintptr_t __CTOR_LIST__[];
extern const void *_EH_FRAME_BEGINS__;
extern void *_EH_FRAME_OBJECTS__;
#endif

extern bool glue_objc_init(void);
extern void glue___objc_exec_class(void);
extern IMP glue_objc_msg_lookup(void);
extern IMP glue_objc_msg_lookup_stret(void);
extern IMP glue_objc_msg_lookup_super(void);
extern IMP glue_objc_msg_lookup_super_stret(void);
extern Class glue_objc_lookUpClass(void);
extern Class glue_objc_getClass(void);
extern Class glue_objc_getRequiredClass(void);
extern Class glue_objc_lookup_class(void);
extern Class glue_objc_get_class(void);
extern void glue_objc_exception_throw(void);
extern int glue_objc_sync_enter(void);
extern int glue_objc_sync_exit(void);
extern id glue_objc_getProperty(void);
extern void glue_objc_setProperty(void);
extern void glue_objc_getPropertyStruct(void);
extern void glue_objc_setPropertyStruct(void);
extern void glue_objc_enumerationMutation(void);
extern int glue___gnu_objc_personality(void);
extern id glue_objc_retain(void);
extern id glue_objc_retainBlock(void);
extern id glue_objc_retainAutorelease(void);
extern void glue_objc_release(void);
extern id glue_objc_autorelease(void);
extern id glue_objc_autoreleaseReturnValue(void);
extern id glue_objc_retainAutoreleaseReturnValue(void);
extern id glue_objc_retainAutoreleasedReturnValue(void);
extern id glue_objc_storeStrong(void);
extern id glue_objc_storeWeak(void);
extern id glue_objc_loadWeakRetained(void);
extern id glue_objc_initWeak(void);
extern void glue_objc_destroyWeak(void);
extern id glue_objc_loadWeak(void);
extern void glue_objc_copyWeak(void);
extern void glue_objc_moveWeak(void);
extern SEL glue_sel_registerName(void);
extern const char *glue_sel_getName(void);
extern bool glue_sel_isEqual(void);
extern Class glue_objc_allocateClassPair(void);
extern void glue_objc_registerClassPair(void);
extern unsigned int glue_objc_getClassList(void);
extern Class *glue_objc_copyClassList(void);
extern bool glue_class_isMetaClass(void);
extern const char *glue_class_getName(void);
extern Class glue_class_getSuperclass(void);
extern unsigned long glue_class_getInstanceSize(void);
extern bool glue_class_respondsToSelector(void);
extern bool glue_class_conformsToProtocol(void);
extern IMP glue_class_getMethodImplementation(void);
extern IMP glue_class_getMethodImplementation_stret(void);
extern Method glue_class_getInstanceMethod(void);
extern bool glue_class_addMethod(void);
extern IMP glue_class_replaceMethod(void);
extern Class glue_object_getClass(void);
extern Class glue_object_setClass(void);
extern const char *glue_object_getClassName(void);
extern const char *glue_protocol_getName(void);
extern bool glue_protocol_isEqual(void);
extern bool glue_protocol_conformsToProtocol(void);
extern objc_uncaught_exception_handler_t
    glue_objc_setUncaughtExceptionHandler(void);
extern void glue_objc_setForwardHandler(void);
extern void glue_objc_setEnumerationMutationHandler(void);
extern id glue_objc_constructInstance(void);
extern void glue_objc_exit(void);
extern Ivar *glue_class_copyIvarList(void);
extern const char *glue_ivar_getName(void);
extern const char *glue_ivar_getTypeEncoding(void);
extern ptrdiff_t glue_ivar_getOffset(void);
extern Method *glue_class_copyMethodList(void);
extern SEL glue_method_getName(void);
extern const char *glue_method_getTypeEncoding(void);
extern objc_property_t *glue_class_copyPropertyList(void);
extern const char *glue_property_getName(void);
extern char *glue_property_copyAttributeValue(void);
extern void *glue_objc_destructInstance(void);
extern void *glue_objc_autoreleasePoolPush(void);
extern void glue_objc_autoreleasePoolPop(void);
extern id glue__objc_rootAutorelease(void);
extern struct objc_hashtable *glue_objc_hashtable_new(void);
extern void glue_objc_hashtable_set(void);
extern void *glue_objc_hashtable_get(void);
extern void glue_objc_hashtable_delete(void);
extern void glue_objc_hashtable_free(void);
extern void glue_objc_setTaggedPointerSecret(void);
extern int glue_objc_registerTaggedPointerClass(void);
extern bool glue_object_isTaggedPointer(void);
extern Class glue_object_getTaggedPointerClass(void);
extern uintptr_t glue_object_getTaggedPointerValue(void);
extern id glue_objc_createTaggedPointer(void);

#ifdef OF_MORPHOS
const ULONG __abox__ = 1;
#endif
struct ExecBase *SysBase;
struct objc_libc libc;
FILE *stdout;
FILE *stderr;

#if defined(OF_AMIGAOS_M68K)
__asm__ (
    ".text\n"
    ".globl ___restore_a4\n"
    ".align 1\n"
    "___restore_a4:\n"







>
>
>
>
>
>
>















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





<
<







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

/* This always needs to be the first thing in the file. */
int
_start()
{
	return -1;
}

#ifdef OF_AMIGAOS_M68K
void
__init_eh(void)
{
}
#endif

struct ObjFWRTBase {
	struct Library library;
	void *segList;
	struct ObjFWRTBase *parent;
	char *dataSeg;
	bool initialized;
};

#ifdef OF_AMIGAOS_M68K
extern uintptr_t __CTOR_LIST__[];
extern const void *_EH_FRAME_BEGINS__;
extern void *_EH_FRAME_OBJECTS__;
#endif





























































































#ifdef OF_MORPHOS
const ULONG __abox__ = 1;
#endif
struct ExecBase *SysBase;
struct objc_libc libc;



#if defined(OF_AMIGAOS_M68K)
__asm__ (
    ".text\n"
    ".globl ___restore_a4\n"
    ".align 1\n"
    "___restore_a4:\n"
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

	child->dataSeg += DATA_OFFSET;

	return &child->library;
}

static void *
expunge(struct ObjFWRTBase *base)
{

	void *segList;

	if (base->parent != NULL) {
		base->parent->library.lib_Flags |= LIBF_DELEXP;
		return 0;
	}

	if (base->library.lib_OpenCnt > 0) {
		base->library.lib_Flags |= LIBF_DELEXP;
		return 0;
	}

	segList = base->segList;

	Remove(&base->library.lib_Node);
	FreeMem((char *)base - base->library.lib_NegSize,
	    base->library.lib_NegSize + base->library.lib_PosSize);

	return segList;

}

static void *__saveds
lib_expunge(void)
{
	OBJC_M68K_ARG(struct ObjFWRTBase *, base, a6)

	return expunge(base);
}

static void *__saveds
lib_close(void)
{







	OBJC_M68K_ARG(struct ObjFWRTBase *, base, a6)

	if (base->parent != NULL) {
		struct ObjFWRTBase *parent;

#ifdef OF_AMIGAOS_M68K
		if (base->initialized)







|

>



















>







|





>
>
>
>
>
>
>







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

	child->dataSeg += DATA_OFFSET;

	return &child->library;
}

static void *
expunge(struct ObjFWRTBase *base, struct ExecBase *sysBase)
{
#define SysBase sysBase
	void *segList;

	if (base->parent != NULL) {
		base->parent->library.lib_Flags |= LIBF_DELEXP;
		return 0;
	}

	if (base->library.lib_OpenCnt > 0) {
		base->library.lib_Flags |= LIBF_DELEXP;
		return 0;
	}

	segList = base->segList;

	Remove(&base->library.lib_Node);
	FreeMem((char *)base - base->library.lib_NegSize,
	    base->library.lib_NegSize + base->library.lib_PosSize);

	return segList;
#undef SysBase
}

static void *__saveds
lib_expunge(void)
{
	OBJC_M68K_ARG(struct ObjFWRTBase *, base, a6)

	return expunge(base, SysBase);
}

static void *__saveds
lib_close(void)
{
	/*
	 * SysBase becomes invalid during this function, so we store it in
	 * sysBase and add a define to make the inlines use the right one.
	 */
	struct ExecBase *sysBase = SysBase;
#define SysBase sysBase

	OBJC_M68K_ARG(struct ObjFWRTBase *, base, a6)

	if (base->parent != NULL) {
		struct ObjFWRTBase *parent;

#ifdef OF_AMIGAOS_M68K
		if (base->initialized)
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
		    base->library.lib_NegSize + base->library.lib_PosSize);

		base = parent;
	}

	if (--base->library.lib_OpenCnt == 0 &&
	    (base->library.lib_Flags & LIBF_DELEXP))
		return expunge(base);

	return NULL;

}

static void *
lib_null(void)
{
	return NULL;
}

bool
objc_init(unsigned int version, struct objc_libc *libc_, FILE *stdout_,
    FILE *stderr_)
{
#ifdef OF_AMIGAOS_M68K
	OBJC_M68K_ARG(struct ObjFWRTBase *, base, a6)
#else
	register struct ObjFWRTBase *r12 __asm__("r12");
	struct ObjFWRTBase *base = r12;



#endif
	uintptr_t *iter, *iter0;

	if (version > 1)
		return false;

	memcpy(&libc, libc_, sizeof(libc));
	stdout = stdout_;
	stderr = stderr_;

#ifdef OF_AMIGAOS_M68K
	if ((size_t)_EH_FRAME_BEGINS__ != (size_t)_EH_FRAME_OBJECTS__)
		return false;

	for (size_t i = 1; i <= (size_t)_EH_FRAME_BEGINS__; i++)
		libc.__register_frame_info((&_EH_FRAME_BEGINS__)[i],
		    (&_EH_FRAME_OBJECTS__)[i]);

	iter0 = &__CTOR_LIST__[1];
#elif defined(OF_MORPHOS)
	__asm__ (
	    "lis	%0, ctors+4@ha\n\t"
	    "la		%0, ctors+4@l(%0)\n\t"


	    : "=r"(iter0)
	);


#endif

	for (iter = iter0; *iter != 0; iter++);

	while (iter > iter0) {
		void (*ctor)(void) = (void (*)(void))*--iter;
		ctor();







|


>









|
<






>
>
>







<
<












|
|
>
>
|

>
>







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
		    base->library.lib_NegSize + base->library.lib_PosSize);

		base = parent;
	}

	if (--base->library.lib_OpenCnt == 0 &&
	    (base->library.lib_Flags & LIBF_DELEXP))
		return expunge(base, sysBase);

	return NULL;
#undef SysBase
}

static void *
lib_null(void)
{
	return NULL;
}

bool
objc_init(unsigned int version, struct objc_libc *libc_)

{
#ifdef OF_AMIGAOS_M68K
	OBJC_M68K_ARG(struct ObjFWRTBase *, base, a6)
#else
	register struct ObjFWRTBase *r12 __asm__("r12");
	struct ObjFWRTBase *base = r12;
#endif
#ifdef OF_MORPHOS
	void *frame;
#endif
	uintptr_t *iter, *iter0;

	if (version > 1)
		return false;

	memcpy(&libc, libc_, sizeof(libc));



#ifdef OF_AMIGAOS_M68K
	if ((size_t)_EH_FRAME_BEGINS__ != (size_t)_EH_FRAME_OBJECTS__)
		return false;

	for (size_t i = 1; i <= (size_t)_EH_FRAME_BEGINS__; i++)
		libc.__register_frame_info((&_EH_FRAME_BEGINS__)[i],
		    (&_EH_FRAME_OBJECTS__)[i]);

	iter0 = &__CTOR_LIST__[1];
#elif defined(OF_MORPHOS)
	__asm__ (
	    "lis	%0, __EH_FRAME_BEGIN__@ha\n\t"
	    "la		%0, __EH_FRAME_BEGIN__@l(%0)\n\t"
	    "lis	%1, __CTOR_LIST__@ha\n\t"
	    "la		%1, __CTOR_LIST__@l(%1)\n\t"
	    : "=r"(frame), "=r"(iter0)
	);

	libc.__register_frame(frame);
#endif

	for (iter = iter0; *iter != 0; iter++);

	while (iter > iter0) {
		void (*ctor)(void) = (void (*)(void))*--iter;
		ctor();
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

void
free(void *ptr)
{
	libc.free(ptr);
}

int
fprintf(FILE *restrict stream, const char *restrict fmt, ...)
{
	int ret;
	va_list args;

	va_start(args, fmt);
	ret = libc.vfprintf(stream, fmt, args);
	va_end(args);

	return ret;
}

int
fflush(FILE *restrict stream)
{
	return libc.fflush(stream);
}

void
abort(void)
{
	libc.abort();

	OF_UNREACHABLE
}

#ifdef HAVE_SJLJ_EXCEPTIONS
int
_Unwind_SjLj_RaiseException(void *ex)
{
	return libc._Unwind_SjLj_RaiseException(ex);
}
#else







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







394
395
396
397
398
399
400



























401
402
403
404
405
406
407

void
free(void *ptr)
{
	libc.free(ptr);
}




























#ifdef HAVE_SJLJ_EXCEPTIONS
int
_Unwind_SjLj_RaiseException(void *ex)
{
	return libc._Unwind_SjLj_RaiseException(ex);
}
#else
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
#else
void
_Unwind_Resume(void *ex)
{
	libc._Unwind_Resume(ex);
}
#endif





































#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
static CONST_APTR functionTable[] = {
#ifdef OF_MORPHOS
	(CONST_APTR)FUNCARRAY_BEGIN,
	(CONST_APTR)FUNCARRAY_32BIT_NATIVE,
#endif
	(CONST_APTR)lib_open,
	(CONST_APTR)lib_close,
	(CONST_APTR)lib_expunge,
	(CONST_APTR)lib_null,
#ifdef OF_MORPHOS
	(CONST_APTR)-1,
	(CONST_APTR)FUNCARRAY_32BIT_SYSTEMV,
#endif
	(CONST_APTR)glue_objc_init,
	(CONST_APTR)glue___objc_exec_class,
	(CONST_APTR)glue_objc_msg_lookup,
	(CONST_APTR)glue_objc_msg_lookup_stret,
	(CONST_APTR)glue_objc_msg_lookup_super,
	(CONST_APTR)glue_objc_msg_lookup_super_stret,
	(CONST_APTR)glue_objc_lookUpClass,
	(CONST_APTR)glue_objc_getClass,
	(CONST_APTR)glue_objc_getRequiredClass,
	(CONST_APTR)glue_objc_lookup_class,
	(CONST_APTR)glue_objc_get_class,
	(CONST_APTR)glue_objc_exception_throw,
	(CONST_APTR)glue_objc_sync_enter,
	(CONST_APTR)glue_objc_sync_exit,
	(CONST_APTR)glue_objc_getProperty,
	(CONST_APTR)glue_objc_setProperty,
	(CONST_APTR)glue_objc_getPropertyStruct,
	(CONST_APTR)glue_objc_setPropertyStruct,
	(CONST_APTR)glue_objc_enumerationMutation,
	(CONST_APTR)glue___gnu_objc_personality,
	(CONST_APTR)glue_objc_retain,
	(CONST_APTR)glue_objc_retainBlock,
	(CONST_APTR)glue_objc_retainAutorelease,
	(CONST_APTR)glue_objc_release,
	(CONST_APTR)glue_objc_autorelease,
	(CONST_APTR)glue_objc_autoreleaseReturnValue,
	(CONST_APTR)glue_objc_retainAutoreleaseReturnValue,
	(CONST_APTR)glue_objc_retainAutoreleasedReturnValue,
	(CONST_APTR)glue_objc_storeStrong,
	(CONST_APTR)glue_objc_storeWeak,
	(CONST_APTR)glue_objc_loadWeakRetained,
	(CONST_APTR)glue_objc_initWeak,
	(CONST_APTR)glue_objc_destroyWeak,
	(CONST_APTR)glue_objc_loadWeak,
	(CONST_APTR)glue_objc_copyWeak,
	(CONST_APTR)glue_objc_moveWeak,
	(CONST_APTR)glue_sel_registerName,
	(CONST_APTR)glue_sel_getName,
	(CONST_APTR)glue_sel_isEqual,
	(CONST_APTR)glue_objc_allocateClassPair,
	(CONST_APTR)glue_objc_registerClassPair,
	(CONST_APTR)glue_objc_getClassList,
	(CONST_APTR)glue_objc_copyClassList,
	(CONST_APTR)glue_class_isMetaClass,
	(CONST_APTR)glue_class_getName,
	(CONST_APTR)glue_class_getSuperclass,
	(CONST_APTR)glue_class_getInstanceSize,
	(CONST_APTR)glue_class_respondsToSelector,
	(CONST_APTR)glue_class_conformsToProtocol,
	(CONST_APTR)glue_class_getMethodImplementation,
	(CONST_APTR)glue_class_getMethodImplementation_stret,
	(CONST_APTR)glue_class_getInstanceMethod,
	(CONST_APTR)glue_class_addMethod,
	(CONST_APTR)glue_class_replaceMethod,
	(CONST_APTR)glue_object_getClass,
	(CONST_APTR)glue_object_setClass,
	(CONST_APTR)glue_object_getClassName,
	(CONST_APTR)glue_protocol_getName,
	(CONST_APTR)glue_protocol_isEqual,
	(CONST_APTR)glue_protocol_conformsToProtocol,
	(CONST_APTR)glue_objc_setUncaughtExceptionHandler,
	(CONST_APTR)glue_objc_setForwardHandler,
	(CONST_APTR)glue_objc_setEnumerationMutationHandler,
	(CONST_APTR)glue_objc_constructInstance,
	(CONST_APTR)glue_objc_exit,
	(CONST_APTR)glue_class_copyIvarList,
	(CONST_APTR)glue_ivar_getName,
	(CONST_APTR)glue_ivar_getTypeEncoding,
	(CONST_APTR)glue_ivar_getOffset,
	(CONST_APTR)glue_class_copyMethodList,
	(CONST_APTR)glue_method_getName,
	(CONST_APTR)glue_method_getTypeEncoding,
	(CONST_APTR)glue_class_copyPropertyList,
	(CONST_APTR)glue_property_getName,
	(CONST_APTR)glue_property_copyAttributeValue,
	(CONST_APTR)glue_objc_destructInstance,
	(CONST_APTR)glue_objc_autoreleasePoolPush,
	(CONST_APTR)glue_objc_autoreleasePoolPop,
	(CONST_APTR)glue__objc_rootAutorelease,
	(CONST_APTR)glue_objc_hashtable_new,
	(CONST_APTR)glue_objc_hashtable_set,
	(CONST_APTR)glue_objc_hashtable_get,
	(CONST_APTR)glue_objc_hashtable_delete,
	(CONST_APTR)glue_objc_hashtable_free,
	(CONST_APTR)glue_objc_setTaggedPointerSecret,
	(CONST_APTR)glue_objc_registerTaggedPointerClass,
	(CONST_APTR)glue_object_isTaggedPointer,
	(CONST_APTR)glue_object_getTaggedPointerClass,
	(CONST_APTR)glue_object_getTaggedPointerValue,
	(CONST_APTR)glue_objc_createTaggedPointer,
	(CONST_APTR)-1,
#ifdef OF_MORPHOS
	(CONST_APTR)FUNCARRAY_END
#endif
};
#pragma GCC diagnostic pop








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
















|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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
#else
void
_Unwind_Resume(void *ex)
{
	libc._Unwind_Resume(ex);
}
#endif

#ifdef OF_AMIGAOS_M68K
int
snprintf(char *restrict str, size_t size, const char *restrict fmt, ...)
{
	va_list args;
	int ret;

	va_start(args, fmt);
	ret = vsnprintf(str, size, fmt, args);
	va_end(args);

	return ret;
}

int
vsnprintf(char *restrict str, size_t size, const char *restrict fmt,
    va_list args)
{
	return libc.vsnprintf(str, size, fmt, args);
}
#endif

int
atexit(void (*function)(void))
{
	return libc.atexit(function);
}

void
exit(int status)
{
	libc.exit(status);

	OF_UNREACHABLE
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
static CONST_APTR functionTable[] = {
#ifdef OF_MORPHOS
	(CONST_APTR)FUNCARRAY_BEGIN,
	(CONST_APTR)FUNCARRAY_32BIT_NATIVE,
#endif
	(CONST_APTR)lib_open,
	(CONST_APTR)lib_close,
	(CONST_APTR)lib_expunge,
	(CONST_APTR)lib_null,
#ifdef OF_MORPHOS
	(CONST_APTR)-1,
	(CONST_APTR)FUNCARRAY_32BIT_SYSTEMV,
#endif
#include "amiga-funcarray.inc"

























































































	(CONST_APTR)-1,
#ifdef OF_MORPHOS
	(CONST_APTR)FUNCARRAY_END
#endif
};
#pragma GCC diagnostic pop

717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733




734
735

736
737
738
739
#endif
	    ,
	.rt_Version = OBJFWRT_LIB_MAJOR,
	.rt_Type = NT_LIBRARY,
	.rt_Pri = 0,
	.rt_Name = (char *)OBJFWRT_AMIGA_LIB,
	.rt_IdString = (char *)"ObjFWRT " VERSION_STRING
	    " \xA9 2008-2019 Jonathan Schleifer",
	.rt_Init = &init_table,
#ifdef OF_MORPHOS
	.rt_Revision = OBJFWRT_LIB_MINOR,
	.rt_Tags = NULL,
#endif
};

#ifdef OF_MORPHOS
__asm__ (




    ".section .ctors, \"aw\", @progbits\n"
    "ctors:\n"

    "	.long -1\n"
    ".section .text"
);
#endif







|









>
>
>
>
|
|
>
|



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
#endif
	    ,
	.rt_Version = OBJFWRT_LIB_MAJOR,
	.rt_Type = NT_LIBRARY,
	.rt_Pri = 0,
	.rt_Name = (char *)OBJFWRT_AMIGA_LIB,
	.rt_IdString = (char *)"ObjFWRT " VERSION_STRING
	    " \xA9 2008-2022 Jonathan Schleifer",
	.rt_Init = &init_table,
#ifdef OF_MORPHOS
	.rt_Revision = OBJFWRT_LIB_MINOR,
	.rt_Tags = NULL,
#endif
};

#ifdef OF_MORPHOS
__asm__ (
    ".section .eh_frame, \"aw\"\n"
    ".globl __EH_FRAME_BEGIN__\n"
    ".type __EH_FRAME_BEGIN__, @object\n"
    "__EH_FRAME_BEGIN__:\n"
    ".section .ctors, \"aw\"\n"
    ".globl __CTOR_LIST__\n"
    ".type __CTOR_LIST__, @object\n"
    "__CTOR_LIST__:\n"
    ".section .text"
);
#endif

Added src/runtime/amiga-library.xml version [f6565586a9].



















































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
<amiga-library version='1.0' base='ObjFWRTBase'>
  <include>ObjFWRT.h</include>
  <include>private.h</include>
  <!-- The following function is only for the linklib. -->
  <function name='objc_init' return-type='bool'>
    <argument name='version' type='unsigned int' m68k-reg='d0'/>
    <argument name='libc' type='struct objc_libc *' m68k-reg='a0'/>
  </function>
  <function name='__objc_exec_class'>
    <argument name='module' type='struct objc_module *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_msg_lookup' return-type='IMP _Nonnull'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_msg_lookup_stret' return-type='IMP _Nonnull'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_msg_lookup_super' return-type='IMP _Nonnull'>
    <argument name='super' type='struct objc_super *_Nonnull' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_msg_lookup_super_stret' return-type='IMP _Nonnull'>
    <argument name='super' type='struct objc_super *_Nonnull' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_lookUpClass' return-type='Class _Nullable'>
    <argument name='name' type='const char *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_getClass' return-type='Class _Nullable'>
    <argument name='name' type='const char *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_getRequiredClass' return-type='Class _Nonnull'>
    <argument name='name' type='const char *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_lookup_class' return-type='Class _Nullable'>
    <argument name='name' type='const char *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_get_class' return-type='Class _Nonnull'>
    <argument name='name' type='const char *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_exception_throw' noreturn='1'>
    <argument name='object' type='id _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_sync_enter' return-type='int'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_sync_exit' return-type='int'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_getProperty' return-type='id _Nullable'>
    <argument name='self' type='id _Nonnull' m68k-reg='a0'/>
    <argument name='_cmd' type='SEL _Nonnull' m68k-reg='a1'/>
    <argument name='offset' type='ptrdiff_t' m68k-reg='d0'/>
    <argument name='atomic' type='bool' m68k-reg='d1'/>
  </function>
  <function name='objc_setProperty'>
    <argument name='self' type='id _Nonnull' m68k-reg='a0'/>
    <argument name='_cmd' type='SEL _Nonnull' m68k-reg='a1'/>
    <argument name='offset' type='ptrdiff_t' m68k-reg='d0'/>
    <argument name='value' type='id _Nullable' m68k-reg='a2'/>
    <argument name='atomic' type='bool' m68k-reg='d1'/>
    <argument name='copy' type='signed char' m68k-reg='d2'/>
  </function>
  <function name='objc_getPropertyStruct'>
    <argument name='dest' type='void *_Nonnull' m68k-reg='a0'/>
    <argument name='src' type='const void *_Nonnull' m68k-reg='a1'/>
    <argument name='size' type='ptrdiff_t' m68k-reg='d0'/>
    <argument name='atomic' type='bool' m68k-reg='d1'/>
    <argument name='strong' type='bool' m68k-reg='d2'/>
  </function>
  <function name='objc_setPropertyStruct'>
    <argument name='dest' type='void *_Nonnull' m68k-reg='a0'/>
    <argument name='src' type='const void *_Nonnull' m68k-reg='a1'/>
    <argument name='size' type='ptrdiff_t' m68k-reg='d0'/>
    <argument name='atomic' type='bool' m68k-reg='d1'/>
    <argument name='strong' type='bool' m68k-reg='d2'/>
  </function>
  <function name='objc_enumerationMutation'>
    <argument name='object' type='id _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='__gnu_objc_personality' return-type='int'>
    <argument name='version' type='int' m68k-reg='d0'/>
    <argument name='actions' type='int' m68k-reg='d1'/>
    <argument name='exClass' type='uint64_t *_Nonnull' m68k-reg='d2'/>
    <argument name='ex' type='void *_Nonnull' m68k-reg='a0'/>
    <argument name='ctx' type='void *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_retain' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_retainBlock' return-type='id _Nullable'>
    <argument name='block' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_retainAutorelease' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_release'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_autorelease' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_autoreleaseReturnValue' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_retainAutoreleaseReturnValue' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_retainAutoreleasedReturnValue'
            return-type='id _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_storeStrong' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable *_Nonnull' m68k-reg='a0'/>
    <argument name='value' type='id _Nullable' m68k-reg='a1'/>
  </function>
  <function name='objc_storeWeak' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable *_Nonnull' m68k-reg='a0'/>
    <argument name='value' type='id _Nullable' m68k-reg='a1'/>
  </function>
  <function name='objc_loadWeakRetained' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_initWeak' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable *_Nonnull' m68k-reg='a0'/>
    <argument name='value' type='id _Nullable' m68k-reg='a1'/>
  </function>
  <function name='objc_destroyWeak'>
    <argument name='object' type='id _Nullable *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_loadWeak' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_copyWeak'>
    <argument name='dest' type='id _Nullable *_Nonnull' m68k-reg='a0'/>
    <argument name='src' type='id _Nullable *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_moveWeak'>
    <argument name='dest' type='id _Nullable *_Nonnull' m68k-reg='a0'/>
    <argument name='src' type='id _Nullable *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='sel_registerName' return-type='SEL _Nonnull'>
    <argument name='name' type='const char *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='sel_getName' return-type='const char *_Nonnull'>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='sel_isEqual' return-type='bool'>
    <argument name='selector1' type='SEL _Nonnull' m68k-reg='a0'/>
    <argument name='selector2' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_allocateClassPair' return-type='Class _Nonnull'>
    <argument name='superclass' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='name' type='const char *_Nonnull' m68k-reg='a1'/>
    <argument name='extraBytes' type='size_t' m68k-reg='d0'/>
  </function>
  <function name='objc_registerClassPair'>
    <argument name='class' type='Class _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_getClassList' return-type='unsigned int'>
    <argument name='buffer' type='Class _Nonnull *_Nullable' m68k-reg='a0'/>
    <argument name='count' type='unsigned int' m68k-reg='d0'/>
  </function>
  <function name='objc_copyClassList' return-type='Class _Nonnull *_Nonnull'>
    <argument name='length' type='unsigned int *_Nullable' m68k-reg='a0'/>
  </function>
  <function name='class_isMetaClass' return-type='bool'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
  </function>
  <function name='class_getName' return-type='const char *_Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
  </function>
  <function name='class_getSuperclass' return-type='Class _Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
  </function>
  <function name='class_getInstanceSize' return-type='unsigned long'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
  </function>
  <function name='class_respondsToSelector' return-type='bool'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='class_conformsToProtocol' return-type='bool'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='p' type='Protocol *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='class_getMethodImplementation' return-type='IMP _Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='class_getMethodImplementation_stret'
            return-type='IMP _Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='class_getInstanceMethod' return-type='Method _Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='class_addMethod' return-type='bool'>
    <argument name='class' type='Class _Nonnull' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
    <argument name='implementation' type='IMP _Nonnull' m68k-reg='a2'/>
    <argument name='typeEncoding' type='const char *_Nullable' m68k-reg='a3'/>
  </function>
  <function name='class_replaceMethod' return-type='IMP _Nullable'>
    <argument name='class' type='Class _Nonnull' m68k-reg='a0'/>
    <argument name='selector' type='SEL _Nonnull' m68k-reg='a1'/>
    <argument name='implementation' type='IMP _Nonnull' m68k-reg='a2'/>
    <argument name='typeEncoding' type='const char *_Nullable' m68k-reg='a3'/>
  </function>
  <function name='object_getClass' return-type='Class _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='object_setClass' return-type='Class _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
    <argument name='class' type='Class _Nonnull' m68k-reg='a1'/>
  </function>
  <function name='object_getClassName' return-type='const char *_Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='protocol_getName' return-type='const char *_Nonnull'>
    <argument name='protocol' type='Protocol *_Nonnull' m68k-reg='a0'/>
  </function>
  <function name='protocol_isEqual' return-type='bool'>
    <argument name='protocol1' type='Protocol *_Nonnull' m68k-reg='a0'/>
    <argument name='protocol2' type='Protocol *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='protocol_conformsToProtocol' return-type='bool'>
    <argument name='protocol1' type='Protocol *_Nonnull' m68k-reg='a0'/>
    <argument name='protocol2' type='Protocol *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_setUncaughtExceptionHandler'
            return-type='_Nullable objc_uncaught_exception_handler'>
    <argument name='handler' type='objc_uncaught_exception_handler _Nullable'
              m68k-reg='a0'/>
  </function>
  <function name='objc_setForwardHandler'>
    <argument name='forward' type='IMP _Nullable' m68k-reg='a0'/>
    <argument name='stretForward' type='IMP _Nullable' m68k-reg='a1'/>
  </function>
  <function name='objc_setEnumerationMutationHandler'>
    <argument name='hadler' type='objc_enumeration_mutation_handler _Nullable'
              m68k-reg='a0'/>
  </function>
  <function name='objc_constructInstance' return-type='id _Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='bytes' type='void *_Nullable' m68k-reg='a1'/>
  </function>
  <function name='objc_deinit'/>
  <function name='class_copyIvarList' return-type='Ivar _Nullable *_Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='outCount' type='unsigned int *_Nullable' m68k-reg='a1'/>
  </function>
  <function name='ivar_getName' return-type='const char *_Nonnull'>
    <argument name='ivar' type='Ivar _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='ivar_getTypeEncoding' return-type='const char *_Nonnull'>
    <argument name='ivar' type='Ivar _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='ivar_getOffset' return-type='ptrdiff_t'>
    <argument name='ivar' type='Ivar _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='class_copyMethodList'
            return-type='Method _Nullable *_Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='outCount' type='unsigned int *_Nullable' m68k-reg='a1'/>
  </function>
  <function name='method_getName' return-type='SEL _Nonnull'>
    <argument name='method' type='Method _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='method_getTypeEncoding' return-type='const char *_Nullable'>
    <argument name='method' type='Method _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='class_copyPropertyList'
            return-type='objc_property_t _Nullable *_Nullable'>
    <argument name='class' type='Class _Nullable' m68k-reg='a0'/>
    <argument name='outCount' type='unsigned int *_Nullable' m68k-reg='a1'/>
  </function>
  <function name='property_getName' return-type='const char *_Nonnull'>
    <argument name='property' type='objc_property_t _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='property_copyAttributeValue' return-type='char *_Nullable'>
    <argument name='property' type='objc_property_t _Nonnull' m68k-reg='a0'/>
    <argument name='name' type='const char *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_destructInstance' return-type='void *_Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='objc_autoreleasePoolPush'
            return-type='void *_Null_unspecified'>
  </function>
  <function name='objc_autoreleasePoolPop'>
    <argument name='pool' type='void *_Null_unspecified' m68k-reg='a0'/>
  </function>
  <function name='_objc_rootAutorelease' return-type='id _Nullable'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <!-- The following functions are private! Don't use! -->
  <function name='objc_hashtable_new'
            return-type='struct objc_hashtable *_Nonnull'>
    <argument name='hash' type='objc_hashtable_hash_func' m68k-reg='a0'/>
    <argument name='equal' type='objc_hashtable_equal_func' m68k-reg='a1'/>
    <argument name='size' type='uint32_t' m68k-reg='d0'/>
  </function>
  <function name='objc_hashtable_set'>
    <argument name='table' type='struct objc_hashtable *_Nonnull'
              m68k-reg='a0'/>
    <argument name='key' type='const void *_Nonnull' m68k-reg='a1'/>
    <argument name='object' type='const void *_Nonnull' m68k-reg='a2'/>
  </function>
  <function name='objc_hashtable_get' return-type='void *_Nullable'>
    <argument name='table' type='struct objc_hashtable *_Nonnull'
              m68k-reg='a0'/>
    <argument name='key' type='const void *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_hashtable_delete'>
    <argument name='table' type='struct objc_hashtable *_Nonnull'
              m68k-reg='a0'/>
    <argument name='key' type='const void *_Nonnull' m68k-reg='a1'/>
  </function>
  <function name='objc_hashtable_free'>
    <argument name='table' type='struct objc_hashtable *_Nonnull'
              m68k-reg='a0'/>
  </function>
  <!-- Public functions again -->
  <function name='objc_setTaggedPointerSecret'>
    <argument name='secret' type='uintptr_t' m68k-reg='d0'/>
  </function>
  <function name='objc_registerTaggedPointerClass' return-type='int'>
    <argument name='class' type='Class _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='object_isTaggedPointer' return-type='bool'>
    <argument name='object' type='id _Nullable' m68k-reg='a0'/>
  </function>
  <function name='object_getTaggedPointerValue' return-type='uintptr_t'>
    <argument name='object' type='id _Nonnull' m68k-reg='a0'/>
  </function>
  <function name='objc_createTaggedPointer' return-type='id _Nullable'>
    <argument name='class' type='int' m68k-reg='d0'/>
    <argument name='value' type='uintptr_t' m68k-reg='d1'/>
  </function>
</amiga-library>

Deleted src/runtime/amigaos3.sfd version [7f54fc7db5].

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
==base _ObjFWRTBase
==basetype struct Library *
==libname objfwrt68k.library
==bias 30
==public
* The following function is only for the linklib.
bool glue_objc_init(unsigned int version, struct objc_libc *libc, FILE *stdout, FILE *stderr)(d0,a0,a1,a2)
void glue___objc_exec_class(struct objc_module *_Nonnull module)(a0)
IMP _Nonnull glue_objc_msg_lookup(id _Nullable object, SEL _Nonnull selector)(a0,a1)
IMP _Nonnull glue_objc_msg_lookup_stret(id _Nullable object, SEL _Nonnull selector)(a0,a1)
IMP _Nonnull glue_objc_msg_lookup_super(struct objc_super *_Nonnull super, SEL _Nonnull selector)(a0,a1)
IMP _Nonnull glue_objc_msg_lookup_super_stret(struct objc_super *_Nonnull super, SEL _Nonnull selector)(a0,a1)
Class _Nullable glue_objc_lookUpClass(const char *_Nonnull name)(a0)
Class _Nullable glue_objc_getClass(const char *_Nonnull name)(a0)
Class _Nonnull glue_objc_getRequiredClass(const char *_Nonnull name)(a0)
Class _Nullable glue_objc_lookup_class(const char *_Nonnull name)(a0)
Class _Nonnull glue_objc_get_class(const char *_Nonnull name)(a0)
void glue_objc_exception_throw(id _Nonnull object)(a0)
int glue_objc_sync_enter(id _Nullable object)(a0)
int glue_objc_sync_exit(id _Nullable object)(a0)
id glue_objc_getProperty(id _Nonnull self, SEL _Nonnull _cmd, ptrdiff_t offset, bool atomic)(a0,a1,d0,d1)
void glue_objc_setProperty(id _Nonnull self, SEL _Nonnull _cmd, ptrdiff_t offset, id value, bool atomic, signed char copy)(a0,a1,d0,a2,d1,d2)
void glue_objc_getPropertyStruct(void *_Nonnull dest, const void *_Nonnull src, ptrdiff_t size, bool atomic, bool strong)(a0,a1,d0,d1,d2)
void glue_objc_setPropertyStruct(void *_Nonnull dest, const void *_Nonnull src, ptrdiff_t size, bool atomic, bool strong)(a0,a1,d0,d1,d2)
void glue_objc_enumerationMutation(id _Nonnull object)(a0)
int glue___gnu_objc_personality(int version, int actions, uint64_t *_Nonnull exClass, void *_Nonnull ex, void *_Nonnull ctx)(d0,d1,d2,a0,a1)
id _Nullable glue_objc_retain(id _Nullable object)(a0)
id _Nullable glue_objc_retainBlock(id _Nullable block)(a0)
id _Nullable glue_objc_retainAutorelease(id _Nullable object)(a0)
void glue_objc_release(id _Nullable object)(a0)
id _Nullable glue_objc_autorelease(id _Nullable object)(a0)
id _Nullable glue_objc_autoreleaseReturnValue(id _Nullable object)(a0)
id _Nullable glue_objc_retainAutoreleaseReturnValue(id _Nullable object)(a0)
id _Nullable glue_objc_retainAutoreleasedReturnValue(id _Nullable object)(a0)
id _Nullable glue_objc_storeStrong(id _Nullable *_Nonnull object, id _Nullable value)(a0,a1)
id _Nullable glue_objc_storeWeak(id _Nullable *_Nonnull object, id _Nullable value)(a0,a1)
id _Nullable glue_objc_loadWeakRetained(id _Nullable *_Nonnull object)(a0)
id _Nullable glue_objc_initWeak(id _Nullable *_Nonnull object, id _Nullable value)(a0,a1)
void glue_objc_destroyWeak(id _Nullable *_Nonnull object)(a0)
id _Nullable glue_objc_loadWeak(id _Nullable *_Nonnull object)(a0)
void glue_objc_copyWeak(id _Nullable *_Nonnull dest, id _Nullable *_Nonnull src)(a0,a1)
void glue_objc_moveWeak(id _Nullable *_Nonnull dest, id _Nullable *_Nonnull src)(a0,a1)
SEL _Nonnull glue_sel_registerName(const char *_Nonnull name)(a0)
const char *_Nonnull glue_sel_getName(SEL _Nonnull selector)(a0)
bool glue_sel_isEqual(SEL _Nonnull selector1, SEL _Nonnull selector2)(a0,a1)
Class _Nonnull glue_objc_allocateClassPair(Class _Nullable superclass, const char *_Nonnull name, size_t extraBytes)(a0,a1,d0)
void glue_objc_registerClassPair(Class _Nonnull class_)(a0)
unsigned int glue_objc_getClassList(Class _Nonnull *_Nullable buffer, unsigned int count)(a0,d0)
Class _Nonnull *_Nonnull glue_objc_copyClassList(unsigned int *_Nullable length)(a0)
bool glue_class_isMetaClass(Class _Nullable class_)(a0)
const char *_Nullable glue_class_getName(Class _Nullable class_)(a0)
Class _Nullable glue_class_getSuperclass(Class _Nullable class_)(a0)
unsigned long glue_class_getInstanceSize(Class _Nullable class_)(a0)
bool glue_class_respondsToSelector(Class _Nullable class_, SEL _Nonnull selector)(a0,a1)
bool glue_class_conformsToProtocol(Class _Nullable class_, Protocol *_Nonnull p)(a0,a1)
IMP _Nullable glue_class_getMethodImplementation(Class _Nullable class_, SEL _Nonnull selector)(a0,a1)
IMP _Nullable glue_class_getMethodImplementation_stret(Class _Nullable class_, SEL _Nonnull selector)(a0,a1)
Method _Nullable glue_class_getInstanceMethod(Class _Nullable class_, SEL _Nonnull selector)(a0,a1)
bool glue_class_addMethod(Class _Nonnull class_, SEL _Nonnull selector, IMP _Nonnull implementation, const char *_Nullable typeEncoding)(a0,a1,a2,a3)
IMP _Nullable glue_class_replaceMethod(Class _Nonnull class_, SEL _Nonnull selector, IMP _Nonnull implementation, const char *_Nullable typeEncoding)(a0,a1,a2,a3)
Class _Nullable glue_object_getClass(id _Nullable object)(a0)
Class _Nullable glue_object_setClass(id _Nullable object, Class _Nonnull class_)(a0,a1)
const char *_Nullable glue_object_getClassName(id _Nullable object)(a0)
const char *_Nonnull glue_protocol_getName(Protocol *_Nonnull protocol)(a0)
bool glue_protocol_isEqual(Protocol *_Nonnull protocol1, Protocol *_Nonnull protocol2)(a0,a1)
bool glue_protocol_conformsToProtocol(Protocol *_Nonnull protocol1, Protocol *_Nonnull protocol2)(a0,a1)
_Nullable objc_uncaught_exception_handler_t glue_objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler_t _Nullable handler)(a0)
void glue_objc_setForwardHandler(IMP _Nullable forward, IMP _Nullable stretForward)(a0,a1)
void glue_objc_setEnumerationMutationHandler(objc_enumeration_mutation_handler_t _Nullable handler)(a0)
id _Nullable glue_objc_constructInstance(Class _Nullable class_, void *_Nullable bytes)(a0,a1)
void glue_objc_exit(void)()
Ivar _Nullable *_Nullable glue_class_copyIvarList(Class _Nullable class_, unsigned int *_Nullable outCount)(a0,a1)
const char *_Nonnull glue_ivar_getName(Ivar _Nonnull ivar)(a0)
const char *_Nonnull glue_ivar_getTypeEncoding(Ivar _Nonnull ivar)(a0)
ptrdiff_t glue_ivar_getOffset(Ivar _Nonnull ivar)(a0)
Method _Nullable *_Nullable glue_class_copyMethodList(Class _Nullable class_, unsigned int *_Nullable outCount)(a0,a1)
SEL _Nonnull glue_method_getName(Method _Nonnull method)(a0)
const char *_Nullable glue_method_getTypeEncoding(Method _Nonnull method)(a0)
objc_property_t _Nullable *_Nullable glue_class_copyPropertyList(Class _Nullable class_, unsigned int *_Nullable outCount)(a0,a1)
const char *_Nonnull glue_property_getName(objc_property_t _Nonnull property)(a0)
char *_Nullable glue_property_copyAttributeValue(objc_property_t _Nonnull property, const char *_Nonnull name)(a0,a1)
void *_Nullable glue_objc_destructInstance(id _Nullable object)(a0)
void *_Null_unspecified glue_objc_autoreleasePoolPush(void)()
void glue_objc_autoreleasePoolPop(void *_Null_unspecified pool)(a0)
id _Nullable glue__objc_rootAutorelease(id _Nullable object)(a0)
* The following functions are private! Don't use!
struct objc_hashtable *_Nonnull glue_objc_hashtable_new(objc_hashtable_hash_func hash, objc_hashtable_equal_func equal, uint32_t size)(a0,a1,d0)
void glue_objc_hashtable_set(struct objc_hashtable *_Nonnull table, const void *_Nonnull key, const void *_Nonnull object)(a0,a1,a2)
void *_Nullable glue_objc_hashtable_get(struct objc_hashtable *_Nonnull table, const void *_Nonnull key)(a0,a1)
void glue_objc_hashtable_delete(struct objc_hashtable *_Nonnull table, const void *_Nonnull key)(a0,a1)
void glue_objc_hashtable_free(struct objc_hashtable *_Nonnull table)(a0)
* Public functions again
void glue_objc_setTaggedPointerSecret(uintptr_t secret)(d0)
int glue_objc_registerTaggedPointerClass(Class _Nonnull class_)(a0)
bool glue_object_isTaggedPointer(id _Nullable object)(a0)
Class _Nullable glue_object_getTaggedPointerClass(id _Nonnull object)(a0)
uintptr_t glue_object_getTaggedPointerValue(id _Nonnull object)(a0)
id _Nullable glue_objc_createTaggedPointer(int class_, uintptr_t value)(d0,d1)
==end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































Modified src/runtime/arc.m from [7f174d7d34] to [262fd1b7bb].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "ObjFWRT.h"
#import "private.h"

#ifdef OF_HAVE_THREADS
# import "mutex.h"
#endif

struct weak_ref {
	id **locations;
	size_t count;
};

static struct objc_hashtable *hashtable;
#ifdef OF_HAVE_THREADS
static of_spinlock_t spinlock;
#endif

static uint32_t
hash(const void *object)
{
	return (uint32_t)(uintptr_t)object;
}

static bool
equal(const void *object1, const void *object2)
{
	return (object1 == object2);
}

OF_CONSTRUCTOR()
{
	hashtable = objc_hashtable_new(hash, equal, 2);

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_new(&spinlock))
		OBJC_ERROR("Failed to create spinlock!")
#endif
}

id
objc_retain(id object)
{
	return [object retain];

<
<
|



















|


|






|



















|
|







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


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

#import "ObjFWRT.h"
#import "private.h"

#ifdef OF_HAVE_THREADS
# import "OFPlainMutex.h"
#endif

struct WeakRef {
	id **locations;
	size_t count;
};

static struct objc_hashtable *hashtable;
#ifdef OF_HAVE_THREADS
static OFSpinlock spinlock;
#endif

static uint32_t
hash(const void *object)
{
	return (uint32_t)(uintptr_t)object;
}

static bool
equal(const void *object1, const void *object2)
{
	return (object1 == object2);
}

OF_CONSTRUCTOR()
{
	hashtable = objc_hashtable_new(hash, equal, 2);

#ifdef OF_HAVE_THREADS
	if (OFSpinlockNew(&spinlock) != 0)
		OBJC_ERROR("Failed to create spinlock!");
#endif
}

id
objc_retain(id object)
{
	return [object retain];
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

	return value;
}

id
objc_storeWeak(id *object, id value)
{
	struct weak_ref *old;

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_lock(&spinlock))
		OBJC_ERROR("Failed to lock spinlock!")
#endif

	if (*object != nil &&
	    (old = objc_hashtable_get(hashtable, *object)) != NULL) {
		for (size_t i = 0; i < old->count; i++) {
			if (old->locations[i] == object) {
				if (--old->count == 0) {







|


|
|







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

	return value;
}

id
objc_storeWeak(id *object, id value)
{
	struct WeakRef *old;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlock) != 0)
		OBJC_ERROR("Failed to lock spinlock!");
#endif

	if (*object != nil &&
	    (old = objc_hashtable_get(hashtable, *object)) != NULL) {
		for (size_t i = 0; i < old->count; i++) {
			if (old->locations[i] == object) {
				if (--old->count == 0) {
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
				break;
			}
		}
	}

	if (value != nil && class_respondsToSelector(object_getClass(value),
	    @selector(allowsWeakReference)) && [value allowsWeakReference]) {
		struct weak_ref *ref = objc_hashtable_get(hashtable, value);

		if (ref == NULL) {
			if ((ref = calloc(1, sizeof(*ref))) == NULL)
				OBJC_ERROR("Not enough memory to allocate weak "
				    "reference!");

			objc_hashtable_set(hashtable, value, ref);
		}

		if ((ref->locations = realloc(ref->locations,
		    (ref->count + 1) * sizeof(id *))) == NULL)
			OBJC_ERROR("Not enough memory to allocate weak "
			    "reference!")

		ref->locations[ref->count++] = object;
	} else
		value = nil;

	*object = value;

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_unlock(&spinlock))
		OBJC_ERROR("Failed to unlock spinlock!")
#endif

	return value;
}

id
objc_loadWeakRetained(id *object)
{
	id value = nil;
	struct weak_ref *ref;

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_lock(&spinlock))
		OBJC_ERROR("Failed to lock spinlock!")
#endif

	if (*object != nil &&
	    (ref = objc_hashtable_get(hashtable, *object)) != NULL)
		value = *object;

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_unlock(&spinlock))
		OBJC_ERROR("Failed to unlock spinlock!")
#endif

	if (class_respondsToSelector(object_getClass(value),
	    @selector(retainWeakReference)) && [value retainWeakReference])
		return value;

	return nil;







|












|








|
|









|


|
|







|
|







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

	if (value != nil && class_respondsToSelector(object_getClass(value),
	    @selector(allowsWeakReference)) && [value allowsWeakReference]) {
		struct WeakRef *ref = objc_hashtable_get(hashtable, value);

		if (ref == NULL) {
			if ((ref = calloc(1, sizeof(*ref))) == NULL)
				OBJC_ERROR("Not enough memory to allocate weak "
				    "reference!");

			objc_hashtable_set(hashtable, value, ref);
		}

		if ((ref->locations = realloc(ref->locations,
		    (ref->count + 1) * sizeof(id *))) == NULL)
			OBJC_ERROR("Not enough memory to allocate weak "
			    "reference!");

		ref->locations[ref->count++] = object;
	} else
		value = nil;

	*object = value;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockUnlock(&spinlock) != 0)
		OBJC_ERROR("Failed to unlock spinlock!");
#endif

	return value;
}

id
objc_loadWeakRetained(id *object)
{
	id value = nil;
	struct WeakRef *ref;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlock) != 0)
		OBJC_ERROR("Failed to lock spinlock!");
#endif

	if (*object != nil &&
	    (ref = objc_hashtable_get(hashtable, *object)) != NULL)
		value = *object;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockUnlock(&spinlock) != 0)
		OBJC_ERROR("Failed to unlock spinlock!");
#endif

	if (class_respondsToSelector(object_getClass(value),
	    @selector(retainWeakReference)) && [value retainWeakReference])
		return value;

	return nil;
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
{
	objc_release(objc_initWeak(dest, objc_loadWeakRetained(src)));
}

void
objc_moveWeak(id *dest, id *src)
{
	struct weak_ref *ref;

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_lock(&spinlock))
		OBJC_ERROR("Failed to lock spinlock!")
#endif

	if (*src != nil &&
	    (ref = objc_hashtable_get(hashtable, *src)) != NULL) {
		for (size_t i = 0; i < ref->count; i++) {
			if (ref->locations[i] == src) {
				ref->locations[i] = dest;
				break;
			}
		}
	}

	*dest = *src;
	*src = nil;

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_unlock(&spinlock))
		OBJC_ERROR("Failed to unlock spinlock!")
#endif
}

void
objc_zero_weak_references(id value)
{
	struct weak_ref *ref;

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_lock(&spinlock))
		OBJC_ERROR("Failed to lock spinlock!")
#endif

	if ((ref = objc_hashtable_get(hashtable, value)) != NULL) {
		for (size_t i = 0; i < ref->count; i++)
			*ref->locations[i] = nil;

		objc_hashtable_delete(hashtable, value);
		free(ref->locations);
		free(ref);
	}

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_unlock(&spinlock))
		OBJC_ERROR("Failed to unlock spinlock!")
#endif
}







|


|
|
















|
|




|

|


|
|












|
|


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
{
	objc_release(objc_initWeak(dest, objc_loadWeakRetained(src)));
}

void
objc_moveWeak(id *dest, id *src)
{
	struct WeakRef *ref;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlock) != 0)
		OBJC_ERROR("Failed to lock spinlock!");
#endif

	if (*src != nil &&
	    (ref = objc_hashtable_get(hashtable, *src)) != NULL) {
		for (size_t i = 0; i < ref->count; i++) {
			if (ref->locations[i] == src) {
				ref->locations[i] = dest;
				break;
			}
		}
	}

	*dest = *src;
	*src = nil;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockUnlock(&spinlock) != 0)
		OBJC_ERROR("Failed to unlock spinlock!");
#endif
}

void
objc_zeroWeakReferences(id value)
{
	struct WeakRef *ref;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlock) != 0)
		OBJC_ERROR("Failed to lock spinlock!");
#endif

	if ((ref = objc_hashtable_get(hashtable, value)) != NULL) {
		for (size_t i = 0; i < ref->count; i++)
			*ref->locations[i] = nil;

		objc_hashtable_delete(hashtable, value);
		free(ref->locations);
		free(ref);
	}

#ifdef OF_HAVE_THREADS
	if (OFSpinlockUnlock(&spinlock) != 0)
		OBJC_ERROR("Failed to unlock spinlock!");
#endif
}

Modified src/runtime/autorelease.m from [e5ceb906d0] to [44274c4d6f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
# import "private.h"
#else
# import <objc/runtime.h>
#endif

#import "macros.h"
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
# import "tlskey.h"
#endif

#ifndef OF_OBJFW_RUNTIME
@interface DummyObject
- (void)release;
@end
#endif






#if defined(OF_HAVE_COMPILER_TLS)
static thread_local id *objects = NULL;
static thread_local uintptr_t count = 0;
static thread_local uintptr_t size = 0;
#elif defined(OF_HAVE_THREADS)
static of_tlskey_t objectsKey, countKey, sizeKey;
#else
static id *objects = NULL;
static uintptr_t count = 0;
static uintptr_t size = 0;
#endif

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
OF_CONSTRUCTOR()
{
	OF_ENSURE(of_tlskey_new(&objectsKey));
	OF_ENSURE(of_tlskey_new(&countKey));
	OF_ENSURE(of_tlskey_new(&sizeKey));

}
#endif

void *
objc_autoreleasePoolPush()
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);
#endif
	return (void *)count;
}

void
objc_autoreleasePoolPop(void *pool)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *objects = of_tlskey_get(objectsKey);
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);
#endif
	uintptr_t idx = (uintptr_t)pool;
	bool freeMem = false;

	if (idx == (uintptr_t)-1) {
		idx++;
		freeMem = true;
	}

	for (uintptr_t i = idx; i < count; i++) {
		[objects[i] release];

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		objects = of_tlskey_get(objectsKey);
		count = (uintptr_t)of_tlskey_get(countKey);
#endif
	}

	count = idx;

	if (freeMem) {
		free(objects);
		objects = NULL;
#if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
		size = 0;
#else
		OF_ENSURE(of_tlskey_set(objectsKey, objects));
		OF_ENSURE(of_tlskey_set(sizeKey, (void *)0));

#endif
	}

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OF_ENSURE(of_tlskey_set(countKey, (void *)count));

#endif
}

id
_objc_rootAutorelease(id object)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *objects = of_tlskey_get(objectsKey);
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);
	uintptr_t size = (uintptr_t)of_tlskey_get(sizeKey);
#endif

	if (count >= size) {
		if (size == 0)
			size = 16;
		else
			size *= 2;

		OF_ENSURE((objects =
		    realloc(objects, size * sizeof(id))) != NULL);


#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		OF_ENSURE(of_tlskey_set(objectsKey, objects));
		OF_ENSURE(of_tlskey_set(sizeKey, (void *)size));

#endif
	}

	objects[count++] = object;

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OF_ENSURE(of_tlskey_set(countKey, (void *)count));

#endif

	return object;
}







|







>
>
>
>
>






|









|
<
|
>







|








|
|













|
|











|
|
>




|
>







|
|
|








<
|
>


|
|
>






|
>




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
# import "private.h"
#else
# import <objc/runtime.h>
#endif

#import "macros.h"
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
# import "OFTLSKey.h"
#endif

#ifndef OF_OBJFW_RUNTIME
@interface DummyObject
- (void)release;
@end
#endif

#ifndef OBJC_ERROR
/* This is also used with old Apple runtimes that lack autorelease pools. */
# define OBJC_ERROR(...) abort()
#endif

#if defined(OF_HAVE_COMPILER_TLS)
static thread_local id *objects = NULL;
static thread_local uintptr_t count = 0;
static thread_local uintptr_t size = 0;
#elif defined(OF_HAVE_THREADS)
static OFTLSKey objectsKey, countKey, sizeKey;
#else
static id *objects = NULL;
static uintptr_t count = 0;
static uintptr_t size = 0;
#endif

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
OF_CONSTRUCTOR()
{
	if (OFTLSKeyNew(&objectsKey) != 0 || OFTLSKeyNew(&countKey) != 0 ||

	    OFTLSKeyNew(&sizeKey) != 0)
		OBJC_ERROR("Failed to create TLS keys!");
}
#endif

void *
objc_autoreleasePoolPush()
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	uintptr_t count = (uintptr_t)OFTLSKeyGet(countKey);
#endif
	return (void *)count;
}

void
objc_autoreleasePoolPop(void *pool)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *objects = OFTLSKeyGet(objectsKey);
	uintptr_t count = (uintptr_t)OFTLSKeyGet(countKey);
#endif
	uintptr_t idx = (uintptr_t)pool;
	bool freeMem = false;

	if (idx == (uintptr_t)-1) {
		idx++;
		freeMem = true;
	}

	for (uintptr_t i = idx; i < count; i++) {
		[objects[i] release];

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		objects = OFTLSKeyGet(objectsKey);
		count = (uintptr_t)OFTLSKeyGet(countKey);
#endif
	}

	count = idx;

	if (freeMem) {
		free(objects);
		objects = NULL;
#if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
		size = 0;
#else
		if (OFTLSKeySet(objectsKey, objects) != 0 ||
		    OFTLSKeySet(sizeKey, (void *)0) != 0)
			OBJC_ERROR("Failed to set TLS key!");
#endif
	}

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	if (OFTLSKeySet(countKey, (void *)count) != 0)
		OBJC_ERROR("Failed to set TLS key!");
#endif
}

id
_objc_rootAutorelease(id object)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *objects = OFTLSKeyGet(objectsKey);
	uintptr_t count = (uintptr_t)OFTLSKeyGet(countKey);
	uintptr_t size = (uintptr_t)OFTLSKeyGet(sizeKey);
#endif

	if (count >= size) {
		if (size == 0)
			size = 16;
		else
			size *= 2;


		if ((objects = realloc(objects, size * sizeof(id))) == NULL)
			OBJC_ERROR("Failed to resize autorelease pool!");

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		if (OFTLSKeySet(objectsKey, objects) != 0 ||
		    OFTLSKeySet(sizeKey, (void *)size) != 0)
			OBJC_ERROR("Failed to set TLS key!");
#endif
	}

	objects[count++] = object;

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	if (OFTLSKeySet(countKey, (void *)count) != 0)
		OBJC_ERROR("Failed to set TLS key!");
#endif

	return object;
}

Modified src/runtime/category.m from [d697edeb41] to [7faaa519d3].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
registerSelectors(struct objc_category *category)
{
	struct objc_method_list *iter;
	unsigned int i;

	for (iter = category->instanceMethods; iter != NULL; iter = iter->next)
		for (i = 0; i < iter->count; i++)
			objc_register_selector(&iter->methods[i].selector);

	for (iter = category->classMethods; iter != NULL; iter = iter->next)
		for (i = 0; i < iter->count; i++)
			objc_register_selector(&iter->methods[i].selector);
}

static void
registerCategory(struct objc_category *category)
{
	struct objc_category **categories;
	Class class = objc_classname_to_class(category->className, false);

	if (categoriesMap == NULL)
		categoriesMap = objc_hashtable_new(
		    objc_hash_string, objc_equal_string, 2);

	categories = (struct objc_category **)objc_hashtable_get(
	    categoriesMap, category->className);

	if (categories != NULL) {
		struct objc_category **newCategories;
		size_t i;







|



|






|



|







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
registerSelectors(struct objc_category *category)
{
	struct objc_method_list *iter;
	unsigned int i;

	for (iter = category->instanceMethods; iter != NULL; iter = iter->next)
		for (i = 0; i < iter->count; i++)
			objc_registerSelector(&iter->methods[i].selector);

	for (iter = category->classMethods; iter != NULL; iter = iter->next)
		for (i = 0; i < iter->count; i++)
			objc_registerSelector(&iter->methods[i].selector);
}

static void
registerCategory(struct objc_category *category)
{
	struct objc_category **categories;
	Class class = objc_classnameToClass(category->className, false);

	if (categoriesMap == NULL)
		categoriesMap = objc_hashtable_new(
		    objc_string_hash, objc_string_equal, 2);

	categories = (struct objc_category **)objc_hashtable_get(
	    categoriesMap, category->className);

	if (categories != NULL) {
		struct objc_category **newCategories;
		size_t i;
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

		newCategories[i] = category;
		newCategories[i + 1] = NULL;
		objc_hashtable_set(categoriesMap, category->className,
		    newCategories);

		if (class != Nil && class->info & OBJC_CLASS_INFO_SETUP) {
			objc_update_dtable(class);
			objc_update_dtable(class->isa);
		}

		return;
	}

	if ((categories = malloc(2 * sizeof(*categories))) == NULL)
		OBJC_ERROR("Not enough memory for category %s of class %s!\n",
		    category->categoryName, category->className);

	categories[0] = category;
	categories[1] = NULL;
	objc_hashtable_set(categoriesMap, category->className, categories);

	if (class != Nil && class->info & OBJC_CLASS_INFO_SETUP) {
		objc_update_dtable(class);
		objc_update_dtable(class->isa);
	}
}

void
objc_register_all_categories(struct objc_symtab *symtab)
{
	struct objc_category **categories =
	    (struct objc_category **)symtab->defs + symtab->classDefsCount;

	for (size_t i = 0; i < symtab->categoryDefsCount; i++) {
		registerSelectors(categories[i]);
		registerCategory(categories[i]);
	}
}

struct objc_category **
objc_categories_for_class(Class class)
{
	if (categoriesMap == NULL)
		return NULL;

	return (struct objc_category **)objc_hashtable_get(categoriesMap,
	    class->name);
}

void
objc_unregister_all_categories(void)
{
	if (categoriesMap == NULL)
		return;

	for (uint32_t i = 0; i < categoriesMap->size; i++)
		if (categoriesMap->data[i] != NULL)
			free((void *)categoriesMap->data[i]->object);

	objc_hashtable_free(categoriesMap);
	categoriesMap = NULL;
}







|
|














|
|




|











|









|











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

		newCategories[i] = category;
		newCategories[i + 1] = NULL;
		objc_hashtable_set(categoriesMap, category->className,
		    newCategories);

		if (class != Nil && class->info & OBJC_CLASS_INFO_SETUP) {
			objc_updateDTable(class);
			objc_updateDTable(class->isa);
		}

		return;
	}

	if ((categories = malloc(2 * sizeof(*categories))) == NULL)
		OBJC_ERROR("Not enough memory for category %s of class %s!\n",
		    category->categoryName, category->className);

	categories[0] = category;
	categories[1] = NULL;
	objc_hashtable_set(categoriesMap, category->className, categories);

	if (class != Nil && class->info & OBJC_CLASS_INFO_SETUP) {
		objc_updateDTable(class);
		objc_updateDTable(class->isa);
	}
}

void
objc_registerAllCategories(struct objc_symtab *symtab)
{
	struct objc_category **categories =
	    (struct objc_category **)symtab->defs + symtab->classDefsCount;

	for (size_t i = 0; i < symtab->categoryDefsCount; i++) {
		registerSelectors(categories[i]);
		registerCategory(categories[i]);
	}
}

struct objc_category **
objc_categoriesForClass(Class class)
{
	if (categoriesMap == NULL)
		return NULL;

	return (struct objc_category **)objc_hashtable_get(categoriesMap,
	    class->name);
}

void
objc_unregisterAllCategories(void)
{
	if (categoriesMap == NULL)
		return;

	for (uint32_t i = 0; i < categoriesMap->size; i++)
		if (categoriesMap->data[i] != NULL)
			free((void *)categoriesMap->data[i]->object);

	objc_hashtable_free(categoriesMap);
	categoriesMap = NULL;
}

Modified src/runtime/class.m from [0dc727f4b5] to [8d4b7cca0f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
static struct objc_sparsearray *fastPath = NULL;

static void
registerClass(Class class)
{
	if (classes == NULL)
		classes = objc_hashtable_new(
		    objc_hash_string, objc_equal_string, 2);

	objc_hashtable_set(classes, class->name, class);

	if (emptyDTable == NULL)
		emptyDTable = objc_dtable_new();

	class->DTable = emptyDTable;
	class->isa->DTable = emptyDTable;

	if (strcmp(class->name, "Protocol") != 0)
		classesCount++;
}

bool
class_registerAlias_np(Class class, const char *name)
{
	objc_global_mutex_lock();

	if (classes == NULL) {
		objc_global_mutex_unlock();

		return NO;
	}

	objc_hashtable_set(classes, name, (Class)((uintptr_t)class | 1));

	objc_global_mutex_unlock();

	return YES;
}

static void
registerSelectors(Class class)
{
	struct objc_method_list *iter;
	unsigned int i;

	for (iter = class->methodList; iter != NULL; iter = iter->next)
		for (i = 0; i < iter->count; i++)
			objc_register_selector(&iter->methods[i].selector);
}

Class
objc_classname_to_class(const char *name, bool cache)
{
	Class class;

	if (classes == NULL)
		return Nil;

	/*







|






|
|








|


|






|












|



|







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
static struct objc_sparsearray *fastPath = NULL;

static void
registerClass(Class class)
{
	if (classes == NULL)
		classes = objc_hashtable_new(
		    objc_string_hash, objc_string_equal, 2);

	objc_hashtable_set(classes, class->name, class);

	if (emptyDTable == NULL)
		emptyDTable = objc_dtable_new();

	class->dTable = emptyDTable;
	class->isa->dTable = emptyDTable;

	if (strcmp(class->name, "Protocol") != 0)
		classesCount++;
}

bool
class_registerAlias_np(Class class, const char *name)
{
	objc_globalMutex_lock();

	if (classes == NULL) {
		objc_globalMutex_unlock();

		return NO;
	}

	objc_hashtable_set(classes, name, (Class)((uintptr_t)class | 1));

	objc_globalMutex_unlock();

	return YES;
}

static void
registerSelectors(Class class)
{
	struct objc_method_list *iter;
	unsigned int i;

	for (iter = class->methodList; iter != NULL; iter = iter->next)
		for (i = 0; i < iter->count; i++)
			objc_registerSelector(&iter->methods[i].selector);
}

Class
objc_classnameToClass(const char *name, bool cache)
{
	Class class;

	if (classes == NULL)
		return Nil;

	/*
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
	if (cache && fastPath != NULL) {
		class = objc_sparsearray_get(fastPath, (uintptr_t)name);

		if (class != Nil)
			return class;
	}

	objc_global_mutex_lock();

	class = (Class)((uintptr_t)objc_hashtable_get(classes, name) & ~1);

	if (cache && fastPath == NULL && --lookupsUntilFastPath == 0)
		fastPath = objc_sparsearray_new(sizeof(uintptr_t));

	if (cache && fastPath != NULL)
		objc_sparsearray_set(fastPath, (uintptr_t)name, class);

	objc_global_mutex_unlock();

	return class;
}

static void
callSelector(Class class, SEL selector)
{







|









|







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
	if (cache && fastPath != NULL) {
		class = objc_sparsearray_get(fastPath, (uintptr_t)name);

		if (class != Nil)
			return class;
	}

	objc_globalMutex_lock();

	class = (Class)((uintptr_t)objc_hashtable_get(classes, name) & ~1);

	if (cache && fastPath == NULL && --lookupsUntilFastPath == 0)
		fastPath = objc_sparsearray_new(sizeof(uintptr_t));

	if (cache && fastPath != NULL)
		objc_sparsearray_set(fastPath, (uintptr_t)name, class);

	objc_globalMutex_unlock();

	return class;
}

static void
callSelector(Class class, SEL selector)
{
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

	callSelector(class, loadSel);

	class->info |= OBJC_CLASS_INFO_LOADED;
}

void
objc_update_dtable(Class class)
{
	struct objc_category **categories;

	if (!(class->info & OBJC_CLASS_INFO_DTABLE))
		return;

	if (class->DTable == emptyDTable)
		class->DTable = objc_dtable_new();

	if (class->superclass != Nil)
		objc_dtable_copy(class->DTable, class->superclass->DTable);

	for (struct objc_method_list *methodList = class->methodList;
	    methodList != NULL; methodList = methodList->next)
		for (unsigned int i = 0; i < methodList->count; i++)
			objc_dtable_set(class->DTable,
			    (uint32_t)methodList->methods[i].selector.UID,
			    methodList->methods[i].implementation);

	if ((categories = objc_categories_for_class(class)) != NULL) {
		for (unsigned int i = 0; categories[i] != NULL; i++) {
			struct objc_method_list *methodList =
			    (class->info & OBJC_CLASS_INFO_CLASS
			    ? categories[i]->instanceMethods
			    : categories[i]->classMethods);

			for (; methodList != NULL;
			    methodList = methodList->next)
				for (unsigned int j = 0;
				    j < methodList->count; j++)
					objc_dtable_set(class->DTable,
					    (uint32_t)methodList->methods[j]
					    .selector.UID,
					    methodList->methods[j]
					    .implementation);
		}
	}

	if (class->subclassList != NULL)
		for (Class *iter = class->subclassList; *iter != NULL; iter++)
			objc_update_dtable(*iter);
}

static void
addSubclass(Class class)
{
	size_t i;








|






|
|


|




|



|










|









|







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

	callSelector(class, loadSel);

	class->info |= OBJC_CLASS_INFO_LOADED;
}

void
objc_updateDTable(Class class)
{
	struct objc_category **categories;

	if (!(class->info & OBJC_CLASS_INFO_DTABLE))
		return;

	if (class->dTable == emptyDTable)
		class->dTable = objc_dtable_new();

	if (class->superclass != Nil)
		objc_dtable_copy(class->dTable, class->superclass->dTable);

	for (struct objc_method_list *methodList = class->methodList;
	    methodList != NULL; methodList = methodList->next)
		for (unsigned int i = 0; i < methodList->count; i++)
			objc_dtable_set(class->dTable,
			    (uint32_t)methodList->methods[i].selector.UID,
			    methodList->methods[i].implementation);

	if ((categories = objc_categoriesForClass(class)) != NULL) {
		for (unsigned int i = 0; categories[i] != NULL; i++) {
			struct objc_method_list *methodList =
			    (class->info & OBJC_CLASS_INFO_CLASS
			    ? categories[i]->instanceMethods
			    : categories[i]->classMethods);

			for (; methodList != NULL;
			    methodList = methodList->next)
				for (unsigned int j = 0;
				    j < methodList->count; j++)
					objc_dtable_set(class->dTable,
					    (uint32_t)methodList->methods[j]
					    .selector.UID,
					    methodList->methods[j]
					    .implementation);
		}
	}

	if (class->subclassList != NULL)
		for (Class *iter = class->subclassList; *iter != NULL; iter++)
			objc_updateDTable(*iter);
}

static void
addSubclass(Class class)
{
	size_t i;

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
		}
	} else
		for (unsigned int i = 0; i < class->ivars->count; i++)
			*class->ivarOffsets[i] = class->ivars->ivars[i].offset;
}

static void
setupClass(Class class)
{
	const char *superclassName;

	if (class->info & OBJC_CLASS_INFO_SETUP)
		return;

	superclassName = (const char *)class->superclass;
	if (superclassName != NULL) {
		Class super = objc_classname_to_class(superclassName, false);
		Class rootClass;

		if (super == Nil)
			return;

		setupClass(super);

		if (!(super->info & OBJC_CLASS_INFO_SETUP))
			return;

		/*
		 * GCC sets class->isa->isa to the name of the root class,
		 * while Clang just sets it to Nil. Therefore always calculate







|








|





|







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
		}
	} else
		for (unsigned int i = 0; i < class->ivars->count; i++)
			*class->ivarOffsets[i] = class->ivars->ivars[i].offset;
}

static void
setUpClass(Class class)
{
	const char *superclassName;

	if (class->info & OBJC_CLASS_INFO_SETUP)
		return;

	superclassName = (const char *)class->superclass;
	if (superclassName != NULL) {
		Class super = objc_classnameToClass(superclassName, false);
		Class rootClass;

		if (super == Nil)
			return;

		setUpClass(super);

		if (!(super->info & OBJC_CLASS_INFO_SETUP))
			return;

		/*
		 * GCC sets class->isa->isa to the name of the root class,
		 * while Clang just sets it to Nil. Therefore always calculate
339
340
341
342
343
344
345







346
347
348
349
350
351
352
353
354
355
356
357

	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;

	if (class->superclass)
		initializeClass(class->superclass);








	class->info |= OBJC_CLASS_INFO_DTABLE;
	class->isa->info |= OBJC_CLASS_INFO_DTABLE;

	objc_update_dtable(class);
	objc_update_dtable(class->isa);

	/*
	 * Set it first to prevent calling it recursively due to message sends
	 * in the initialize method
	 */
	class->info |= OBJC_CLASS_INFO_INITIALIZED;
	class->isa->info |= OBJC_CLASS_INFO_INITIALIZED;







>
>
>
>
>
>
>



|
|







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

	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;

	if (class->superclass)
		initializeClass(class->superclass);

	/*
	 * Avoid double-initialization: One of the superclasses' +[initialize]
	 * might have called this class and hence it already got initialized.
	 */
	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;

	class->info |= OBJC_CLASS_INFO_DTABLE;
	class->isa->info |= OBJC_CLASS_INFO_DTABLE;

	objc_updateDTable(class);
	objc_updateDTable(class->isa);

	/*
	 * Set it first to prevent calling it recursively due to message sends
	 * in the initialize method
	 */
	class->info |= OBJC_CLASS_INFO_INITIALIZED;
	class->isa->info |= OBJC_CLASS_INFO_INITIALIZED;
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
		    objc_msg_lookup(class, initializeSel);

		initialize(class, initializeSel);
	}
}

void
objc_initialize_class(Class class)
{
	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;

	objc_global_mutex_lock();

	/*
	 * It's possible that two threads try to initialize a class at the same
	 * time. Make sure that the thread which held the lock did not already
	 * initialize it.
	 */
	if (class->info & OBJC_CLASS_INFO_INITIALIZED) {
		objc_global_mutex_unlock();
		return;
	}

	setupClass(class);

	if (!(class->info & OBJC_CLASS_INFO_SETUP)) {
		objc_global_mutex_unlock();
		return;
	}

	initializeClass(class);

	objc_global_mutex_unlock();
}

static void
processLoadQueue()
{
	for (size_t i = 0; i < loadQueueCount; i++) {
		setupClass(loadQueue[i]);

		if (loadQueue[i]->info & OBJC_CLASS_INFO_SETUP) {
			callLoad(loadQueue[i]);

			loadQueueCount--;

			if (loadQueueCount == 0) {







|




|







|



|


|





|






|







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
		    objc_msg_lookup(class, initializeSel);

		initialize(class, initializeSel);
	}
}

void
objc_initializeClass(Class class)
{
	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;

	objc_globalMutex_lock();

	/*
	 * It's possible that two threads try to initialize a class at the same
	 * time. Make sure that the thread which held the lock did not already
	 * initialize it.
	 */
	if (class->info & OBJC_CLASS_INFO_INITIALIZED) {
		objc_globalMutex_unlock();
		return;
	}

	setUpClass(class);

	if (!(class->info & OBJC_CLASS_INFO_SETUP)) {
		objc_globalMutex_unlock();
		return;
	}

	initializeClass(class);

	objc_globalMutex_unlock();
}

static void
processLoadQueue()
{
	for (size_t i = 0; i < loadQueueCount; i++) {
		setUpClass(loadQueue[i]);

		if (loadQueue[i]->info & OBJC_CLASS_INFO_SETUP) {
			callLoad(loadQueue[i]);

			loadQueueCount--;

			if (loadQueueCount == 0) {
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
			if (loadQueue == NULL)
				OBJC_ERROR("Not enough memory for load queue!");
		}
	}
}

void
objc_register_all_classes(struct objc_symtab *symtab)
{
	for (uint16_t i = 0; i < symtab->classDefsCount; i++) {
		Class class = (Class)symtab->defs[i];

		registerClass(class);
		registerSelectors(class);
		registerSelectors(class->isa);
	}

	for (uint16_t i = 0; i < symtab->classDefsCount; i++) {
		Class class = (Class)symtab->defs[i];

		if (hasLoad(class)) {
			setupClass(class);

			if (class->info & OBJC_CLASS_INFO_SETUP)
				callLoad(class);
			else {
				loadQueue = realloc(loadQueue,
				    sizeof(Class) * (loadQueueCount + 1));








|













|







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
			if (loadQueue == NULL)
				OBJC_ERROR("Not enough memory for load queue!");
		}
	}
}

void
objc_registerAllClasses(struct objc_symtab *symtab)
{
	for (uint16_t i = 0; i < symtab->classDefsCount; i++) {
		Class class = (Class)symtab->defs[i];

		registerClass(class);
		registerSelectors(class);
		registerSelectors(class->isa);
	}

	for (uint16_t i = 0; i < symtab->classDefsCount; i++) {
		Class class = (Class)symtab->defs[i];

		if (hasLoad(class)) {
			setUpClass(class);

			if (class->info & OBJC_CLASS_INFO_SETUP)
				callLoad(class);
			else {
				loadQueue = realloc(loadQueue,
				    sizeof(Class) * (loadQueueCount + 1));

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

Class
objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
{
	struct objc_class *class, *metaclass;
	Class iter, rootclass = Nil;

	if (extraBytes > LONG_MAX)
		OBJC_ERROR("extraBytes out of range!")

	if ((class = calloc(1, sizeof(*class))) == NULL ||
	    (metaclass = calloc(1, sizeof(*class))) == NULL)
		OBJC_ERROR("Not enough memory to allocate class pair for class "
		    "%s!", name)

	class->isa = metaclass;
	class->superclass = superclass;
	class->name = name;
	class->info = OBJC_CLASS_INFO_CLASS;
	class->instanceSize = (superclass != Nil ?
	    superclass->instanceSize : 0) + (long)extraBytes;







	for (iter = superclass; iter != Nil; iter = iter->superclass)
		rootclass = iter;

	metaclass->isa = (rootclass != Nil ? rootclass->isa : class);
	metaclass->superclass = (superclass != Nil ? superclass->isa : Nil);
	metaclass->name = name;
	metaclass->info = OBJC_CLASS_INFO_CLASS;
	metaclass->instanceSize = (superclass != Nil ?
	    superclass->isa->instanceSize : 0) + (long)extraBytes;

	return class;
}

void
objc_registerClassPair(Class class)
{
	objc_global_mutex_lock();

	registerClass(class);

	if (class->superclass != Nil) {
		addSubclass(class);
		addSubclass(class->isa);
	}

	class->info |= OBJC_CLASS_INFO_SETUP;
	class->isa->info |= OBJC_CLASS_INFO_SETUP;

	if (hasLoad(class))
		callLoad(class);
	else
		class->info |= OBJC_CLASS_INFO_LOADED;

	processLoadQueue();

	objc_global_mutex_unlock();
}

Class
objc_lookUpClass(const char *name)
{
	Class class;

	if ((class = objc_classname_to_class(name, true)) == NULL)
		return Nil;

	if (class->info & OBJC_CLASS_INFO_SETUP)
		return class;

	objc_global_mutex_lock();

	setupClass(class);

	objc_global_mutex_unlock();

	if (!(class->info & OBJC_CLASS_INFO_SETUP))
		return Nil;

	return class;
}








<
<
<



|






|
>
>
>
>
>
>

















|


















|







|





|

|

|







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

Class
objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
{
	struct objc_class *class, *metaclass;
	Class iter, rootclass = Nil;




	if ((class = calloc(1, sizeof(*class))) == NULL ||
	    (metaclass = calloc(1, sizeof(*class))) == NULL)
		OBJC_ERROR("Not enough memory to allocate class pair for class "
		    "%s!", name);

	class->isa = metaclass;
	class->superclass = superclass;
	class->name = name;
	class->info = OBJC_CLASS_INFO_CLASS;
	class->instanceSize = (superclass != Nil ?
	    superclass->instanceSize : 0);

	if (extraBytes > LONG_MAX ||
	    LONG_MAX - class->instanceSize < (long)extraBytes)
		OBJC_ERROR("extraBytes too large!");

	class->instanceSize += (long)extraBytes;

	for (iter = superclass; iter != Nil; iter = iter->superclass)
		rootclass = iter;

	metaclass->isa = (rootclass != Nil ? rootclass->isa : class);
	metaclass->superclass = (superclass != Nil ? superclass->isa : Nil);
	metaclass->name = name;
	metaclass->info = OBJC_CLASS_INFO_CLASS;
	metaclass->instanceSize = (superclass != Nil ?
	    superclass->isa->instanceSize : 0) + (long)extraBytes;

	return class;
}

void
objc_registerClassPair(Class class)
{
	objc_globalMutex_lock();

	registerClass(class);

	if (class->superclass != Nil) {
		addSubclass(class);
		addSubclass(class->isa);
	}

	class->info |= OBJC_CLASS_INFO_SETUP;
	class->isa->info |= OBJC_CLASS_INFO_SETUP;

	if (hasLoad(class))
		callLoad(class);
	else
		class->info |= OBJC_CLASS_INFO_LOADED;

	processLoadQueue();

	objc_globalMutex_unlock();
}

Class
objc_lookUpClass(const char *name)
{
	Class class;

	if ((class = objc_classnameToClass(name, true)) == NULL)
		return Nil;

	if (class->info & OBJC_CLASS_INFO_SETUP)
		return class;

	objc_globalMutex_lock();

	setUpClass(class);

	objc_globalMutex_unlock();

	if (!(class->info & OBJC_CLASS_INFO_SETUP))
		return Nil;

	return class;
}

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
	return objc_getRequiredClass(name);
}

unsigned int
objc_getClassList(Class *buffer, unsigned int count)
{
	unsigned int j;
	objc_global_mutex_lock();

	if (buffer == NULL)
		return classesCount;

	if (classesCount < count)
		count = classesCount;

	j = 0;
	for (uint32_t i = 0; i < classes->size; i++) {
		void *class;

		if (j >= count) {
			objc_global_mutex_unlock();
			return j;
		}

		if (classes->data[i] == NULL)
			continue;

		if (strcmp(classes->data[i]->key, "Protocol") == 0)
			continue;

		class = (Class)classes->data[i]->object;

		if (class == Nil || (uintptr_t)class & 1)
			continue;

		buffer[j++] = class;
	}

	objc_global_mutex_unlock();

	return j;
}

Class *
objc_copyClassList(unsigned int *length)
{
	Class *ret;
	unsigned int count;

	objc_global_mutex_lock();

	if ((ret = malloc((classesCount + 1) * sizeof(Class))) == NULL)
		OBJC_ERROR("Failed to allocate memory for class list!");

	count = objc_getClassList(ret, classesCount);
	OF_ENSURE(count == classesCount);


	ret[count] = Nil;

	if (length != NULL)
		*length = count;

	objc_global_mutex_unlock();

	return ret;
}

bool
class_isMetaClass(Class class)
{







|












|

















|










|





|
>






|







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
	return objc_getRequiredClass(name);
}

unsigned int
objc_getClassList(Class *buffer, unsigned int count)
{
	unsigned int j;
	objc_globalMutex_lock();

	if (buffer == NULL)
		return classesCount;

	if (classesCount < count)
		count = classesCount;

	j = 0;
	for (uint32_t i = 0; i < classes->size; i++) {
		void *class;

		if (j >= count) {
			objc_globalMutex_unlock();
			return j;
		}

		if (classes->data[i] == NULL)
			continue;

		if (strcmp(classes->data[i]->key, "Protocol") == 0)
			continue;

		class = (Class)classes->data[i]->object;

		if (class == Nil || (uintptr_t)class & 1)
			continue;

		buffer[j++] = class;
	}

	objc_globalMutex_unlock();

	return j;
}

Class *
objc_copyClassList(unsigned int *length)
{
	Class *ret;
	unsigned int count;

	objc_globalMutex_lock();

	if ((ret = malloc((classesCount + 1) * sizeof(Class))) == NULL)
		OBJC_ERROR("Failed to allocate memory for class list!");

	count = objc_getClassList(ret, classesCount);
	if (count != classesCount)
		OBJC_ERROR("Fatal internal inconsistency!");

	ret[count] = Nil;

	if (length != NULL)
		*length = count;

	objc_globalMutex_unlock();

	return ret;
}

bool
class_isMetaClass(Class class)
{
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
}

static struct objc_method *
getMethod(Class class, SEL selector)
{
	struct objc_category **categories;

	if ((categories = objc_categories_for_class(class)) != NULL) {
		for (; *categories != NULL; categories++) {
			struct objc_method_list *methodList =
			    (class->info & OBJC_CLASS_INFO_METACLASS
			    ? (*categories)->classMethods
			    : (*categories)->instanceMethods);

			for (; methodList != NULL;







|







726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
}

static struct objc_method *
getMethod(Class class, SEL selector)
{
	struct objc_category **categories;

	if ((categories = objc_categoriesForClass(class)) != NULL) {
		for (; *categories != NULL; categories++) {
			struct objc_method_list *methodList =
			    (class->info & OBJC_CLASS_INFO_METACLASS
			    ? (*categories)->classMethods
			    : (*categories)->instanceMethods);

			for (; methodList != NULL;
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

static void
addMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
{
	struct objc_method_list *methodList;

	/* FIXME: We need a way to free this at objc_exit() */
	if ((methodList = malloc(sizeof(*methodList))) == NULL)
		OBJC_ERROR("Not enough memory to replace method!");

	methodList->next = class->methodList;
	methodList->count = 1;
	methodList->methods[0].selector.UID = selector->UID;
	methodList->methods[0].selector.typeEncoding = typeEncoding;
	methodList->methods[0].implementation = implementation;

	class->methodList = methodList;

	objc_update_dtable(class);
}

Method
class_getInstanceMethod(Class class, SEL selector)
{
	Method method;
	Class superclass;

	if (class == Nil)
		return NULL;

	objc_global_mutex_lock();

	if ((method = getMethod(class, selector)) != NULL) {
		objc_global_mutex_unlock();
		return method;
	}

	superclass = class->superclass;

	objc_global_mutex_unlock();

	if (superclass != Nil)
		return class_getInstanceMethod(superclass, selector);

	return NULL;
}

bool
class_addMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
{
	bool ret;

	objc_global_mutex_lock();

	if (getMethod(class, selector) == NULL) {
		addMethod(class, selector, implementation, typeEncoding);
		ret = true;
	} else
		ret = false;

	objc_global_mutex_unlock();

	return ret;
}

IMP
class_replaceMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
{
	struct objc_method *method;
	IMP oldImplementation;

	objc_global_mutex_lock();

	if ((method = getMethod(class, selector)) != NULL) {
		oldImplementation = method->implementation;
		method->implementation = implementation;
		objc_update_dtable(class);
	} else {
		oldImplementation = NULL;
		addMethod(class, selector, implementation, typeEncoding);
	}

	objc_global_mutex_unlock();

	return oldImplementation;
}

Class
object_getClass(id object_)
{







|











|











|


|





|













|







|











|




|





|







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

static void
addMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
{
	struct objc_method_list *methodList;

	/* FIXME: We need a way to free this at objc_deinit() */
	if ((methodList = malloc(sizeof(*methodList))) == NULL)
		OBJC_ERROR("Not enough memory to replace method!");

	methodList->next = class->methodList;
	methodList->count = 1;
	methodList->methods[0].selector.UID = selector->UID;
	methodList->methods[0].selector.typeEncoding = typeEncoding;
	methodList->methods[0].implementation = implementation;

	class->methodList = methodList;

	objc_updateDTable(class);
}

Method
class_getInstanceMethod(Class class, SEL selector)
{
	Method method;
	Class superclass;

	if (class == Nil)
		return NULL;

	objc_globalMutex_lock();

	if ((method = getMethod(class, selector)) != NULL) {
		objc_globalMutex_unlock();
		return method;
	}

	superclass = class->superclass;

	objc_globalMutex_unlock();

	if (superclass != Nil)
		return class_getInstanceMethod(superclass, selector);

	return NULL;
}

bool
class_addMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
{
	bool ret;

	objc_globalMutex_lock();

	if (getMethod(class, selector) == NULL) {
		addMethod(class, selector, implementation, typeEncoding);
		ret = true;
	} else
		ret = false;

	objc_globalMutex_unlock();

	return ret;
}

IMP
class_replaceMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
{
	struct objc_method *method;
	IMP oldImplementation;

	objc_globalMutex_lock();

	if ((method = getMethod(class, selector)) != NULL) {
		oldImplementation = method->implementation;
		method->implementation = implementation;
		objc_updateDTable(class);
	} else {
		oldImplementation = NULL;
		addMethod(class, selector, implementation, typeEncoding);
	}

	objc_globalMutex_unlock();

	return oldImplementation;
}

Class
object_getClass(id object_)
{
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
	}

	if (class->subclassList != NULL) {
		free(class->subclassList);
		class->subclassList = NULL;
	}

	if (class->DTable != NULL && class->DTable != emptyDTable)
		objc_dtable_free(class->DTable);

	class->DTable = NULL;

	if ((class->info & OBJC_CLASS_INFO_SETUP) && class->superclass != Nil)
		class->superclass = (Class)class->superclass->name;

	class->info &= ~OBJC_CLASS_INFO_SETUP;
}

void
objc_unregister_class(Class class)
{
	static SEL unloadSel = NULL;



	if (unloadSel == NULL)
		unloadSel = sel_registerName("unload");

	while (class->subclassList != NULL && class->subclassList[0] != Nil)
		objc_unregister_class(class->subclassList[0]);

	if (class->info & OBJC_CLASS_INFO_LOADED)
		callSelector(class, unloadSel);

	objc_hashtable_delete(classes, class->name);

	if (strcmp(class_getName(class), "Protocol") != 0)
		classesCount--;

	unregisterClass(class);
	unregisterClass(class->isa);


}

void
objc_unregister_all_classes(void)
{
	if (classes == NULL)
		return;

	for (uint32_t i = 0; i < classes->size; i++) {
		if (classes->data[i] != NULL &&
		    classes->data[i] != &objc_deleted_bucket) {
			void *class = (Class)classes->data[i]->object;

			if (class == Nil || (uintptr_t)class & 1)
				continue;

			objc_unregister_class(class);

			/*
			 * The table might have been resized, so go back to the
			 * start again.
			 *
			 * Due to the i++ in the for loop, we need to set it to
			 * UINT32_MAX so that it will get increased at the end
			 * of the loop and thus become 0.
			 */
			i = UINT32_MAX;
		}
	}

	OF_ENSURE(classesCount == 0);


	if (emptyDTable != NULL) {
		objc_dtable_free(emptyDTable);
		emptyDTable = NULL;
	}

	objc_sparsearray_free(fastPath);
	fastPath = NULL;

	objc_hashtable_free(classes);
	classes = NULL;
}







|
|

|








|


>
>





|











>
>



|






|





|













|
>












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
	}

	if (class->subclassList != NULL) {
		free(class->subclassList);
		class->subclassList = NULL;
	}

	if (class->dTable != NULL && class->dTable != emptyDTable)
		objc_dtable_free(class->dTable);

	class->dTable = NULL;

	if ((class->info & OBJC_CLASS_INFO_SETUP) && class->superclass != Nil)
		class->superclass = (Class)class->superclass->name;

	class->info &= ~OBJC_CLASS_INFO_SETUP;
}

void
objc_unregisterClass(Class class)
{
	static SEL unloadSel = NULL;

	objc_globalMutex_lock();

	if (unloadSel == NULL)
		unloadSel = sel_registerName("unload");

	while (class->subclassList != NULL && class->subclassList[0] != Nil)
		objc_unregisterClass(class->subclassList[0]);

	if (class->info & OBJC_CLASS_INFO_LOADED)
		callSelector(class, unloadSel);

	objc_hashtable_delete(classes, class->name);

	if (strcmp(class_getName(class), "Protocol") != 0)
		classesCount--;

	unregisterClass(class);
	unregisterClass(class->isa);

	objc_globalMutex_unlock();
}

void
objc_unregisterAllClasses(void)
{
	if (classes == NULL)
		return;

	for (uint32_t i = 0; i < classes->size; i++) {
		if (classes->data[i] != NULL &&
		    classes->data[i] != &objc_deletedBucket) {
			void *class = (Class)classes->data[i]->object;

			if (class == Nil || (uintptr_t)class & 1)
				continue;

			objc_unregisterClass(class);

			/*
			 * The table might have been resized, so go back to the
			 * start again.
			 *
			 * Due to the i++ in the for loop, we need to set it to
			 * UINT32_MAX so that it will get increased at the end
			 * of the loop and thus become 0.
			 */
			i = UINT32_MAX;
		}
	}

	if (classesCount != 0)
		OBJC_ERROR("Fatal internal inconsistency!");

	if (emptyDTable != NULL) {
		objc_dtable_free(emptyDTable);
		emptyDTable = NULL;
	}

	objc_sparsearray_free(fastPath);
	fastPath = NULL;

	objc_hashtable_free(classes);
	classes = NULL;
}

Modified src/runtime/dtable.m from [6362d17715] to [f0602aa5e2].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
static struct objc_dtable_level3 *emptyLevel3 = NULL;
#endif

static void
init(void)
{
	if ((emptyLevel2 = malloc(sizeof(*emptyLevel2))) == NULL)
		OBJC_ERROR("Not enough memory to allocate dtable!");

#ifdef OF_SELUID24
	if ((emptyLevel3 = malloc(sizeof(*emptyLevel3))) == NULL)
		OBJC_ERROR("Not enough memory to allocate dtable!");
#endif

#ifdef OF_SELUID24
	for (uint_fast16_t i = 0; i < 256; i++) {
		emptyLevel2->buckets[i] = emptyLevel3;
		emptyLevel3->buckets[i] = (IMP)0;
	}
#else
	for (uint_fast16_t i = 0; i < 256; i++)
		emptyLevel2->buckets[i] = (IMP)0;
#endif
}

struct objc_dtable *
objc_dtable_new(void)
{
	struct objc_dtable *DTable;

#ifdef OF_SELUID24
	if (emptyLevel2 == NULL || emptyLevel3 == NULL)
		init();
#else
	if (emptyLevel2 == NULL)
		init();
#endif

	if ((DTable = malloc(sizeof(*DTable))) == NULL)
		OBJC_ERROR("Not enough memory to allocate dtable!");

	for (uint_fast16_t i = 0; i < 256; i++)
		DTable->buckets[i] = emptyLevel2;

	return DTable;
}

void
objc_dtable_copy(struct objc_dtable *dest, struct objc_dtable *src)
{
	for (uint_fast16_t i = 0; i < 256; i++) {
		if (src->buckets[i] == emptyLevel2)







|



|
















|









|
|


|

|







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
static struct objc_dtable_level3 *emptyLevel3 = NULL;
#endif

static void
init(void)
{
	if ((emptyLevel2 = malloc(sizeof(*emptyLevel2))) == NULL)
		OBJC_ERROR("Not enough memory to allocate dispatch table!");

#ifdef OF_SELUID24
	if ((emptyLevel3 = malloc(sizeof(*emptyLevel3))) == NULL)
		OBJC_ERROR("Not enough memory to allocate dispatch table!");
#endif

#ifdef OF_SELUID24
	for (uint_fast16_t i = 0; i < 256; i++) {
		emptyLevel2->buckets[i] = emptyLevel3;
		emptyLevel3->buckets[i] = (IMP)0;
	}
#else
	for (uint_fast16_t i = 0; i < 256; i++)
		emptyLevel2->buckets[i] = (IMP)0;
#endif
}

struct objc_dtable *
objc_dtable_new(void)
{
	struct objc_dtable *dTable;

#ifdef OF_SELUID24
	if (emptyLevel2 == NULL || emptyLevel3 == NULL)
		init();
#else
	if (emptyLevel2 == NULL)
		init();
#endif

	if ((dTable = malloc(sizeof(*dTable))) == NULL)
		OBJC_ERROR("Not enough memory to allocate dispatch table!");

	for (uint_fast16_t i = 0; i < 256; i++)
		dTable->buckets[i] = emptyLevel2;

	return dTable;
}

void
objc_dtable_copy(struct objc_dtable *dest, struct objc_dtable *src)
{
	for (uint_fast16_t i = 0; i < 256; i++) {
		if (src->buckets[i] == emptyLevel2)
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
			objc_dtable_set(dest, idx, implementation);
		}
#endif
	}
}

void
objc_dtable_set(struct objc_dtable *DTable, uint32_t idx, IMP implementation)
{
#ifdef OF_SELUID24
	uint8_t i = idx >> 16;
	uint8_t j = idx >> 8;
	uint8_t k = idx;
#else
	uint8_t i = idx >> 8;
	uint8_t j = idx;
#endif

	if (DTable->buckets[i] == emptyLevel2) {
		struct objc_dtable_level2 *level2 = malloc(sizeof(*level2));

		if (level2 == NULL)
			OBJC_ERROR("Not enough memory to insert into dtable!");


		for (uint_fast16_t l = 0; l < 256; l++)
#ifdef OF_SELUID24
			level2->buckets[l] = emptyLevel3;
#else
			level2->buckets[l] = (IMP)0;
#endif

		DTable->buckets[i] = level2;
	}

#ifdef OF_SELUID24
	if (DTable->buckets[i]->buckets[j] == emptyLevel3) {
		struct objc_dtable_level3 *level3 = malloc(sizeof(*level3));

		if (level3 == NULL)
			OBJC_ERROR("Not enough memory to insert into dtable!");


		for (uint_fast16_t l = 0; l < 256; l++)
			level3->buckets[l] = (IMP)0;

		DTable->buckets[i]->buckets[j] = level3;
	}

	DTable->buckets[i]->buckets[j]->buckets[k] = implementation;
#else
	DTable->buckets[i]->buckets[j] = implementation;
#endif
}

void
objc_dtable_free(struct objc_dtable *DTable)
{
	for (uint_fast16_t i = 0; i < 256; i++) {
		if (DTable->buckets[i] == emptyLevel2)
			continue;

#ifdef OF_SELUID24
		for (uint_fast16_t j = 0; j < 256; j++)
			if (DTable->buckets[i]->buckets[j] != emptyLevel3)
				free(DTable->buckets[i]->buckets[j]);
#endif

		free(DTable->buckets[i]);
	}

	free(DTable);
}

void
objc_dtable_cleanup(void)
{
	if (emptyLevel2 != NULL)
		free(emptyLevel2);







|










|



|
>








|



|



|
>




|


|

|




|


|




|
|


|


|







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
			objc_dtable_set(dest, idx, implementation);
		}
#endif
	}
}

void
objc_dtable_set(struct objc_dtable *dTable, uint32_t idx, IMP implementation)
{
#ifdef OF_SELUID24
	uint8_t i = idx >> 16;
	uint8_t j = idx >> 8;
	uint8_t k = idx;
#else
	uint8_t i = idx >> 8;
	uint8_t j = idx;
#endif

	if (dTable->buckets[i] == emptyLevel2) {
		struct objc_dtable_level2 *level2 = malloc(sizeof(*level2));

		if (level2 == NULL)
			OBJC_ERROR("Not enough memory to insert into "
			    "dispatch table!");

		for (uint_fast16_t l = 0; l < 256; l++)
#ifdef OF_SELUID24
			level2->buckets[l] = emptyLevel3;
#else
			level2->buckets[l] = (IMP)0;
#endif

		dTable->buckets[i] = level2;
	}

#ifdef OF_SELUID24
	if (dTable->buckets[i]->buckets[j] == emptyLevel3) {
		struct objc_dtable_level3 *level3 = malloc(sizeof(*level3));

		if (level3 == NULL)
			OBJC_ERROR("Not enough memory to insert into "
			    "dispatch table!");

		for (uint_fast16_t l = 0; l < 256; l++)
			level3->buckets[l] = (IMP)0;

		dTable->buckets[i]->buckets[j] = level3;
	}

	dTable->buckets[i]->buckets[j]->buckets[k] = implementation;
#else
	dTable->buckets[i]->buckets[j] = implementation;
#endif
}

void
objc_dtable_free(struct objc_dtable *dTable)
{
	for (uint_fast16_t i = 0; i < 256; i++) {
		if (dTable->buckets[i] == emptyLevel2)
			continue;

#ifdef OF_SELUID24
		for (uint_fast16_t j = 0; j < 256; j++)
			if (dTable->buckets[i]->buckets[j] != emptyLevel3)
				free(dTable->buckets[i]->buckets[j]);
#endif

		free(dTable->buckets[i]);
	}

	free(dTable);
}

void
objc_dtable_cleanup(void)
{
	if (emptyLevel2 != NULL)
		free(emptyLevel2);

Modified src/runtime/exception.m from [ff0a7a161b] to [ba2a43d17f].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <string.h>

#import "ObjFWRT.h"
#import "private.h"

#import "macros.h"
#ifdef OF_HAVE_THREADS
# include "mutex.h"
#endif

#ifdef HAVE_SEH_EXCEPTIONS
# include <windows.h>
#endif

#if defined(HAVE_DWARF_EXCEPTIONS)







|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <string.h>

#import "ObjFWRT.h"
#import "private.h"

#import "macros.h"
#ifdef OF_HAVE_THREADS
# include "OFPlainMutex.h"
#endif

#ifdef HAVE_SEH_EXCEPTIONS
# include <windows.h>
#endif

#if defined(HAVE_DWARF_EXCEPTIONS)
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
# define CALL_PERSONALITY(func) func(state, ex, ctx)
#endif

#define GNUCOBJC_EXCEPTION_CLASS UINT64_C(0x474E55434F424A43) /* GNUCOBJC */
#define GNUCCXX0_EXCEPTION_CLASS UINT64_C(0x474E5543432B2B00) /* GNUCC++\0 */
#define CLNGCXX0_EXCEPTION_CLASS UINT64_C(0x434C4E47432B2B00) /* CLNGC++\0 */

#define NUM_EMERGENCY_EXCEPTIONS 4


#define _UA_SEARCH_PHASE  0x01
#define _UA_CLEANUP_PHASE 0x02
#define _UA_HANDLER_FRAME 0x04
#define _UA_FORCE_UNWIND  0x08



#define DW_EH_PE_absptr	  0x00

#define DW_EH_PE_uleb128  0x01
#define DW_EH_PE_udata2	  0x02
#define DW_EH_PE_udata4	  0x03
#define DW_EH_PE_udata8	  0x04

#define DW_EH_PE_signed	  0x08
#define DW_EH_PE_sleb128  (DW_EH_PE_signed | DW_EH_PE_uleb128)
#define DW_EH_PE_sdata2	  (DW_EH_PE_signed | DW_EH_PE_udata2)
#define DW_EH_PE_sdata4	  (DW_EH_PE_signed | DW_EH_PE_udata4)
#define DW_EH_PE_sdata8	  (DW_EH_PE_signed | DW_EH_PE_udata8)

#define DW_EH_PE_pcrel	  0x10
#define DW_EH_PE_textrel  0x20
#define DW_EH_PE_datarel  0x30
#define DW_EH_PE_funcrel  0x40
#define DW_EH_PE_aligned  0x50

#define DW_EH_PE_indirect 0x80

#define DW_EH_PE_omit	  0xFF



#define CLEANUP_FOUND	  0x01
#define HANDLER_FOUND	  0x02


struct _Unwind_Context;

typedef enum {
	_URC_OK			= 0,
	_URC_FATAL_PHASE1_ERROR	= 3,
	_URC_END_OF_STACK	= 5,







|

>
|
|
|
|
>

>
|

|
|
|
|

|
|
|
|
|

|
|
|
|
|

|

|
>

>
|
|
>







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
# define CALL_PERSONALITY(func) func(state, ex, ctx)
#endif

#define GNUCOBJC_EXCEPTION_CLASS UINT64_C(0x474E55434F424A43) /* GNUCOBJC */
#define GNUCCXX0_EXCEPTION_CLASS UINT64_C(0x474E5543432B2B00) /* GNUCC++\0 */
#define CLNGCXX0_EXCEPTION_CLASS UINT64_C(0x434C4E47432B2B00) /* CLNGC++\0 */

#define numEmergencyExceptions 4

enum {
	_UA_SEARCH_PHASE  = 0x01,
	_UA_CLEANUP_PHASE = 0x02,
	_UA_HANDLER_FRAME = 0x04,
	_UA_FORCE_UNWIND  = 0x08
};

enum {
	DW_EH_PE_absptr	  = 0x00,

	DW_EH_PE_uleb128  = 0x01,
	DW_EH_PE_udata2	  = 0x02,
	DW_EH_PE_udata4	  = 0x03,
	DW_EH_PE_udata8	  = 0x04,

	DW_EH_PE_signed	  = 0x08,
	DW_EH_PE_sleb128  = (DW_EH_PE_signed | DW_EH_PE_uleb128),
	DW_EH_PE_sdata2	  = (DW_EH_PE_signed | DW_EH_PE_udata2),
	DW_EH_PE_sdata4	  = (DW_EH_PE_signed | DW_EH_PE_udata4),
	DW_EH_PE_sdata8	  = (DW_EH_PE_signed | DW_EH_PE_udata8),

	DW_EH_PE_pcrel	  = 0x10,
	DW_EH_PE_textrel  = 0x20,
	DW_EH_PE_datarel  = 0x30,
	DW_EH_PE_funcrel  = 0x40,
	DW_EH_PE_aligned  = 0x50,

	DW_EH_PE_indirect = 0x80,

	DW_EH_PE_omit	  = 0xFF
};

enum {
	CLEANUP_FOUND = 0x01,
	HANDLER_FOUND = 0x02
};

struct _Unwind_Context;

typedef enum {
	_URC_OK			= 0,
	_URC_FATAL_PHASE1_ERROR	= 3,
	_URC_END_OF_STACK	= 5,
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
	id object;
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
	uintptr_t landingpad;
	intptr_t filter;
#endif
};

struct lsda {
	uintptr_t regionStart, landingpadsStart;
	uint8_t typesTableEnc;
	const uint8_t *typesTable;
	uintptr_t typesTableBase;
	uint8_t callsitesEnc;
	const uint8_t *callsites, *actionTable;
};







|







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
	id object;
#ifndef HAVE_ARM_EHABI_EXCEPTIONS
	uintptr_t landingpad;
	intptr_t filter;
#endif
};

struct LSDA {
	uintptr_t regionStart, landingpadsStart;
	uint8_t typesTableEnc;
	const uint8_t *typesTable;
	uintptr_t typesTableBase;
	uint8_t callsitesEnc;
	const uint8_t *callsites, *actionTable;
};
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259

#ifdef HAVE_SEH_EXCEPTIONS
extern EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *,
    PCONTEXT, PDISPATCHER_CONTEXT, _Unwind_Reason_Code (*)(int, int, uint64_t,
    struct _Unwind_Exception *, struct _Unwind_Context *));
#endif

static objc_uncaught_exception_handler_t uncaughtExceptionHandler;
static struct objc_exception emergencyExceptions[NUM_EMERGENCY_EXCEPTIONS];
#ifdef OF_HAVE_THREADS
static of_spinlock_t emergencyExceptionsSpinlock;

OF_CONSTRUCTOR()
{
	if (!of_spinlock_new(&emergencyExceptionsSpinlock))
		OBJC_ERROR("Cannot create spinlock!")
}
#endif

static uint64_t
readULEB128(const uint8_t **ptr)
{
	uint64_t value = 0;







|
|

|



|
|







241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263

#ifdef HAVE_SEH_EXCEPTIONS
extern EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *,
    PCONTEXT, PDISPATCHER_CONTEXT, _Unwind_Reason_Code (*)(int, int, uint64_t,
    struct _Unwind_Exception *, struct _Unwind_Context *));
#endif

static objc_uncaught_exception_handler uncaughtExceptionHandler;
static struct objc_exception emergencyExceptions[numEmergencyExceptions];
#ifdef OF_HAVE_THREADS
static OFSpinlock emergencyExceptionsSpinlock;

OF_CONSTRUCTOR()
{
	if (OFSpinlockNew(&emergencyExceptionsSpinlock) != 0)
		OBJC_ERROR("Failed to create spinlock!");
}
#endif

static uint64_t
readULEB128(const uint8_t **ptr)
{
	uint64_t value = 0;
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
#endif
#ifdef HAVE__UNWIND_GETTEXTRELBASE
	case DW_EH_PE_textrel:
		return _Unwind_GetTextRelBase(ctx);
#endif
	}

	OBJC_ERROR("Unknown encoding!")
}

static size_t
sizeForEncoding(uint8_t enc)
{
	if (enc == DW_EH_PE_omit)
		return 0;

	switch (enc & 0x07) {
	case DW_EH_PE_absptr:
		return sizeof(void *);
	case DW_EH_PE_udata2:
		return 2;
	case DW_EH_PE_udata4:
		return 4;
	case DW_EH_PE_udata8:
		return 8;
	}

	OBJC_ERROR("Unknown encoding!")
}

static uint64_t
readValue(uint8_t enc, const uint8_t **ptr)
{
	uint64_t value;

	if (enc == DW_EH_PE_aligned)


		OBJC_ERROR("DW_EH_PE_aligned is not implemented!")





#define READ(type)					\
	{						\
		type tmp;				\
		memcpy(&tmp, *ptr, sizeof(type));	\
		value = tmp;				\
		*ptr += sizeForEncoding(enc);		\







|



















|







|
>
>
|
>
>
>
>







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
#endif
#ifdef HAVE__UNWIND_GETTEXTRELBASE
	case DW_EH_PE_textrel:
		return _Unwind_GetTextRelBase(ctx);
#endif
	}

	OBJC_ERROR("Unknown encoding!");
}

static size_t
sizeForEncoding(uint8_t enc)
{
	if (enc == DW_EH_PE_omit)
		return 0;

	switch (enc & 0x07) {
	case DW_EH_PE_absptr:
		return sizeof(void *);
	case DW_EH_PE_udata2:
		return 2;
	case DW_EH_PE_udata4:
		return 4;
	case DW_EH_PE_udata8:
		return 8;
	}

	OBJC_ERROR("Unknown encoding!");
}

static uint64_t
readValue(uint8_t enc, const uint8_t **ptr)
{
	uint64_t value;

	if (enc == DW_EH_PE_aligned) {
		const uintptr_t *aligned = (const uintptr_t *)
		    OFRoundUpToPowerOf2(sizeof(void *), (uintptr_t)*ptr);

		*ptr = (const uint8_t *)(aligned + 1);

		return *aligned;
	}

#define READ(type)					\
	{						\
		type tmp;				\
		memcpy(&tmp, *ptr, sizeof(type));	\
		value = tmp;				\
		*ptr += sizeForEncoding(enc);		\
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
	case DW_EH_PE_sdata2:
		READ(int16_t)
	case DW_EH_PE_sdata4:
		READ(int32_t)
	case DW_EH_PE_sdata8:
		READ(int64_t)
	default:
		OBJC_ERROR("Unknown encoding!")
	}
#undef READ

	return value;
}

#ifndef HAVE_ARM_EHABI_EXCEPTIONS
static uint64_t
resolveValue(uint64_t value, uint8_t enc, const uint8_t *start, uint64_t base)
{
	if (value == 0)
		return 0;

	value += ((enc & 0x70) == DW_EH_PE_pcrel ? (uintptr_t)start : base);

	if (enc & DW_EH_PE_indirect)
		value = *(uintptr_t *)(uintptr_t)value;

	return value;
}
#endif

static void
readLSDA(struct _Unwind_Context *ctx, const uint8_t *ptr, struct lsda *LSDA)
{
	uint8_t landingpadsStartEnc;
	uintptr_t callsitesSize;

	LSDA->regionStart = _Unwind_GetRegionStart(ctx);
	LSDA->landingpadsStart = LSDA->regionStart;
	LSDA->typesTable = NULL;







|










|
|











|







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
	case DW_EH_PE_sdata2:
		READ(int16_t)
	case DW_EH_PE_sdata4:
		READ(int32_t)
	case DW_EH_PE_sdata8:
		READ(int64_t)
	default:
		OBJC_ERROR("Unknown encoding!");
	}
#undef READ

	return value;
}

#ifndef HAVE_ARM_EHABI_EXCEPTIONS
static uint64_t
resolveValue(uint64_t value, uint8_t enc, const uint8_t *start, uint64_t base)
{
	if (value == 0 || enc == DW_EH_PE_aligned)
		return value;

	value += ((enc & 0x70) == DW_EH_PE_pcrel ? (uintptr_t)start : base);

	if (enc & DW_EH_PE_indirect)
		value = *(uintptr_t *)(uintptr_t)value;

	return value;
}
#endif

static void
readLSDA(struct _Unwind_Context *ctx, const uint8_t *ptr, struct LSDA *LSDA)
{
	uint8_t landingpadsStartEnc;
	uintptr_t callsitesSize;

	LSDA->regionStart = _Unwind_GetRegionStart(ctx);
	LSDA->landingpadsStart = LSDA->regionStart;
	LSDA->typesTable = NULL;
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
	callsitesSize = (uintptr_t)readULEB128(&ptr);
	LSDA->callsites = ptr;

	LSDA->actionTable = LSDA->callsites + callsitesSize;
}

static bool
findCallsite(struct _Unwind_Context *ctx, struct lsda *LSDA,
    uintptr_t *landingpad, const uint8_t **actionRecords)
{
	uintptr_t IP = _Unwind_GetIP(ctx);
	const uint8_t *ptr = LSDA->callsites;

	*landingpad = 0;
	*actionRecords = NULL;







|







429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
	callsitesSize = (uintptr_t)readULEB128(&ptr);
	LSDA->callsites = ptr;

	LSDA->actionTable = LSDA->callsites + callsitesSize;
}

static bool
findCallsite(struct _Unwind_Context *ctx, struct LSDA *LSDA,
    uintptr_t *landingpad, const uint8_t **actionRecords)
{
	uintptr_t IP = _Unwind_GetIP(ctx);
	const uint8_t *ptr = LSDA->callsites;

	*landingpad = 0;
	*actionRecords = NULL;
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
		if (iter == class)
			return true;

	return false;
}

static uint8_t
findActionRecord(const uint8_t *actionRecords, struct lsda *LSDA, int actions,
    bool foreign, struct objc_exception *e, intptr_t *filterPtr)
{
	const uint8_t *ptr;
	intptr_t filter, displacement;

	do {
		ptr = actionRecords;







|







506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
		if (iter == class)
			return true;

	return false;
}

static uint8_t
findActionRecord(const uint8_t *actionRecords, struct LSDA *LSDA, int actions,
    bool foreign, struct objc_exception *e, intptr_t *filterPtr)
{
	const uint8_t *ptr;
	intptr_t filter, displacement;

	do {
		ptr = actionRecords;
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
			if (classMatches(class, e->object)) {
				*filterPtr = filter;
				return HANDLER_FOUND;
			}
		} else if (filter == 0)
			return CLEANUP_FOUND;
		else if (filter < 0)
			OBJC_ERROR("Invalid filter!")
	} while (displacement != 0);

	return 0;
}

#ifdef HAVE_SEH_EXCEPTIONS
static







|







566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
			if (classMatches(class, e->object)) {
				*filterPtr = filter;
				return HANDLER_FOUND;
			}
		} else if (filter == 0)
			return CLEANUP_FOUND;
		else if (filter < 0)
			OBJC_ERROR("Invalid filter!");
	} while (displacement != 0);

	return 0;
}

#ifdef HAVE_SEH_EXCEPTIONS
static
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
	}

	_Unwind_SetGR(ctx, 12, (uintptr_t)ex);
#endif
	struct objc_exception *e = (struct objc_exception *)ex;
	bool foreign = (exClass != GNUCOBJC_EXCEPTION_CLASS);
	const uint8_t *LSDAAddr, *actionRecords;
	struct lsda LSDA;
	uintptr_t landingpad = 0;
	uint8_t found = 0;
	intptr_t filter = 0;

	if (foreign) {
		switch (exClass) {
#ifdef CXX_PERSONALITY







|







602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
	}

	_Unwind_SetGR(ctx, 12, (uintptr_t)ex);
#endif
	struct objc_exception *e = (struct objc_exception *)ex;
	bool foreign = (exClass != GNUCOBJC_EXCEPTION_CLASS);
	const uint8_t *LSDAAddr, *actionRecords;
	struct LSDA LSDA;
	uintptr_t landingpad = 0;
	uint8_t found = 0;
	intptr_t filter = 0;

	if (foreign) {
		switch (exClass) {
#ifdef CXX_PERSONALITY
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
		    (uintptr_t)ex);
		_Unwind_SetGR(ctx, __builtin_eh_return_data_regno(1), filter);
		_Unwind_SetIP(ctx, landingpad);

		return _URC_INSTALL_CONTEXT;
	}


	OBJC_ERROR("Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE in actions!")
}

static void
cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *ex)
{
	free(ex);
}

static void
emergencyExceptionCleanup(_Unwind_Reason_Code reason,
    struct _Unwind_Exception *ex)
{
#ifdef OF_HAVE_THREADS
	if (!of_spinlock_lock(&emergencyExceptionsSpinlock))
		OBJC_ERROR("Cannot lock spinlock!");
#endif

	ex->class = 0;

#ifdef OF_HAVE_THREADS
	if (!of_spinlock_unlock(&emergencyExceptionsSpinlock))
		OBJC_ERROR("Cannot unlock spinlock!");
#endif
}

void
objc_exception_throw(id object)
{
	struct objc_exception *e = calloc(1, sizeof(*e));
	bool emergency = false;

	if (e == NULL) {
#ifdef OF_HAVE_THREADS
		if (!of_spinlock_lock(&emergencyExceptionsSpinlock))
			OBJC_ERROR("Cannot lock spinlock!");
#endif

		for (uint_fast8_t i = 0; i < NUM_EMERGENCY_EXCEPTIONS; i++) {
			if (emergencyExceptions[i].exception.class == 0) {
				e = &emergencyExceptions[i];
				e->exception.class = GNUCOBJC_EXCEPTION_CLASS;
				emergency = true;

				break;
			}
		}

#ifdef OF_HAVE_THREADS
		if (!of_spinlock_unlock(&emergencyExceptionsSpinlock))
			OBJC_ERROR("Cannot lock spinlock!");
#endif
	}

	if (e == NULL)
		OBJC_ERROR("Not enough memory to allocate exception!")

	e->exception.class = GNUCOBJC_EXCEPTION_CLASS;
	e->exception.cleanup = (emergency
	    ? emergencyExceptionCleanup : cleanup);
	e->object = object;

	_Unwind_RaiseException(&e->exception);

	if (uncaughtExceptionHandler != NULL)
		uncaughtExceptionHandler(object);

	OBJC_ERROR("_Unwind_RaiseException() returned!")
}

objc_uncaught_exception_handler_t
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler_t handler)
{
	objc_uncaught_exception_handler_t old = uncaughtExceptionHandler;
	uncaughtExceptionHandler = handler;

	return old;
}

#ifdef HAVE_SEH_EXCEPTIONS
typedef EXCEPTION_DISPOSITION (*seh_personality_fn)(PEXCEPTION_RECORD, void *,







>
|













|
|





|
|











|
|


|










|
|




|











|


|
|

|







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
		    (uintptr_t)ex);
		_Unwind_SetGR(ctx, __builtin_eh_return_data_regno(1), filter);
		_Unwind_SetIP(ctx, landingpad);

		return _URC_INSTALL_CONTEXT;
	}

	OBJC_ERROR(
	    "Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE in actions!");
}

static void
cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *ex)
{
	free(ex);
}

static void
emergencyExceptionCleanup(_Unwind_Reason_Code reason,
    struct _Unwind_Exception *ex)
{
#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&emergencyExceptionsSpinlock) != 0)
		OBJC_ERROR("Failed to lock spinlock!");
#endif

	ex->class = 0;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockUnlock(&emergencyExceptionsSpinlock) != 0)
		OBJC_ERROR("Failed to unlock spinlock!");
#endif
}

void
objc_exception_throw(id object)
{
	struct objc_exception *e = calloc(1, sizeof(*e));
	bool emergency = false;

	if (e == NULL) {
#ifdef OF_HAVE_THREADS
		if (OFSpinlockLock(&emergencyExceptionsSpinlock) != 0)
			OBJC_ERROR("Failed to lock spinlock!");
#endif

		for (uint_fast8_t i = 0; i < numEmergencyExceptions; i++) {
			if (emergencyExceptions[i].exception.class == 0) {
				e = &emergencyExceptions[i];
				e->exception.class = GNUCOBJC_EXCEPTION_CLASS;
				emergency = true;

				break;
			}
		}

#ifdef OF_HAVE_THREADS
		if (OFSpinlockUnlock(&emergencyExceptionsSpinlock) != 0)
			OBJC_ERROR("Failed to lock spinlock!");
#endif
	}

	if (e == NULL)
		OBJC_ERROR("Not enough memory to allocate exception!");

	e->exception.class = GNUCOBJC_EXCEPTION_CLASS;
	e->exception.cleanup = (emergency
	    ? emergencyExceptionCleanup : cleanup);
	e->object = object;

	_Unwind_RaiseException(&e->exception);

	if (uncaughtExceptionHandler != NULL)
		uncaughtExceptionHandler(object);

	OBJC_ERROR("_Unwind_RaiseException() returned!");
}

objc_uncaught_exception_handler
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler)
{
	objc_uncaught_exception_handler old = uncaughtExceptionHandler;
	uncaughtExceptionHandler = handler;

	return old;
}

#ifdef HAVE_SEH_EXCEPTIONS
typedef EXCEPTION_DISPOSITION (*seh_personality_fn)(PEXCEPTION_RECORD, void *,

Modified src/runtime/hashtable.m from [f92943fe11] to [4617c1e6d6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#import "ObjFWRT.h"
#import "private.h"

struct objc_hashtable_bucket objc_deleted_bucket;

uint32_t
objc_hash_string(const void *str_)
{
	const char *str = str_;
	uint32_t hash = 0;

	while (*str != 0) {
		hash += *str;
		hash += (hash << 10);
		hash ^= (hash >> 6);
		str++;
	}

	hash += (hash << 3);
	hash ^= (hash >> 11);
	hash += (hash << 15);

	return hash;
}

bool
objc_equal_string(const void *ptr1, const void *ptr2)
{
	return (strcmp(ptr1, ptr2) == 0);
}

struct objc_hashtable *
objc_hashtable_new(uint32_t (*hash)(const void *),
    bool (*equal)(const void *, const void *), uint32_t size)







|


|



















|







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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#import "ObjFWRT.h"
#import "private.h"

struct objc_hashtable_bucket objc_deletedBucket;

uint32_t
objc_string_hash(const void *str_)
{
	const char *str = str_;
	uint32_t hash = 0;

	while (*str != 0) {
		hash += *str;
		hash += (hash << 10);
		hash ^= (hash >> 6);
		str++;
	}

	hash += (hash << 3);
	hash ^= (hash >> 11);
	hash += (hash << 15);

	return hash;
}

bool
objc_string_equal(const void *ptr1, const void *ptr2)
{
	return (strcmp(ptr1, ptr2) == 0);
}

struct objc_hashtable *
objc_hashtable_new(uint32_t (*hash)(const void *),
    bool (*equal)(const void *, const void *), uint32_t size)
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
		return;

	if ((newData = calloc(newSize, sizeof(*newData))) == NULL)
		OBJC_ERROR("Not enough memory to resize hash table!");

	for (uint32_t i = 0; i < table->size; i++) {
		if (table->data[i] != NULL &&
		    table->data[i] != &objc_deleted_bucket) {
			uint32_t j, last;

			last = newSize;

			for (j = table->data[i]->hash & (newSize - 1);
			    j < last && newData[j] != NULL; j++);

			if (j >= last) {
				last = table->data[i]->hash & (newSize - 1);

				for (j = 0; j < last && newData[j] != NULL;
				    j++);
			}

			if (j >= last)
				OBJC_ERROR("No free bucket!");

			newData[j] = table->data[i];
		}
	}

	free(table->data);
	table->data = newData;
	table->size = newSize;
}

static inline bool
indexForKey(struct objc_hashtable *table, const void *key, uint32_t *idx)
{
	uint32_t i, hash;

	hash = table->hash(key) & (table->size - 1);

	for (i = hash; i < table->size && table->data[i] != NULL; i++) {
		if (table->data[i] == &objc_deleted_bucket)
			continue;

		if (table->equal(table->data[i]->key, key)) {
			*idx = i;
			return true;
		}
	}

	if (i < table->size)
		return false;

	for (i = 0; i < hash && table->data[i] != NULL; i++) {
		if (table->data[i] == &objc_deleted_bucket)
			continue;

		if (table->equal(table->data[i]->key, key)) {
			*idx = i;
			return true;
		}
	}







|















|


















|












|







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
		return;

	if ((newData = calloc(newSize, sizeof(*newData))) == NULL)
		OBJC_ERROR("Not enough memory to resize hash table!");

	for (uint32_t i = 0; i < table->size; i++) {
		if (table->data[i] != NULL &&
		    table->data[i] != &objc_deletedBucket) {
			uint32_t j, last;

			last = newSize;

			for (j = table->data[i]->hash & (newSize - 1);
			    j < last && newData[j] != NULL; j++);

			if (j >= last) {
				last = table->data[i]->hash & (newSize - 1);

				for (j = 0; j < last && newData[j] != NULL;
				    j++);
			}

			if (j >= last)
				OBJC_ERROR("No free bucket in hash table!");

			newData[j] = table->data[i];
		}
	}

	free(table->data);
	table->data = newData;
	table->size = newSize;
}

static inline bool
indexForKey(struct objc_hashtable *table, const void *key, uint32_t *idx)
{
	uint32_t i, hash;

	hash = table->hash(key) & (table->size - 1);

	for (i = hash; i < table->size && table->data[i] != NULL; i++) {
		if (table->data[i] == &objc_deletedBucket)
			continue;

		if (table->equal(table->data[i]->key, key)) {
			*idx = i;
			return true;
		}
	}

	if (i < table->size)
		return false;

	for (i = 0; i < hash && table->data[i] != NULL; i++) {
		if (table->data[i] == &objc_deletedBucket)
			continue;

		if (table->equal(table->data[i]->key, key)) {
			*idx = i;
			return true;
		}
	}
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

	resize(table, table->count + 1);

	hash = table->hash(key);
	last = table->size;

	for (i = hash & (table->size - 1); i < last && table->data[i] != NULL &&
	    table->data[i] != &objc_deleted_bucket; i++);

	if (i >= last) {
		last = hash & (table->size - 1);

		for (i = 0; i < last && table->data[i] != NULL &&
		    table->data[i] != &objc_deleted_bucket; i++);
	}

	if (i >= last)
		OBJC_ERROR("No free bucket!");

	if ((bucket = malloc(sizeof(*bucket))) == NULL)
		OBJC_ERROR("Not enough memory to allocate hash table bucket!");

	bucket->key = key;
	bucket->hash = hash;
	bucket->object = object;







|





|



|







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

	resize(table, table->count + 1);

	hash = table->hash(key);
	last = table->size;

	for (i = hash & (table->size - 1); i < last && table->data[i] != NULL &&
	    table->data[i] != &objc_deletedBucket; i++);

	if (i >= last) {
		last = hash & (table->size - 1);

		for (i = 0; i < last && table->data[i] != NULL &&
		    table->data[i] != &objc_deletedBucket; i++);
	}

	if (i >= last)
		OBJC_ERROR("No free bucket in hash table!");

	if ((bucket = malloc(sizeof(*bucket))) == NULL)
		OBJC_ERROR("Not enough memory to allocate hash table bucket!");

	bucket->key = key;
	bucket->hash = hash;
	bucket->object = object;
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
{
	uint32_t idx;

	if (!indexForKey(table, key, &idx))
		return;

	free(table->data[idx]);
	table->data[idx] = &objc_deleted_bucket;

	table->count--;
	resize(table, table->count);
}

void
objc_hashtable_free(struct objc_hashtable *table)
{
	for (uint32_t i = 0; i < table->size; i++)
		if (table->data[i] != NULL &&
		    table->data[i] != &objc_deleted_bucket)
			free(table->data[i]);

	free(table->data);
	free(table);
}







|










|





220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
{
	uint32_t idx;

	if (!indexForKey(table, key, &idx))
		return;

	free(table->data[idx]);
	table->data[idx] = &objc_deletedBucket;

	table->count--;
	resize(table, table->count);
}

void
objc_hashtable_free(struct objc_hashtable *table)
{
	for (uint32_t i = 0; i < table->size; i++)
		if (table->data[i] != NULL &&
		    table->data[i] != &objc_deletedBucket)
			free(table->data[i]);

	free(table->data);
	free(table);
}

Modified src/runtime/init.m from [89cae05911] to [bad26900aa].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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

#import "ObjFWRT.h"
#import "private.h"

void
__objc_exec_class(struct objc_module *module)
{
	objc_global_mutex_lock();

	objc_register_all_selectors(module->symtab);
	objc_register_all_classes(module->symtab);
	objc_register_all_categories(module->symtab);
	objc_init_static_instances(module->symtab);

	objc_global_mutex_unlock();
}

void
objc_exit(void)
{
	objc_global_mutex_lock();

	objc_unregister_all_categories();
	objc_unregister_all_classes();
	objc_unregister_all_selectors();
	objc_forget_pending_static_instances();
	objc_dtable_cleanup();

	objc_global_mutex_unlock();
}







|

|
|
|
|

|



|

|

|
|
|
|


|

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

#import "ObjFWRT.h"
#import "private.h"

void
__objc_exec_class(struct objc_module *module)
{
	objc_globalMutex_lock();

	objc_registerAllSelectors(module->symtab);
	objc_registerAllClasses(module->symtab);
	objc_registerAllCategories(module->symtab);
	objc_initStaticInstances(module->symtab);

	objc_globalMutex_unlock();
}

void
objc_deinit(void)
{
	objc_globalMutex_lock();

	objc_unregisterAllCategories();
	objc_unregisterAllClasses();
	objc_unregisterAllSelectors();
	objc_forgetPendingStaticInstances();
	objc_dtable_cleanup();

	objc_globalMutex_unlock();
}

Modified src/runtime/instance.m from [7b2e36d210] to [bb4ce37e30].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
	Class class;
	void (*last)(id, SEL) = NULL;

	if (object == nil)
		return NULL;

#ifdef OF_OBJFW_RUNTIME
	objc_zero_weak_references(object);
#endif

	if (destructSelector == NULL)
		destructSelector = sel_registerName(".cxx_destruct");

	for (class = object_getClass(object); class != Nil;
	    class = class_getSuperclass(class)) {







|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
	Class class;
	void (*last)(id, SEL) = NULL;

	if (object == nil)
		return NULL;

#ifdef OF_OBJFW_RUNTIME
	objc_zeroWeakReferences(object);
#endif

	if (destructSelector == NULL)
		destructSelector = sel_registerName(".cxx_destruct");

	for (class = object_getClass(object); class != Nil;
	    class = class_getSuperclass(class)) {

Modified src/runtime/ivar.m from [a37b0223f2] to [9c027bf0b1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	if (class == Nil) {
		if (outCount != NULL)
			*outCount = 0;

		return NULL;
	}

	objc_global_mutex_lock();

	count = (class->ivars != NULL ? class->ivars->count : 0);

	if (count == 0) {
		if (outCount != NULL)
			*outCount = 0;

		objc_global_mutex_unlock();
		return NULL;
	}

	if ((ivars = malloc((count + 1) * sizeof(Ivar))) == NULL)
		OBJC_ERROR("Not enough memory to copy ivars");

	for (unsigned int i = 0; i < count; i++)
		ivars[i] = &class->ivars->ivars[i];
	ivars[count] = NULL;

	if (outCount != NULL)
		*outCount = count;

	objc_global_mutex_unlock();

	return ivars;
}

const char *
ivar_getName(Ivar ivar)
{







|







|




|








|







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
	if (class == Nil) {
		if (outCount != NULL)
			*outCount = 0;

		return NULL;
	}

	objc_globalMutex_lock();

	count = (class->ivars != NULL ? class->ivars->count : 0);

	if (count == 0) {
		if (outCount != NULL)
			*outCount = 0;

		objc_globalMutex_unlock();
		return NULL;
	}

	if ((ivars = malloc((count + 1) * sizeof(Ivar))) == NULL)
		OBJC_ERROR("Not enough memory to copy ivars!");

	for (unsigned int i = 0; i < count; i++)
		ivars[i] = &class->ivars->ivars[i];
	ivars[count] = NULL;

	if (outCount != NULL)
		*outCount = count;

	objc_globalMutex_unlock();

	return ivars;
}

const char *
ivar_getName(Ivar ivar)
{

Modified src/runtime/linklib/Makefile from [90ff5bb4a5] to [dc27346920].

1
2
3

4
5
6
7
8
9
10
11
12
include ../../../extra.mk

STATIC_LIB = libobjfwrt.library.a

SRCS = linklib.m

include ../../../buildsys.mk

CPPFLAGS += -I..  -I../..  -I../../..				\
	    -DOBJC_COMPILING_AMIGA_LINKLIB			\
	    -DOBJFWRT_AMIGA_LIB=\"${OBJFWRT_AMIGA_LIB}\"	\
	    -DOBJFWRT_LIB_MINOR=${OBJFWRT_LIB_MINOR}
LD = ${OBJC}



>
|








1
2
3
4
5
6
7
8
9
10
11
12
13
include ../../../extra.mk

STATIC_LIB = libobjfwrt.library.a
SRCS = init.m		\
       linklib.m

include ../../../buildsys.mk

CPPFLAGS += -I..  -I../..  -I../../..				\
	    -DOBJC_COMPILING_AMIGA_LINKLIB			\
	    -DOBJFWRT_AMIGA_LIB=\"${OBJFWRT_AMIGA_LIB}\"	\
	    -DOBJFWRT_LIB_MINOR=${OBJFWRT_LIB_MINOR}
LD = ${OBJC}

Added src/runtime/linklib/init.m version [6ddafedf04].

























































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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"

#import "ObjFWRT.h"
#import "private.h"
#import "macros.h"

#include <proto/exec.h>
#include <proto/intuition.h>

struct ObjFWRTBase;

#include <stdio.h>
#include <stdlib.h>

#if defined(OF_AMIGAOS_M68K)
# include <stabs.h>
#elif defined(OF_MORPHOS)
# include <constructor.h>
#endif

#ifdef HAVE_SJLJ_EXCEPTIONS
extern int _Unwind_SjLj_RaiseException(void *);
#else
extern int _Unwind_RaiseException(void *);
#endif
extern void _Unwind_DeleteException(void *);
extern void *_Unwind_GetLanguageSpecificData(void *);
extern uintptr_t _Unwind_GetRegionStart(void *);
extern uintptr_t _Unwind_GetDataRelBase(void *);
extern uintptr_t _Unwind_GetTextRelBase(void *);
extern uintptr_t _Unwind_GetIP(void *);
extern uintptr_t _Unwind_GetGR(void *, int);
extern void _Unwind_SetIP(void *, uintptr_t);
extern void _Unwind_SetGR(void *, int, uintptr_t);
#ifdef HAVE_SJLJ_EXCEPTIONS
extern void _Unwind_SjLj_Resume(void *);
#else
extern void _Unwind_Resume(void *);
#endif
#ifdef OF_AMIGAOS_M68K
extern void __register_frame_info(const void *, void *);
extern void *__deregister_frame_info(const void *);
#endif
#ifdef OF_MORPHOS
extern void __register_frame(void *);
extern void __deregister_frame(void *);
#endif

struct Library *ObjFWRTBase;
void *__objc_class_name_Protocol;

extern bool objc_init(unsigned int version, struct objc_libc *libc);

static void
error(const char *string, ULONG arg)
{
	struct Library *IntuitionBase = OpenLibrary("intuition.library", 0);

	if (IntuitionBase != NULL) {
		struct EasyStruct easy = {
			.es_StructSize = sizeof(easy),
			.es_Flags = 0,
			.es_Title = (void *)NULL,
			.es_TextFormat = (void *)string,
			(void *)"OK"
		};

		EasyRequest(NULL, &easy, NULL, arg);

		CloseLibrary(IntuitionBase);
	}

	exit(EXIT_FAILURE);
}

static void __attribute__((__used__))
ctor(void)
{
	static bool initialized = false;
	struct objc_libc libc = {
		.malloc = malloc,
		.calloc = calloc,
		.realloc = realloc,
		.free = free,
#ifdef HAVE_SJLJ_EXCEPTIONS
		._Unwind_SjLj_RaiseException = _Unwind_SjLj_RaiseException,
#else
		._Unwind_RaiseException = _Unwind_RaiseException,
#endif
		._Unwind_DeleteException = _Unwind_DeleteException,
		._Unwind_GetLanguageSpecificData =
		    _Unwind_GetLanguageSpecificData,
		._Unwind_GetRegionStart = _Unwind_GetRegionStart,
		._Unwind_GetDataRelBase = _Unwind_GetDataRelBase,
		._Unwind_GetTextRelBase = _Unwind_GetTextRelBase,
		._Unwind_GetIP = _Unwind_GetIP,
		._Unwind_GetGR = _Unwind_GetGR,
		._Unwind_SetIP = _Unwind_SetIP,
		._Unwind_SetGR = _Unwind_SetGR,
#ifdef HAVE_SJLJ_EXCEPTIONS
		._Unwind_SjLj_Resume = _Unwind_SjLj_Resume,
#else
		._Unwind_Resume = _Unwind_Resume,
#endif
#ifdef OF_AMIGAOS_M68K
		.__register_frame_info = __register_frame_info,
		.__deregister_frame_info = __deregister_frame_info,
#endif
#ifdef OF_MORPHOS
		.__register_frame = __register_frame,
		.__deregister_frame = __deregister_frame,
#endif
#ifdef OF_AMIGAOS_M68K
		.vsnprintf = vsnprintf,
#endif
		.atexit = atexit,
		.exit = exit,
	};

	if (initialized)
		return;

	if ((ObjFWRTBase = OpenLibrary(OBJFWRT_AMIGA_LIB,
	    OBJFWRT_LIB_MINOR)) == NULL)
		error("Failed to open " OBJFWRT_AMIGA_LIB " version %lu!",
		    OBJFWRT_LIB_MINOR);

	if (!objc_init(1, &libc))
		error("Failed to initialize " OBJFWRT_AMIGA_LIB "!", 0);

	initialized = true;
}

static void __attribute__((__used__))
dtor(void)
{
	CloseLibrary(ObjFWRTBase);
}

#if defined(OF_AMIGAOS_M68K)
ADD2INIT(ctor, -5)
ADD2EXIT(dtor, -5)
#elif defined(OF_MORPHOS)
CONSTRUCTOR_P(ObjFWRT, 4000)
{
	ctor();

	return 0;
}

DESTRUCTOR_P(ObjFWRT, 0)
{
	dtor();
}
#endif

extern int __gnu_objc_personality(int version, int actions, uint64_t *exClass,
    void *ex, void *ctx);

int
#ifdef HAVE_SJLJ_EXCEPTIONS
__gnu_objc_personality_sj0(
#else
__gnu_objc_personality_v0(
#endif
    int version, int actions, uint64_t exClass, void *ex, void *ctx)
{
#ifdef OF_AMIGAOS_M68K
	return __gnu_objc_personality(version, actions, &exClass, ex, ctx);
#else
	return __gnu_objc_personality(version, actions, &exClass, ex, ctx);
#endif
}

Modified src/runtime/linklib/linklib.m from [f6ecf9cc72] to [0c00bc31d2].

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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "ObjFWRT.h"
#import "private.h"
#import "macros.h"


#include <proto/exec.h>










struct ObjFWRTBase;


#import "inline.h"



#include <stdio.h>
#include <stdlib.h>



#if defined(OF_AMIGAOS_M68K)
# include <stabs.h>
# define SYM(name) __asm__("_" name)


#elif defined(OF_MORPHOS)
# include <constructor.h>



# define SYM(name) __asm__(name)

#endif

#ifdef HAVE_SJLJ_EXCEPTIONS
extern int _Unwind_SjLj_RaiseException(void *);
#else
extern int _Unwind_RaiseException(void *);


#endif
extern void _Unwind_DeleteException(void *);
extern void *_Unwind_GetLanguageSpecificData(void *);
extern uintptr_t _Unwind_GetRegionStart(void *);
extern uintptr_t _Unwind_GetDataRelBase(void *);
extern uintptr_t _Unwind_GetTextRelBase(void *);
extern uintptr_t _Unwind_GetIP(void *);
extern uintptr_t _Unwind_GetGR(void *, int);
extern void _Unwind_SetIP(void *, uintptr_t);
extern void _Unwind_SetGR(void *, int, uintptr_t);
#ifdef HAVE_SJLJ_EXCEPTIONS
extern void _Unwind_SjLj_Resume(void *);
#else

extern void _Unwind_Resume(void *);

#endif
extern void __register_frame_info(const void *, void *);
extern void *__deregister_frame_info(const void *);

struct Library *ObjFWRTBase;
void *__objc_class_name_Protocol;



static void __attribute__((__used__))


ctor(void)






{
	static bool initialized = false;
	struct objc_libc libc = {
		.malloc = malloc,
		.calloc = calloc,
		.realloc = realloc,
		.free = free,
		.vfprintf = vfprintf,
		.fflush = fflush,
		.abort = abort,
#ifdef HAVE_SJLJ_EXCEPTIONS
		._Unwind_SjLj_RaiseException = _Unwind_SjLj_RaiseException,
#else
		._Unwind_RaiseException = _Unwind_RaiseException,
#endif

		._Unwind_DeleteException = _Unwind_DeleteException,
		._Unwind_GetLanguageSpecificData =



		    _Unwind_GetLanguageSpecificData,
		._Unwind_GetRegionStart = _Unwind_GetRegionStart,
		._Unwind_GetDataRelBase = _Unwind_GetDataRelBase,
		._Unwind_GetTextRelBase = _Unwind_GetTextRelBase,
		._Unwind_GetIP = _Unwind_GetIP,
		._Unwind_GetGR = _Unwind_GetGR,
		._Unwind_SetIP = _Unwind_SetIP,
		._Unwind_SetGR = _Unwind_SetGR,

#ifdef HAVE_SJLJ_EXCEPTIONS
		._Unwind_SjLj_Resume = _Unwind_SjLj_Resume,

#else
		._Unwind_Resume = _Unwind_Resume,
#endif
		.__register_frame_info = __register_frame_info,
		.__deregister_frame_info = __deregister_frame_info,
	};

	if (initialized)
		return;

	if ((ObjFWRTBase = OpenLibrary(OBJFWRT_AMIGA_LIB,
	    OBJFWRT_LIB_MINOR)) == NULL) {
		fputs("Failed to open " OBJFWRT_AMIGA_LIB "!\n", stderr);
		abort();
	}


	if (!glue_objc_init(1, &libc, stdout, stderr)) {
		fputs("Failed to initialize " OBJFWRT_AMIGA_LIB "!\n", stderr);







		abort();
	}

	initialized = true;

}

static void __attribute__((__used__))
dtor(void)
{
	CloseLibrary(ObjFWRTBase);
}


#if defined(OF_AMIGAOS_M68K)
ADD2INIT(ctor, -2);
ADD2EXIT(dtor, -2);

#elif defined(OF_MORPHOS)


CONSTRUCTOR_P(ObjFWRT, 4000)
{
	ctor();

	return 0;

}

DESTRUCTOR_P(ObjFWRT, 4000)


{
	dtor();
}
#endif

void
__objc_exec_class(struct objc_module *module)
{
	/*
	 * The compiler generates constructors that call into this, so it is
	 * possible that we are not set up yet when we get called.
	 */


	ctor();

	glue___objc_exec_class(module);
}

IMP
objc_msg_lookup(id object, SEL selector)
{
	return glue_objc_msg_lookup(object, selector);
}

IMP

objc_msg_lookup_stret(id object, SEL selector)
{
	return glue_objc_msg_lookup_stret(object, selector);
}

IMP
objc_msg_lookup_super(struct objc_super *super, SEL selector)







{

	return glue_objc_msg_lookup_super(super, selector);
}

IMP
objc_msg_lookup_super_stret(struct objc_super *super, SEL selector)
{
	return glue_objc_msg_lookup_super_stret(super, selector);
}

Class
objc_lookUpClass(const char *name)
{
	return glue_objc_lookUpClass(name);
}


Class
objc_getClass(const char *name)
{
	return glue_objc_getClass(name);
}

Class
objc_getRequiredClass(const char *name)
{
	return glue_objc_getRequiredClass(name);
}

Class
objc_lookup_class(const char *name)

{
	return glue_objc_lookup_class(name);


}

Class
objc_get_class(const char *name)
{









	return glue_objc_get_class(name);


}

void
objc_exception_throw(id object)
{
#ifdef OF_AMIGAOS_M68K
	/*
	 * This does not use the glue code to hack around a compiler bug.
	 *
	 * When using the generated inline stubs, the compiler does not emit
	 * any frame information, making the unwind fail. As unwind always
	 * starts from objc_exception_throw(), this means exceptions would
	 * never work. If, however, we're using a function pointer instead of
	 * the inline stub, the compiler does generate a frame and everything
	 * works fine.
	 */
	register void *a6 __asm__("a6") = ObjFWRTBase;
	uintptr_t throw = (((uintptr_t)ObjFWRTBase) - 0x60);
	((void (*)(id __asm__("a0")))throw)(object);
	(void)a6;

#else




	glue_objc_exception_throw(object);

#endif

	OF_UNREACHABLE
}

int
objc_sync_enter(id object)
{









	return glue_objc_sync_enter(object);


}

int
objc_sync_exit(id object)
{



	return glue_objc_sync_exit(object);





}



id

objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, bool atomic)
{









	return glue_objc_getProperty(self, _cmd, offset, atomic);


}

void
objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id value, bool atomic,
    signed char copy)
{
	glue_objc_setProperty(self, _cmd, offset, value, atomic, copy);
}














void
objc_getPropertyStruct(void *dest, const void *src, ptrdiff_t size, bool atomic,









    bool strong)
{
	glue_objc_getPropertyStruct(dest, src, size, atomic, strong);

}

void
objc_setPropertyStruct(void *dest, const void *src, ptrdiff_t size, bool atomic,
    bool strong)
{



	glue_objc_setPropertyStruct(dest, src, size, atomic, strong);
}










void
objc_enumerationMutation(id object)
{
#ifdef OF_AMIGAOS_M68K








	/*
	 * This does not use the glue code to hack around a compiler bug.



	 *
	 * When using the generated inline stubs, the compiler does not emit
	 * any frame information, making the unwind fail. As a result
	 * objc_enumerationMutation() might throw an exception that could never
	 * be caught. If, however, we're using a function pointer instead of
	 * the inline stub, the compiler does generate a frame and everything
	 * works fine.
	 */



	register void *a6 __asm__("a6") = ObjFWRTBase;







	uintptr_t enumerationMutation = (((uintptr_t)ObjFWRTBase) - 0x8A);



	((void (*)(id __asm__("a0")))enumerationMutation)(object);





	(void)a6;

#else




	glue_objc_enumerationMutation(object);

#endif

	OF_UNREACHABLE
}

#ifdef HAVE_SJLJ_EXCEPTIONS
int
__gnu_objc_personality_sj0(int version, int actions, uint64_t exClass,
    void *ex, void *ctx)
{
# ifdef OF_AMIGAOS_M68K



	return glue___gnu_objc_personality(version, actions, &exClass, ex, ctx);


# else

	return glue___gnu_objc_personality(version, actions, exClass, ex, ctx);

# endif
}
#else
int
__gnu_objc_personality_v0(int version, int actions, uint64_t exClass,
    void *ex, void *ctx)
{
# ifdef OF_AMIGAOS_M68K
	return glue___gnu_objc_personality(version, actions, &exClass, ex, ctx);



# else
	return glue___gnu_objc_personality(version, actions, exClass, ex, ctx);
# endif




}

#endif


id
objc_retain(id object)
{









	return glue_objc_retain(object);


}

id
objc_retainBlock(id block)
{









	return glue_objc_retainBlock(block);


}

id
objc_retainAutorelease(id object)
{









	return glue_objc_retainAutorelease(object);


}










void

objc_release(id object)

{
	glue_objc_release(object);


}

id
objc_autorelease(id object)
{
	return glue_objc_autorelease(object);
}


id
objc_autoreleaseReturnValue(id object)





{
	return glue_objc_autoreleaseReturnValue(object);


}

id
objc_retainAutoreleaseReturnValue(id object)

{
	return glue_objc_retainAutoreleaseReturnValue(object);
}


id
objc_retainAutoreleasedReturnValue(id object)





{
	return glue_objc_retainAutoreleasedReturnValue(object);


}

id
objc_storeStrong(id *object, id value)
{
	return glue_objc_storeStrong(object, value);
}


id
objc_storeWeak(id *object, id value)






{
	return glue_objc_storeWeak(object, value);

}

id
objc_loadWeakRetained(id *object)
{









	return glue_objc_loadWeakRetained(object);


}


















id
objc_initWeak(id *object, id value)
{









	return glue_objc_initWeak(object, value);


}


















void
objc_destroyWeak(id *object)
{









	glue_objc_destroyWeak(object);


}

id
objc_loadWeak(id *object)
{
	return glue_objc_loadWeak(object);
}





void
objc_copyWeak(id *dest, id *src)


{
	glue_objc_copyWeak(dest, src);


}






void
objc_moveWeak(id *dest, id *src)






{
	glue_objc_moveWeak(dest, src);


}






SEL

sel_registerName(const char *name)





{
	return glue_sel_registerName(name);


}

const char *
sel_getName(SEL selector)
{









	return glue_sel_getName(selector);


}



bool
sel_isEqual(SEL selector1, SEL selector2)
{
	return glue_sel_isEqual(selector1, selector2);
}


Class
objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)





{
	return glue_objc_allocateClassPair(superclass, name, extraBytes);

}

void
objc_registerClassPair(Class class)
{









	glue_objc_registerClassPair(class);


}

unsigned int
objc_getClassList(Class *buffer, unsigned int count)
{









	return glue_objc_getClassList(buffer, count);


}

Class *
objc_copyClassList(unsigned int *length)
{









	return glue_objc_copyClassList(length);


}

bool
class_isMetaClass(Class class)
{









	return glue_class_isMetaClass(class);


}

const char *
class_getName(Class class)
{









	return glue_class_getName(class);


}

Class
class_getSuperclass(Class class)
{









	return glue_class_getSuperclass(class);


}

unsigned long
class_getInstanceSize(Class class)
{









	return glue_class_getInstanceSize(class);


}

bool
class_respondsToSelector(Class class, SEL selector)
{









	return glue_class_respondsToSelector(class, selector);


}

bool
class_conformsToProtocol(Class class, Protocol *protocol)
{
	return glue_class_conformsToProtocol(class, protocol);
}














IMP
class_getMethodImplementation(Class class, SEL selector)
{









	return glue_class_getMethodImplementation(class, selector);


}

IMP
class_getMethodImplementation_stret(Class class, SEL selector)
{



	return glue_class_getMethodImplementation_stret(class, selector);





}




Method
class_getInstanceMethod(Class class, SEL selector)
{









	return glue_class_getInstanceMethod(class, selector);


}

bool
class_addMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
{









	return glue_class_addMethod(class, selector, implementation,

	    typeEncoding);
}




IMP
class_replaceMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)






{
	return glue_class_replaceMethod(class, selector, implementation,
	    typeEncoding);
}

Class
object_getClass(id object)
{









	return glue_object_getClass(object);


}

















Class

object_setClass(id object, Class class)
{









	return glue_object_setClass(object, class);


}

const char *
object_getClassName(id object)
{








	return glue_object_getClassName(object);
}


const char *
protocol_getName(Protocol *protocol)
{
	return glue_protocol_getName(protocol);
}

bool
protocol_isEqual(Protocol *protocol1, Protocol *protocol2)
{









	return glue_protocol_isEqual(protocol1, protocol2);


}

bool
protocol_conformsToProtocol(Protocol *protocol1, Protocol *protocol2)
{









	return glue_protocol_conformsToProtocol(protocol1, protocol2);


}







objc_uncaught_exception_handler_t
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler_t handler)





{
	return glue_objc_setUncaughtExceptionHandler(handler);


}

void
objc_setForwardHandler(IMP forward, IMP stretForward)
{









	glue_objc_setForwardHandler(forward, stretForward);


}











void

objc_setEnumerationMutationHandler(objc_enumeration_mutation_handler_t handler)
{
	glue_objc_setEnumerationMutationHandler(handler);

}

id
objc_constructInstance(Class class, void *_Nullable bytes)
{









	return glue_objc_constructInstance(class, bytes);


}

void
objc_exit(void)
{









	glue_objc_exit();



}





Ivar *
class_copyIvarList(Class class, unsigned int *outCount)





{
	return glue_class_copyIvarList(class, outCount);

}

const char *
ivar_getName(Ivar ivar)
{









	return glue_ivar_getName(ivar);


}







const char *
ivar_getTypeEncoding(Ivar ivar)




{
	return glue_ivar_getTypeEncoding(ivar);


}

ptrdiff_t
ivar_getOffset(Ivar ivar)
{









	return glue_ivar_getOffset(ivar);


}













Method *
class_copyMethodList(Class class, unsigned int *outCount)
{
	return glue_class_copyMethodList(class, outCount);

}

SEL
method_getName(Method method)
{









	return glue_method_getName(method);


}

const char *
method_getTypeEncoding(Method method)
{









	return glue_method_getTypeEncoding(method);


}

objc_property_t *
class_copyPropertyList(Class class, unsigned int *outCount)
{









	return glue_class_copyPropertyList(class, outCount);


}

const char *
property_getName(objc_property_t property)
{









	return glue_property_getName(property);


}

















char *
property_copyAttributeValue(objc_property_t property, const char *name)


{









	return glue_property_copyAttributeValue(property, name);


}

void *
objc_destructInstance(id object)
{

	return glue_objc_destructInstance(object);







}

void *
objc_autoreleasePoolPush(void)
{
	return glue_objc_autoreleasePoolPush();
}

void
objc_autoreleasePoolPop(void *pool)
{









	glue_objc_autoreleasePoolPop(pool);


}

id
_objc_rootAutorelease(id object)
{









	return glue__objc_rootAutorelease(object);


}





struct objc_hashtable *

objc_hashtable_new(objc_hashtable_hash_func hash,
    objc_hashtable_equal_func equal, uint32_t size)





{
	return glue_objc_hashtable_new(hash, equal, size);

}

void
objc_hashtable_set(struct objc_hashtable *table, const void *key,



    const void *object)






{
	glue_objc_hashtable_set(table, key, object);

}










void *
objc_hashtable_get(struct objc_hashtable *table, const void *key)

{
	return glue_objc_hashtable_get(table, key);

}

void
objc_hashtable_delete(struct objc_hashtable *table, const void *key)
{









	glue_objc_hashtable_delete(table, key);


}

void
objc_hashtable_free(struct objc_hashtable *table)
{









	glue_objc_hashtable_free(table);


}

void
objc_setTaggedPointerSecret(uintptr_t secret)
{









	glue_objc_setTaggedPointerSecret(secret);


}

int
objc_registerTaggedPointerClass(Class class)
{









	return glue_objc_registerTaggedPointerClass(class);


}

bool
object_isTaggedPointer(id object)
{



	return glue_object_isTaggedPointer(object);
}

Class
object_getTaggedPointerClass(id object)



{
	return glue_object_getTaggedPointerClass(object);

}

uintptr_t
object_getTaggedPointerValue(id object)
{









	return glue_object_getTaggedPointerValue(object);


}

id
objc_createTaggedPointer(int class, uintptr_t value)
{









	return objc_createTaggedPointer(class, value);


}

<
<
|













>
>




|
>

<
>
>
|
>
>
>
>
>
>
>
|
>

<
>
>
|
|
<
>
>
|

<
|
>
>

|
>
>
>
|
>

|
|
<
|
<
>
>
|
<
<
<
<
|
|
|
|
|
|
<
|
>
|
>

<
<
|
|
<
>
>
|
<
>
>
|
>
>
>
>
>
>
|
<
<
<
<
<
<
<
<
<
|
<
<
<

>
|
<
>
>
>
|
|
|
|
|
|
|
|
>
|
<
>
<
<

<
<
<
|
<
<

<
<
<
|
<
>
|
<
|
>
>
>
>
>
>
>
|
|
|
<
>


<
<
<
|
<
>
|

|
|
>

>
>
|
<
|

|
>


<
>
>

<
<
|
|
|
<
<
|
|
|
<
>
>
|

|
<
|
<
<
<
<


<
>
|

<
<
|
<
|
>
>
>
>
>
>
>
|
>
|

<
<
<
<
<
|
<
|
|

<
<
|
>
|
|
<
<
<
|
|
<
<
<
<
|
|
<
>
|
<
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>



|

|
<
<
<
<
<
<
<
<
<
<
|
<
<

>
|
>
>
>
>
|
>






|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
|
>
>
>
>
>
|
>
>
|
|
>
|

>
>
>
>
>
>
>
>
>
|
>
>



|
<

<
<
>
>
>
>
>
>
>
>
>

>
>
>
>

|
>
>
>
>
>
>
>
>
>
|
|
|
>



|
<

>
>
>
|
<
>
>
>
>
>

>
>
>
>

|

|
>
>
>
>
>
>
>
>
|
<
>
>
>
|
<
<
<
<
<
|
<
>
>
>
|
>
>
>
>
>
>
>
|
>
>
>
|
>
>
>
>
>

>
|
>
>
>
>
|
>

|
<
|
|
<
<
<
|

|
>
>
>
|
>
>
|
>
|
>
|

|
|
<
|

|
<
>
>
>
|
<
<
>
>
>
>
|
>

|
>
|
|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
|
<
>
|
>
|
<
>
>


|
|

<
<
|
>
|
|
>
>
>
>
>
|
<
>
>


|
<
>

<
<
>
|
|
|
>
>
>
>
>
|
<
>
>


|
|

<
<
|
>
|
<
>
>
>
>
>
>
|
|
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>

|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

<
<
>
>
>
>
|
|
|
>
>
|
<
>
>


>
>
>
>
>
|
<
>
>
>
>
>
>
|
<
>
>

>
>
>
>
>
|
<
>
|
>
>
>
>
>
|
<
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
|
<
<

<
<
|
>
|
|
>
>
>
>
>
|
|
>



|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>



|

<
<
>
>
>
>
>
>
>
>
>

>
>
>
>
|
|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
|
>
>
>
>
>
|
>
>
|
>
|
|

>
>
>
>
>
>
>
>
>
|
>
>



|
<

>
>
>
>
>
>
>
>
>
|
>
|

>
>
>
|
|
|
|
>
>
>
>
>
>
|
|
|


|
|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
|
>
|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
|
|
>
|
<
<
<
<



|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
>
|
>
>
|
<
>
>
>
>
>
|
<
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
>
|
<
>
|
|
|
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>
>
|
>
>
|
>
>
|
|
>
>
>
>
>
|
|
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
>
>
>
|
|
|
>
>
>
>
|
<
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
|
<
<
|
|
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
|
<
>
>

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
|
>
>
>
>
>
>
>
|
|
|
<
<
<



|

>
>
>
>
>
>
>
>
>
|
>
>


|
|

>
>
>
>
>
>
>
>
>
|
>
>

>
>
>
|
>
|
>
|
<
>
>
>
>
>
|
|
>



|
>
>
>
|
>
>
>
>
>
>
|
|
>

>
>
>
>
>
>
>
>
>
|
|
|
>
|
|
>



|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>





>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
>
>
>
>
>
>
|
>
>



|

>
>
>
|
<
|
|
<
>
>
>
|
|
>



|

>
>
>
>
>
>
>
>
>
|
>
>


|


>
>
>
>
>
>
>
>
>
|
>
>

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
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
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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
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
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
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495

1496
1497

1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
/*


 * 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.
 */

/* This file is automatically generated from amiga-library.xml */

#include "config.h"

#import "ObjFWRT.h"
#import "private.h"

extern struct Library *ObjFWRTBase;


bool
objc_init(unsigned int version, struct objc_libc *libc)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(unsigned int __asm__("d0"), struct objc_libc *__asm__("a0")))(((uintptr_t)ObjFWRTBase) - 30))(version, libc);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((bool (*)(unsigned int, struct objc_libc *))*(void **)(((uintptr_t)ObjFWRTBase) - 28))(version, libc);
#endif
}


void
__objc_exec_class(struct objc_module *_Nonnull module)
{
#if defined(OF_AMIGAOS_M68K)

	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(struct objc_module *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 36))(module);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(struct objc_module *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 34))(module);
#endif
}


IMP _Nonnull

objc_msg_lookup(id _Nullable object, SEL _Nonnull selector)
{
#if defined(OF_AMIGAOS_M68K)




	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((IMP _Nonnull (*)(id _Nullable __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 42))(object, selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"

	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((IMP _Nonnull (*)(id _Nullable, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 40))(object, selector);
#endif


}


IMP _Nonnull
objc_msg_lookup_stret(id _Nullable object, SEL _Nonnull selector)
{

#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((IMP _Nonnull (*)(id _Nullable __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 48))(object, selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);










	return __extension__ ((IMP _Nonnull (*)(id _Nullable, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 46))(object, selector);



#endif
}


IMP _Nonnull
objc_msg_lookup_super(struct objc_super *_Nonnull super, SEL _Nonnull selector)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((IMP _Nonnull (*)(struct objc_super *_Nonnull __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 54))(super, selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((IMP _Nonnull (*)(struct objc_super *_Nonnull, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 52))(super, selector);


#endif



}






IMP _Nonnull

objc_msg_lookup_super_stret(struct objc_super *_Nonnull super, SEL _Nonnull selector)
{

#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((IMP _Nonnull (*)(struct objc_super *_Nonnull __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 60))(super, selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((IMP _Nonnull (*)(struct objc_super *_Nonnull, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 58))(super, selector);

#endif
}




Class _Nullable

objc_lookUpClass(const char *_Nonnull name)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nullable (*)(const char *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 66))(name);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"

	);

	return __extension__ ((Class _Nullable (*)(const char *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 64))(name);
#endif
}


Class _Nullable
objc_getClass(const char *_Nonnull name)
{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;


	return ((Class _Nullable (*)(const char *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 72))(name);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (

	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Class _Nullable (*)(const char *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 70))(name);

#endif




}


Class _Nonnull
objc_getRequiredClass(const char *_Nonnull name)
{


#if defined(OF_AMIGAOS_M68K)

	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nonnull (*)(const char *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 78))(name);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Class _Nonnull (*)(const char *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 76))(name);
#endif
}







Class _Nullable
objc_lookup_class(const char *_Nonnull name)
{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nullable (*)(const char *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 84))(name);



#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (




	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"

	);


	return __extension__ ((Class _Nullable (*)(const char *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 82))(name);
#endif
}

Class _Nonnull
objc_get_class(const char *_Nonnull name)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nonnull (*)(const char *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 90))(name);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Class _Nonnull (*)(const char *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 88))(name);
#endif
}

void
objc_exception_throw(id _Nonnull object)
{
#if defined(OF_AMIGAOS_M68K)










	register struct Library *a6 __asm__("a6") = ObjFWRTBase;


	(void)a6;
	((void (*)(id _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 96))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(id _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 94))(object);
#endif

	OF_UNREACHABLE
}

int
objc_sync_enter(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((int (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 102))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((int (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 100))(object);
#endif
}

int
objc_sync_exit(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((int (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 108))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((int (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 106))(object);
#endif
}

id _Nullable
objc_getProperty(id _Nonnull self, SEL _Nonnull _cmd, ptrdiff_t offset, bool atomic)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nonnull __asm__("a0"), SEL _Nonnull __asm__("a1"), ptrdiff_t __asm__("d0"), bool __asm__("d1")))(((uintptr_t)ObjFWRTBase) - 114))(self, _cmd, offset, atomic);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nonnull, SEL _Nonnull, ptrdiff_t, bool))*(void **)(((uintptr_t)ObjFWRTBase) - 112))(self, _cmd, offset, atomic);
#endif
}

void
objc_setProperty(id _Nonnull self, SEL _Nonnull _cmd, ptrdiff_t offset, id _Nullable value, bool atomic, signed char copy)

{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(id _Nonnull __asm__("a0"), SEL _Nonnull __asm__("a1"), ptrdiff_t __asm__("d0"), id _Nullable __asm__("a2"), bool __asm__("d1"), signed char __asm__("d2")))(((uintptr_t)ObjFWRTBase) - 120))(self, _cmd, offset, value, atomic, copy);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(id _Nonnull, SEL _Nonnull, ptrdiff_t, id _Nullable, bool, signed char))*(void **)(((uintptr_t)ObjFWRTBase) - 118))(self, _cmd, offset, value, atomic, copy);
#endif
}

void
objc_getPropertyStruct(void *_Nonnull dest, const void *_Nonnull src, ptrdiff_t size, bool atomic, bool strong)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(void *_Nonnull __asm__("a0"), const void *_Nonnull __asm__("a1"), ptrdiff_t __asm__("d0"), bool __asm__("d1"), bool __asm__("d2")))(((uintptr_t)ObjFWRTBase) - 126))(dest, src, size, atomic, strong);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(void *_Nonnull, const void *_Nonnull, ptrdiff_t, bool, bool))*(void **)(((uintptr_t)ObjFWRTBase) - 124))(dest, src, size, atomic, strong);
#endif
}

void
objc_setPropertyStruct(void *_Nonnull dest, const void *_Nonnull src, ptrdiff_t size, bool atomic, bool strong)

{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(void *_Nonnull __asm__("a0"), const void *_Nonnull __asm__("a1"), ptrdiff_t __asm__("d0"), bool __asm__("d1"), bool __asm__("d2")))(((uintptr_t)ObjFWRTBase) - 132))(dest, src, size, atomic, strong);

#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(void *_Nonnull, const void *_Nonnull, ptrdiff_t, bool, bool))*(void **)(((uintptr_t)ObjFWRTBase) - 130))(dest, src, size, atomic, strong);
#endif
}

void
objc_enumerationMutation(id _Nonnull object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(id _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 138))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	__extension__ ((void (*)(id _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 136))(object);
#endif
}






int

__gnu_objc_personality(int version, int actions, uint64_t *_Nonnull exClass, void *_Nonnull ex, void *_Nonnull ctx)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((int (*)(int __asm__("d0"), int __asm__("d1"), uint64_t *_Nonnull __asm__("d2"), void *_Nonnull __asm__("a0"), void *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 144))(version, actions, exClass, ex, ctx);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((int (*)(int, int, uint64_t *_Nonnull, void *_Nonnull, void *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 142))(version, actions, exClass, ex, ctx);
#endif
}

id _Nullable
objc_retain(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 150))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 148))(object);
#endif
}


id _Nullable



objc_retainBlock(id _Nullable block)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 156))(block);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 154))(block);
#endif
}

id _Nullable

objc_retainAutorelease(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)

	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 162))(object);
#elif defined(OF_MORPHOS)


	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 160))(object);
#endif
}

void
objc_release(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 168))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 166))(object);
#endif
}

id _Nullable
objc_autorelease(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 174))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 172))(object);
#endif
}

id _Nullable
objc_autoreleaseReturnValue(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 180))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 178))(object);
#endif
}

id _Nullable
objc_retainAutoreleaseReturnValue(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 186))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (

	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((id _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 184))(object);
#endif
}

id _Nullable
objc_retainAutoreleasedReturnValue(id _Nullable object)
{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 192))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((id _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 190))(object);
#endif
}

id _Nullable

objc_storeStrong(id _Nullable *_Nonnull object, id _Nullable value)
{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable *_Nonnull __asm__("a0"), id _Nullable __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 198))(object, value);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((id _Nullable (*)(id _Nullable *_Nonnull, id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 196))(object, value);
#endif
}

id _Nullable
objc_storeWeak(id _Nullable *_Nonnull object, id _Nullable value)
{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;

	return ((id _Nullable (*)(id _Nullable *_Nonnull __asm__("a0"), id _Nullable __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 204))(object, value);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable *_Nonnull, id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 202))(object, value);
#endif
}

id _Nullable
objc_loadWeakRetained(id _Nullable *_Nonnull object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 210))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 208))(object);
#endif
}

id _Nullable
objc_initWeak(id _Nullable *_Nonnull object, id _Nullable value)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable *_Nonnull __asm__("a0"), id _Nullable __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 216))(object, value);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable *_Nonnull, id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 214))(object, value);
#endif
}

void
objc_destroyWeak(id _Nullable *_Nonnull object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(id _Nullable *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 222))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(id _Nullable *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 220))(object);
#endif
}

id _Nullable
objc_loadWeak(id _Nullable *_Nonnull object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 228))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 226))(object);
#endif
}

void
objc_copyWeak(id _Nullable *_Nonnull dest, id _Nullable *_Nonnull src)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(id _Nullable *_Nonnull __asm__("a0"), id _Nullable *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 234))(dest, src);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(id _Nullable *_Nonnull, id _Nullable *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 232))(dest, src);
#endif
}

void
objc_moveWeak(id _Nullable *_Nonnull dest, id _Nullable *_Nonnull src)
{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(id _Nullable *_Nonnull __asm__("a0"), id _Nullable *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 240))(dest, src);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	__extension__ ((void (*)(id _Nullable *_Nonnull, id _Nullable *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 238))(dest, src);
#endif
}

SEL _Nonnull
sel_registerName(const char *_Nonnull name)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;

	return ((SEL _Nonnull (*)(const char *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 246))(name);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((SEL _Nonnull (*)(const char *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 244))(name);
#endif
}

const char *_Nonnull
sel_getName(SEL _Nonnull selector)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;

	(void)a6;
	return ((const char *_Nonnull (*)(SEL _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 252))(selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((const char *_Nonnull (*)(SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 250))(selector);
#endif
}

bool
sel_isEqual(SEL _Nonnull selector1, SEL _Nonnull selector2)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(SEL _Nonnull __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 258))(selector1, selector2);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((bool (*)(SEL _Nonnull, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 256))(selector1, selector2);
#endif
}

Class _Nonnull
objc_allocateClassPair(Class _Nullable superclass, const char *_Nonnull name, size_t extraBytes)


{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nonnull (*)(Class _Nullable __asm__("a0"), const char *_Nonnull __asm__("a1"), size_t __asm__("d0")))(((uintptr_t)ObjFWRTBase) - 264))(superclass, name, extraBytes);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Class _Nonnull (*)(Class _Nullable, const char *_Nonnull, size_t))*(void **)(((uintptr_t)ObjFWRTBase) - 262))(superclass, name, extraBytes);
#endif
}

void
objc_registerClassPair(Class _Nonnull class)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(Class _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 270))(class);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(Class _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 268))(class);
#endif
}

unsigned int
objc_getClassList(Class _Nonnull *_Nullable buffer, unsigned int count)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((unsigned int (*)(Class _Nonnull *_Nullable __asm__("a0"), unsigned int __asm__("d0")))(((uintptr_t)ObjFWRTBase) - 276))(buffer, count);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((unsigned int (*)(Class _Nonnull *_Nullable, unsigned int))*(void **)(((uintptr_t)ObjFWRTBase) - 274))(buffer, count);
#endif
}

Class _Nonnull *_Nonnull
objc_copyClassList(unsigned int *_Nullable length)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nonnull *_Nonnull (*)(unsigned int *_Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 282))(length);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Class _Nonnull *_Nonnull (*)(unsigned int *_Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 280))(length);
#endif
}

bool
class_isMetaClass(Class _Nullable class)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(Class _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 288))(class);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((bool (*)(Class _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 286))(class);
#endif
}

const char *_Nullable
class_getName(Class _Nullable class)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((const char *_Nullable (*)(Class _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 294))(class);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((const char *_Nullable (*)(Class _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 292))(class);
#endif
}

Class _Nullable
class_getSuperclass(Class _Nullable class)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nullable (*)(Class _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 300))(class);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Class _Nullable (*)(Class _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 298))(class);
#endif
}

unsigned long
class_getInstanceSize(Class _Nullable class)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((unsigned long (*)(Class _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 306))(class);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((unsigned long (*)(Class _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 304))(class);
#endif
}

bool
class_respondsToSelector(Class _Nullable class, SEL _Nonnull selector)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(Class _Nullable __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 312))(class, selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((bool (*)(Class _Nullable, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 310))(class, selector);
#endif
}

bool
class_conformsToProtocol(Class _Nullable class, Protocol *_Nonnull p)
{


#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(Class _Nullable __asm__("a0"), Protocol *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 318))(class, p);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((bool (*)(Class _Nullable, Protocol *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 316))(class, p);
#endif
}

IMP _Nullable
class_getMethodImplementation(Class _Nullable class, SEL _Nonnull selector)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((IMP _Nullable (*)(Class _Nullable __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 324))(class, selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((IMP _Nullable (*)(Class _Nullable, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 322))(class, selector);
#endif
}

IMP _Nullable
class_getMethodImplementation_stret(Class _Nullable class, SEL _Nonnull selector)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((IMP _Nullable (*)(Class _Nullable __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 330))(class, selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((IMP _Nullable (*)(Class _Nullable, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 328))(class, selector);
#endif
}

Method _Nullable
class_getInstanceMethod(Class _Nullable class, SEL _Nonnull selector)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Method _Nullable (*)(Class _Nullable __asm__("a0"), SEL _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 336))(class, selector);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Method _Nullable (*)(Class _Nullable, SEL _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 334))(class, selector);
#endif
}

bool
class_addMethod(Class _Nonnull class, SEL _Nonnull selector, IMP _Nonnull implementation, const char *_Nullable typeEncoding)

{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(Class _Nonnull __asm__("a0"), SEL _Nonnull __asm__("a1"), IMP _Nonnull __asm__("a2"), const char *_Nullable __asm__("a3")))(((uintptr_t)ObjFWRTBase) - 342))(class, selector, implementation, typeEncoding);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((bool (*)(Class _Nonnull, SEL _Nonnull, IMP _Nonnull, const char *_Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 340))(class, selector, implementation, typeEncoding);
#endif
}

IMP _Nullable
class_replaceMethod(Class _Nonnull class, SEL _Nonnull selector, IMP _Nonnull implementation, const char *_Nullable typeEncoding)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((IMP _Nullable (*)(Class _Nonnull __asm__("a0"), SEL _Nonnull __asm__("a1"), IMP _Nonnull __asm__("a2"), const char *_Nullable __asm__("a3")))(((uintptr_t)ObjFWRTBase) - 348))(class, selector, implementation, typeEncoding);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((IMP _Nullable (*)(Class _Nonnull, SEL _Nonnull, IMP _Nonnull, const char *_Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 346))(class, selector, implementation, typeEncoding);
#endif
}

Class _Nullable
object_getClass(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 354))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Class _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 352))(object);
#endif
}

Class _Nullable
object_setClass(id _Nullable object, Class _Nonnull class)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Class _Nullable (*)(id _Nullable __asm__("a0"), Class _Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 360))(object, class);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Class _Nullable (*)(id _Nullable, Class _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 358))(object, class);
#endif
}

const char *_Nullable
object_getClassName(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((const char *_Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 366))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((const char *_Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 364))(object);
#endif
}

const char *_Nonnull
protocol_getName(Protocol *_Nonnull protocol)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((const char *_Nonnull (*)(Protocol *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 372))(protocol);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((const char *_Nonnull (*)(Protocol *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 370))(protocol);
#endif




}

bool
protocol_isEqual(Protocol *_Nonnull protocol1, Protocol *_Nonnull protocol2)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(Protocol *_Nonnull __asm__("a0"), Protocol *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 378))(protocol1, protocol2);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((bool (*)(Protocol *_Nonnull, Protocol *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 376))(protocol1, protocol2);
#endif
}

bool
protocol_conformsToProtocol(Protocol *_Nonnull protocol1, Protocol *_Nonnull protocol2)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(Protocol *_Nonnull __asm__("a0"), Protocol *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 384))(protocol1, protocol2);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((bool (*)(Protocol *_Nonnull, Protocol *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 382))(protocol1, protocol2);
#endif
}

_Nullable objc_uncaught_exception_handler
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler _Nullable handler)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((_Nullable objc_uncaught_exception_handler (*)(objc_uncaught_exception_handler _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 390))(handler);

#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((_Nullable objc_uncaught_exception_handler (*)(objc_uncaught_exception_handler _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 388))(handler);
#endif
}

void
objc_setForwardHandler(IMP _Nullable forward, IMP _Nullable stretForward)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(IMP _Nullable __asm__("a0"), IMP _Nullable __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 396))(forward, stretForward);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(IMP _Nullable, IMP _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 394))(forward, stretForward);
#endif
}

void
objc_setEnumerationMutationHandler(objc_enumeration_mutation_handler _Nullable hadler)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(objc_enumeration_mutation_handler _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 402))(hadler);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"

	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(objc_enumeration_mutation_handler _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 400))(hadler);
#endif
}

id _Nullable
objc_constructInstance(Class _Nullable class, void *_Nullable bytes)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(Class _Nullable __asm__("a0"), void *_Nullable __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 408))(class, bytes);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(Class _Nullable, void *_Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 406))(class, bytes);
#endif
}

void
objc_deinit()
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)())(((uintptr_t)ObjFWRTBase) - 414))();
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)())*(void **)(((uintptr_t)ObjFWRTBase) - 412))();
#endif
}

Ivar _Nullable *_Nullable
class_copyIvarList(Class _Nullable class, unsigned int *_Nullable outCount)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Ivar _Nullable *_Nullable (*)(Class _Nullable __asm__("a0"), unsigned int *_Nullable __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 420))(class, outCount);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((Ivar _Nullable *_Nullable (*)(Class _Nullable, unsigned int *_Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 418))(class, outCount);
#endif
}

const char *_Nonnull
ivar_getName(Ivar _Nonnull ivar)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((const char *_Nonnull (*)(Ivar _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 426))(ivar);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((const char *_Nonnull (*)(Ivar _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 424))(ivar);
#endif
}

const char *_Nonnull
ivar_getTypeEncoding(Ivar _Nonnull ivar)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((const char *_Nonnull (*)(Ivar _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 432))(ivar);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);


	return __extension__ ((const char *_Nonnull (*)(Ivar _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 430))(ivar);
#endif
}

ptrdiff_t
ivar_getOffset(Ivar _Nonnull ivar)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((ptrdiff_t (*)(Ivar _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 438))(ivar);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((ptrdiff_t (*)(Ivar _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 436))(ivar);
#endif
}

Method _Nullable *_Nullable
class_copyMethodList(Class _Nullable class, unsigned int *_Nullable outCount)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((Method _Nullable *_Nullable (*)(Class _Nullable __asm__("a0"), unsigned int *_Nullable __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 444))(class, outCount);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);



	return __extension__ ((Method _Nullable *_Nullable (*)(Class _Nullable, unsigned int *_Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 442))(class, outCount);
#endif
}

SEL _Nonnull
method_getName(Method _Nonnull method)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((SEL _Nonnull (*)(Method _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 450))(method);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((SEL _Nonnull (*)(Method _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 448))(method);
#endif
}

const char *_Nullable
method_getTypeEncoding(Method _Nonnull method)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((const char *_Nullable (*)(Method _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 456))(method);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((const char *_Nullable (*)(Method _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 454))(method);
#endif
}

objc_property_t _Nullable *_Nullable
class_copyPropertyList(Class _Nullable class, unsigned int *_Nullable outCount)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((objc_property_t _Nullable *_Nullable (*)(Class _Nullable __asm__("a0"), unsigned int *_Nullable __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 462))(class, outCount);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((objc_property_t _Nullable *_Nullable (*)(Class _Nullable, unsigned int *_Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 460))(class, outCount);
#endif
}

const char *_Nonnull
property_getName(objc_property_t _Nonnull property)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((const char *_Nonnull (*)(objc_property_t _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 468))(property);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((const char *_Nonnull (*)(objc_property_t _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 466))(property);
#endif
}

char *_Nullable
property_copyAttributeValue(objc_property_t _Nonnull property, const char *_Nonnull name)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((char *_Nullable (*)(objc_property_t _Nonnull __asm__("a0"), const char *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 474))(property, name);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((char *_Nullable (*)(objc_property_t _Nonnull, const char *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 472))(property, name);
#endif
}


void *_Nullable
objc_destructInstance(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((void *_Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 480))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((void *_Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 478))(object);
#endif
}

void *_Null_unspecified
objc_autoreleasePoolPush()
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((void *_Null_unspecified (*)())(((uintptr_t)ObjFWRTBase) - 486))();
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((void *_Null_unspecified (*)())*(void **)(((uintptr_t)ObjFWRTBase) - 484))();
#endif



}

void
objc_autoreleasePoolPop(void *_Null_unspecified pool)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(void *_Null_unspecified __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 492))(pool);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(void *_Null_unspecified))*(void **)(((uintptr_t)ObjFWRTBase) - 490))(pool);
#endif
}

id _Nullable
_objc_rootAutorelease(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 498))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 496))(object);
#endif
}

struct objc_hashtable *_Nonnull
objc_hashtable_new(objc_hashtable_hash_func hash, objc_hashtable_equal_func equal, uint32_t size)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((struct objc_hashtable *_Nonnull (*)(objc_hashtable_hash_func __asm__("a0"), objc_hashtable_equal_func __asm__("a1"), uint32_t __asm__("d0")))(((uintptr_t)ObjFWRTBase) - 504))(hash, equal, size);

#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((struct objc_hashtable *_Nonnull (*)(objc_hashtable_hash_func, objc_hashtable_equal_func, uint32_t))*(void **)(((uintptr_t)ObjFWRTBase) - 502))(hash, equal, size);
#endif
}

void
objc_hashtable_set(struct objc_hashtable *_Nonnull table, const void *_Nonnull key, const void *_Nonnull object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(struct objc_hashtable *_Nonnull __asm__("a0"), const void *_Nonnull __asm__("a1"), const void *_Nonnull __asm__("a2")))(((uintptr_t)ObjFWRTBase) - 510))(table, key, object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(struct objc_hashtable *_Nonnull, const void *_Nonnull, const void *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 508))(table, key, object);
#endif
}

void *_Nullable
objc_hashtable_get(struct objc_hashtable *_Nonnull table, const void *_Nonnull key)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((void *_Nullable (*)(struct objc_hashtable *_Nonnull __asm__("a0"), const void *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 516))(table, key);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((void *_Nullable (*)(struct objc_hashtable *_Nonnull, const void *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 514))(table, key);
#endif
}

void
objc_hashtable_delete(struct objc_hashtable *_Nonnull table, const void *_Nonnull key)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(struct objc_hashtable *_Nonnull __asm__("a0"), const void *_Nonnull __asm__("a1")))(((uintptr_t)ObjFWRTBase) - 522))(table, key);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(struct objc_hashtable *_Nonnull, const void *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 520))(table, key);
#endif
}

void
objc_hashtable_free(struct objc_hashtable *_Nonnull table)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(struct objc_hashtable *_Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 528))(table);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(struct objc_hashtable *_Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 526))(table);
#endif
}

void
objc_setTaggedPointerSecret(uintptr_t secret)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	((void (*)(uintptr_t __asm__("d0")))(((uintptr_t)ObjFWRTBase) - 534))(secret);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	__extension__ ((void (*)(uintptr_t))*(void **)(((uintptr_t)ObjFWRTBase) - 532))(secret);
#endif
}

int
objc_registerTaggedPointerClass(Class _Nonnull class)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((int (*)(Class _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 540))(class);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((int (*)(Class _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 538))(class);
#endif
}

bool
object_isTaggedPointer(id _Nullable object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((bool (*)(id _Nullable __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 546))(object);

#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (

	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((bool (*)(id _Nullable))*(void **)(((uintptr_t)ObjFWRTBase) - 544))(object);
#endif
}

uintptr_t
object_getTaggedPointerValue(id _Nonnull object)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((uintptr_t (*)(id _Nonnull __asm__("a0")))(((uintptr_t)ObjFWRTBase) - 552))(object);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((uintptr_t (*)(id _Nonnull))*(void **)(((uintptr_t)ObjFWRTBase) - 550))(object);
#endif
}

id _Nullable
objc_createTaggedPointer(int class, uintptr_t value)
{
#if defined(OF_AMIGAOS_M68K)
	register struct Library *a6 __asm__("a6") = ObjFWRTBase;
	(void)a6;
	return ((id _Nullable (*)(int __asm__("d0"), uintptr_t __asm__("d1")))(((uintptr_t)ObjFWRTBase) - 558))(class, value);
#elif defined(OF_MORPHOS)
	__asm__ __volatile__ (
	    "mr		%%r12, %0"
	    :: "r"(ObjFWRTBase) : "r12"
	);

	return __extension__ ((id _Nullable (*)(int, uintptr_t))*(void **)(((uintptr_t)ObjFWRTBase) - 556))(class, value);
#endif
}

Modified src/runtime/lookup-asm/Makefile from [a8b7579307] to [3c7b7f7fc1].

1
2
3

4
5
6
7
8
9
10

include ../../../extra.mk

STATIC_PIC_LIB_NOINST = ${LOOKUP_ASM_LIB_A}

STATIC_LIB_NOINST = ${LOOKUP_ASM_A}

SRCS = lookup-asm.S

include ../../../buildsys.mk

ASFLAGS += -I../../.. -I../..




>







>
1
2
3
4
5
6
7
8
9
10
11
12
include ../../../extra.mk

STATIC_PIC_LIB_NOINST = ${LOOKUP_ASM_LIB_A}
STATIC_AMIGA_LIB_NOINST = ${LOOKUP_ASM_AMIGALIB_A}
STATIC_LIB_NOINST = ${LOOKUP_ASM_A}

SRCS = lookup-asm.S

include ../../../buildsys.mk

ASFLAGS += -I../../.. -I../..
ASFLAGS_lookup-asm.amigalib.o += -DOF_BASEREL

Modified src/runtime/lookup-asm/lookup-asm-arm-elf.S from [fae6543de8] to [f0e5b05f4e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	cmp	r0, #0
	beq	ret_nil

	tst	r0, #1
	bne	.Ltagged_pointer_\name

	ldr	r2, [r0, #0]
	ldr	r2, [r2, #32]

.Lmain_\name:
#ifndef OF_BIG_ENDIAN
# ifdef OF_SELUID24







|


|


|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	cmp	r0, #0
	beq	returnNilMethod

	tst	r0, #1
	bne	.LtaggedPointer_\name

	ldr	r2, [r0, #0]
	ldr	r2, [r2, #32]

.Lmain_\name:
#ifndef OF_BIG_ENDIAN
# ifdef OF_SELUID24
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
	ldrb	r3, [r1, #2]
	ldr	r2, [r2, r3, lsl #2]
	ldrb	r3, [r1, #3]
	ldr	r2, [r2, r3, lsl #2]
#endif

	cmp	r2, #0
	beq	\not_found(PLT)

	mov	r0, r2
	bx	lr

.Ltagged_pointer_\name:
	ldr	r2, .Lgot$indirect_.Ltagged_pointer_\name
	add	r2, pc, r2

	ldr	r3, .Lgot$indirect_.Ltagged_pointer_\name+4
	ldr	r3, [r2, r3]
	ldr	r3, [r3]
	eor	r0, r0, r3
	and	r0, r0, #0xE
	lsl	r0, r0, #1

	ldr	r3, .Lgot$indirect_.Ltagged_pointer_\name+8
	ldr	r3, [r2, r3]
	ldr	r2, [r3, r0]
	ldr	r2, [r2, #32]

	b	.Lmain_\name
.type \name, %function
.size \name, .-\name

.Lgot$indirect_.Ltagged_pointer_\name:
	.long	_GLOBAL_OFFSET_TABLE_-(.Ltagged_pointer_\name+12)
	.long	objc_tagged_pointer_secret(GOT)
	.long	objc_tagged_pointer_classes(GOT)
.endm

.macro generate_lookup_super name lookup
\name:
	mov	r2, r0
	ldr	r0, [r0, #0]
	cmp	r0, #0
	beq	ret_nil

	ldr	r2, [r2, #4]
	ldr	r2, [r2, #32]

	b	.Lmain_\lookup
.type \name, %function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

ret_nil:
	adr	r0, nil_method
	bx	lr

nil_method:
	mov	r0, #0
	bx	lr

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif







|




|
|


|






|








|
|
|
|


|




|









|
|
|
|

|
|


|






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
	ldrb	r3, [r1, #2]
	ldr	r2, [r2, r3, lsl #2]
	ldrb	r3, [r1, #3]
	ldr	r2, [r2, r3, lsl #2]
#endif

	cmp	r2, #0
	beq	\notFound(PLT)

	mov	r0, r2
	bx	lr

.LtaggedPointer_\name:
	ldr	r2, .Lgot$indirect_.LtaggedPointer_\name
	add	r2, pc, r2

	ldr	r3, .Lgot$indirect_.LtaggedPointer_\name+4
	ldr	r3, [r2, r3]
	ldr	r3, [r3]
	eor	r0, r0, r3
	and	r0, r0, #0xE
	lsl	r0, r0, #1

	ldr	r3, .Lgot$indirect_.LtaggedPointer_\name+8
	ldr	r3, [r2, r3]
	ldr	r2, [r3, r0]
	ldr	r2, [r2, #32]

	b	.Lmain_\name
.type \name, %function
.size \name, .-\name

.Lgot$indirect_.LtaggedPointer_\name:
	.long	_GLOBAL_OFFSET_TABLE_-(.LtaggedPointer_\name+12)
	.long	objc_taggedPointerSecret(GOT)
	.long	objc_taggedPointerClasses(GOT)
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	mov	r2, r0
	ldr	r0, [r0, #0]
	cmp	r0, #0
	beq	returnNilMethod

	ldr	r2, [r2, #4]
	ldr	r2, [r2, #32]

	b	.Lmain_\lookup
.type \name, %function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

returnNilMethod:
	adr	r0, nilMethod
	bx	lr

nilMethod:
	mov	r0, #0
	bx	lr

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-arm64-elf.S from [a8a0a8247a] to [ad3c36fa42].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	cbz	x0, ret_nil

	tst	x0, #1
	b.ne	.Ltagged_pointer_\name

	ldr	x2, [x0]
	ldr	x2, [x2, #64]

.Lmain_\name:
#ifdef OF_SELUID24
	ldrb	w3, [x1, #2]
	ldr	x2, [x2, x3, lsl #3]
#endif
	ldrb	w3, [x1, #1]
	ldr	x2, [x2, x3, lsl #3]
	ldrb	w3, [x1]
	ldr	x2, [x2, x3, lsl #3]

	cbz	x2, \not_found

	mov	x0, x2
	ret

.Ltagged_pointer_\name:
	adrp	x2, :got:objc_tagged_pointer_secret
	ldr	x2, [x2, #:got_lo12:objc_tagged_pointer_secret]
	ldr	x2, [x2]
	eor	x0, x0, x2
	and	x0, x0, #0xE
	lsl	x0, x0, #2

	adrp	x2, :got:objc_tagged_pointer_classes
	ldr	x2, [x2, #:got_lo12:objc_tagged_pointer_classes]
	ldr	x2, [x2, x0]
	ldr	x2, [x2, #64]

	b	.Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro generate_lookup_super name lookup
\name:
	mov	x2, x0
	ldr	x0, [x0]
	cbz	x0, ret_nil

	ldr	x2, [x2, #8]
	ldr	x2, [x2, #64]

	b	.Lmain_\lookup
.type \name, %function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

ret_nil:
	adr	x0, nil_method
	ret

nil_method:
	mov	x0, #0
	ret

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif







|

|


|














|




|
|
|





|
|








|



|









|
|
|
|

|
|


|






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

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	cbz	x0, returnNilMethod

	tst	x0, #1
	b.ne	.LtaggedPointer_\name

	ldr	x2, [x0]
	ldr	x2, [x2, #64]

.Lmain_\name:
#ifdef OF_SELUID24
	ldrb	w3, [x1, #2]
	ldr	x2, [x2, x3, lsl #3]
#endif
	ldrb	w3, [x1, #1]
	ldr	x2, [x2, x3, lsl #3]
	ldrb	w3, [x1]
	ldr	x2, [x2, x3, lsl #3]

	cbz	x2, \notFound

	mov	x0, x2
	ret

.LtaggedPointer_\name:
	adrp	x2, :got:objc_taggedPointerSecret
	ldr	x2, [x2, #:got_lo12:objc_taggedPointerSecret]
	ldr	x2, [x2]
	eor	x0, x0, x2
	and	x0, x0, #0xE
	lsl	x0, x0, #2

	adrp	x2, :got:objc_taggedPointerClasses
	ldr	x2, [x2, #:got_lo12:objc_taggedPointerClasses]
	ldr	x2, [x2, x0]
	ldr	x2, [x2, #64]

	b	.Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	mov	x2, x0
	ldr	x0, [x0]
	cbz	x0, returnNilMethod

	ldr	x2, [x2, #8]
	ldr	x2, [x2, #64]

	b	.Lmain_\lookup
.type \name, %function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

returnNilMethod:
	adr	x0, nilMethod
	ret

nilMethod:
	mov	x0, #0
	ret

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-mips-elf.S from [87115a51c3] to [c727ce9d5a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	beqz	$a0, 0f

	andi	$t0, $a0, 1
	bnez	$t0, .Ltagged_pointer_\name

	lw	$t0, 0($a0)
	lw	$t0, 32($t0)

.Lmain_\name:
#ifdef OF_BIG_ENDIAN
# ifdef OF_SELUID24







|




|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	beqz	$a0, 0f

	andi	$t0, $a0, 1
	bnez	$t0, .LtaggedPointer_\name

	lw	$t0, 0($a0)
	lw	$t0, 32($t0)

.Lmain_\name:
#ifdef OF_BIG_ENDIAN
# ifdef OF_SELUID24
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
	lw	$t0, 0($t0)
	addu	$t0, $t0, $t3
	lw	$t0, 0($t0)

#ifdef OF_PIC
	beqz	$t0, 1f
#else
	beqz	$t0, \not_found
#endif

	move	$v0, $t0
	jr	$ra

0:
#ifdef OF_PIC
	addiu	$v0, $t9, nil_method-\name
#else
	la	$v0, nil_method
#endif
	jr	$ra

#ifdef OF_PIC
1:
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9
	addiu	$gp, $gp, 1b-\name

	lw	$t9, %call16(\not_found)($gp)
	jr	$t9
#endif

.Ltagged_pointer_\name:
#ifdef OF_PIC
0:
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9
	addiu	$gp, $gp, 0b-\name

	lw	$t0, %got(objc_tagged_pointer_secret)($gp)
#else
	la	$t0, objc_tagged_pointer_secret
#endif
	lw	$t0, 0($t0)
	xor	$t0, $a0, $t0
	and	$t0, $t0, 0xE
	sll	$t0, $t0, 1

#ifdef OF_PIC
	lw	$t1, %got(objc_tagged_pointer_classes)($gp)
#else
	la	$t1, objc_tagged_pointer_classes
#endif
	addu	$t0, $t1, $t0
	ld	$t0, ($t0)
	ld	$t0, 32($t0)

	b	.Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro generate_lookup_super name lookup
\name:
	move	$t0, $a0
	lw	$a0, 0($a0)
	beqz	$a0, 0f

	lw	$t0, 4($t0)
	lw	$t0, 32($t0)

	addiu	$t9, $t9, \lookup-\name
	b	.Lmain_\lookup

0:
#ifdef OF_PIC
	addiu	$v0, $t9, nil_method-\name
#else
	la	$v0, nil_method
#endif
	jr	$ra
.type \name, %function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

nil_method:
	move	$v0, $zero
	jr	$ra

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif







|







|

|










|



|







|

|







|

|










|













|

|






|
|
|
|

|






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
	lw	$t0, 0($t0)
	addu	$t0, $t0, $t3
	lw	$t0, 0($t0)

#ifdef OF_PIC
	beqz	$t0, 1f
#else
	beqz	$t0, \notFound
#endif

	move	$v0, $t0
	jr	$ra

0:
#ifdef OF_PIC
	addiu	$v0, $t9, nilMethod-\name
#else
	la	$v0, nilMethod
#endif
	jr	$ra

#ifdef OF_PIC
1:
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9
	addiu	$gp, $gp, 1b-\name

	lw	$t9, %call16(\notFound)($gp)
	jr	$t9
#endif

.LtaggedPointer_\name:
#ifdef OF_PIC
0:
	lui	$gp, %hi(_gp_disp)
	addiu	$gp, $gp, %lo(_gp_disp)
	addu	$gp, $gp, $t9
	addiu	$gp, $gp, 0b-\name

	lw	$t0, %got(objc_taggedPointerSecret)($gp)
#else
	la	$t0, objc_taggedPointerSecret
#endif
	lw	$t0, 0($t0)
	xor	$t0, $a0, $t0
	and	$t0, $t0, 0xE
	sll	$t0, $t0, 1

#ifdef OF_PIC
	lw	$t1, %got(objc_taggedPointerClasses)($gp)
#else
	la	$t1, objc_taggedPointerClasses
#endif
	addu	$t0, $t1, $t0
	ld	$t0, ($t0)
	ld	$t0, 32($t0)

	b	.Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	move	$t0, $a0
	lw	$a0, 0($a0)
	beqz	$a0, 0f

	lw	$t0, 4($t0)
	lw	$t0, 32($t0)

	addiu	$t9, $t9, \lookup-\name
	b	.Lmain_\lookup

0:
#ifdef OF_PIC
	addiu	$v0, $t9, nilMethod-\name
#else
	la	$v0, nilMethod
#endif
	jr	$ra
.type \name, %function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

nilMethod:
	move	$v0, $zero
	jr	$ra

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-mips64-n64-elf.S from [3b31980e08] to [53637868a6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	beqz	$a0, 0f

	andi	$t0, $a0, 1
	bnez	$t0, .Ltagged_pointer_\name

	ld	$t0, ($a0)
	ld	$t0, 64($t0)

.Lmain_\name:
#ifdef OF_BIG_ENDIAN
# ifdef OF_SELUID24







|




|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	beqz	$a0, 0f

	andi	$t0, $a0, 1
	bnez	$t0, .LtaggedPointer_\name

	ld	$t0, ($a0)
	ld	$t0, 64($t0)

.Lmain_\name:
#ifdef OF_BIG_ENDIAN
# ifdef OF_SELUID24
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
	move	$v0, $t0
	jr	$ra

0:
	lui	$v0, %hi(%neg(%gp_rel(\name)))
	daddiu	$v0, $v0, %lo(%neg(%gp_rel(\name)))
	daddu	$v0, $v0, $t9
	ld	$v0, %got_disp(nil_method)($v0)
	jr	$ra

1:
	lui	$t0, %hi(%neg(%gp_rel(\name)))
	daddiu	$t0, $t0, %lo(%neg(%gp_rel(\name)))
	daddu	$t0, $t0, $t9
	ld	$t9, %got_disp(\not_found)($t0)
	jr	$t9

.Ltagged_pointer_\name:
	lui	$t0, %hi(%neg(%gp_rel(\name)))
	daddiu	$t0, $t0, %lo(%neg(%gp_rel(\name)))
	daddu	$t0, $t0, $t9

	ld	$t1, %got_disp(objc_tagged_pointer_secret)($t0)
	ld	$t1, 0($t1)
	xor	$t1, $a0, $t1
	and	$t1, $t1, 0xE
	dsll	$t1, $t1, 2

	ld	$t0, %got_disp(objc_tagged_pointer_classes)($t0)
	daddu	$t0, $t0, $t1
	ld	$t0, ($t0)
	ld	$t0, 64($t0)

	b	.Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro generate_lookup_super name lookup
\name:
	move	$t0, $a0
	ld	$a0, ($a0)
	beqz	$a0, 0f

	ld	$t0, 8($t0)
	ld	$t0, 64($t0)

	daddiu	$t9, $t9, \lookup-\name
	b	.Lmain_\lookup

0:
	lui	$v0, %hi(%neg(%gp_rel(\name)))
	daddiu	$v0, $v0, %lo(%neg(%gp_rel(\name)))
	daddu	$v0, $v0, $t9
	ld	$v0, %got_disp(nil_method)($v0)
	jr	$ra
.type \name, %function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

nil_method:
	move	$v0, $zero
	jr	$ra

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif







|






|


|




|





|









|















|





|
|
|
|

|






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
	move	$v0, $t0
	jr	$ra

0:
	lui	$v0, %hi(%neg(%gp_rel(\name)))
	daddiu	$v0, $v0, %lo(%neg(%gp_rel(\name)))
	daddu	$v0, $v0, $t9
	ld	$v0, %got_disp(nilMethod)($v0)
	jr	$ra

1:
	lui	$t0, %hi(%neg(%gp_rel(\name)))
	daddiu	$t0, $t0, %lo(%neg(%gp_rel(\name)))
	daddu	$t0, $t0, $t9
	ld	$t9, %got_disp(\notFound)($t0)
	jr	$t9

.LtaggedPointer_\name:
	lui	$t0, %hi(%neg(%gp_rel(\name)))
	daddiu	$t0, $t0, %lo(%neg(%gp_rel(\name)))
	daddu	$t0, $t0, $t9

	ld	$t1, %got_disp(objc_taggedPointerSecret)($t0)
	ld	$t1, 0($t1)
	xor	$t1, $a0, $t1
	and	$t1, $t1, 0xE
	dsll	$t1, $t1, 2

	ld	$t0, %got_disp(objc_taggedPointerClasses)($t0)
	daddu	$t0, $t0, $t1
	ld	$t0, ($t0)
	ld	$t0, 64($t0)

	b	.Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	move	$t0, $a0
	ld	$a0, ($a0)
	beqz	$a0, 0f

	ld	$t0, 8($t0)
	ld	$t0, 64($t0)

	daddiu	$t9, $t9, \lookup-\name
	b	.Lmain_\lookup

0:
	lui	$v0, %hi(%neg(%gp_rel(\name)))
	daddiu	$v0, $v0, %lo(%neg(%gp_rel(\name)))
	daddu	$v0, $v0, $t9
	ld	$v0, %got_disp(nilMethod)($v0)
	jr	$ra
.type \name, %function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

nilMethod:
	move	$v0, $zero
	jr	$ra

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-powerpc-elf.S from [6a0efd2f1b] to [134bfef460].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	cmpwi	%r3, 0
	beq-	ret_nil

	andi.	%r0, %r3, 1
	bne-	.Ltagged_pointer_\name

	lwz	%r5, 0(%r3)
	lwz	%r5, 32(%r5)

.Lmain_\name:
	lwz	%r8, 0(%r4)
#ifdef OF_SELUID24







|


|


|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	cmpwi	%r3, 0
	beq-	returnNilMethod

	andi.	%r0, %r3, 1
	bne-	.LtaggedPointer_\name

	lwz	%r5, 0(%r3)
	lwz	%r5, 32(%r5)

.Lmain_\name:
	lwz	%r8, 0(%r4)
#ifdef OF_SELUID24
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
	cmpwi	%r5, 0
	beq-	0f

	mr	%r3, %r5
	blr

0:

	stwu	%r1, -16(%r1)
	mflr	%r0	
	stw	%r0, 20(%r1)
	stw	%r30, 8(%r1)

	bl	0f
0:
	mflr	%r30
	addis	%r30, %r30, .Lbiased_got2-0b@ha
	addi	%r30, %r30, .Lbiased_got2-0b@l

	lwz	%r0, .Lgot_\not_found-.Lbiased_got2(%r30)
	mtctr	%r0

	lwz	%r30, 8(%r1)
	lwz	%r0, 20(%r1)
	addi	%r1, %r1, 16
	mtlr	%r0

	bctr




.Ltagged_pointer_\name:

	mflr	%r7
	bl	0f
0:
	mflr	%r6
	mtlr	%r7
	addis	%r6, %r6, .Lbiased_got2-0b@ha
	addi	%r6, %r6, .Lbiased_got2-0b@l

	lwz	%r5, .Lgot_objc_tagged_pointer_secret-.Lbiased_got2(%r6)
	lwz	%r5, 0(%r5)







	xor	%r5, %r3, %r5
	rlwinm	%r5, %r5, 1, 0x1C


	lwz	%r6, .Lgot_objc_tagged_pointer_classes-.Lbiased_got2(%r6)







	lwzx	%r5, %r6, %r5
	lwz	%r5, 32(%r5)

	b	.Lmain_\name
.type \name, @function
.size \name, .-\name
.endm

.macro generate_lookup_super name lookup
\name:
	mr	%r5, %r3
	lwz	%r3, 0(%r3)
	cmpwi	%r3, 0
	beq-	ret_nil

	lwz	%r5, 4(%r5)
	lwz	%r5, 32(%r5)

	b	.Lmain_\lookup
.type \name, @function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

ret_nil:
	mflr	%r0
	bl	get_pc
	mtlr	%r0
0:
	addi	%r3, %r3, nil_method-0b
	blr

nil_method:
	li	%r3, 0
	blr

get_pc:
	mflr	%r3
	blr


.section .got2, "aw"
.Lbiased_got2 = .+0x8000
.Lgot_objc_method_not_found:
	.long objc_method_not_found
.Lgot_objc_method_not_found_stret:
	.long objc_method_not_found_stret
.Lgot_objc_tagged_pointer_secret:
	.long objc_tagged_pointer_secret
.Lgot_objc_tagged_pointer_classes:
	.long objc_tagged_pointer_classes


#ifdef OF_LINUX
.section .note.GNU-stack, "", @progbits
#endif







>











|








>
>
>

|
>








|

>
>
>
>
>
>
>



>
|
>
>
>
>
>
>
>








|




|









|
|
|
|

|

|


|


|



|



>


|
|
|
|
|
|
|
|
>




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
	cmpwi	%r5, 0
	beq-	0f

	mr	%r3, %r5
	blr

0:
#ifdef OF_PIC
	stwu	%r1, -16(%r1)
	mflr	%r0	
	stw	%r0, 20(%r1)
	stw	%r30, 8(%r1)

	bl	0f
0:
	mflr	%r30
	addis	%r30, %r30, .Lbiased_got2-0b@ha
	addi	%r30, %r30, .Lbiased_got2-0b@l

	lwz	%r0, .Lgot_\notFound-.Lbiased_got2(%r30)
	mtctr	%r0

	lwz	%r30, 8(%r1)
	lwz	%r0, 20(%r1)
	addi	%r1, %r1, 16
	mtlr	%r0

	bctr
#else
	b	\notFound
#endif

.LtaggedPointer_\name:
#if defined(OF_PIC)
	mflr	%r7
	bl	0f
0:
	mflr	%r6
	mtlr	%r7
	addis	%r6, %r6, .Lbiased_got2-0b@ha
	addi	%r6, %r6, .Lbiased_got2-0b@l

	lwz	%r5, .Lgot_objc_taggedPointerSecret-.Lbiased_got2(%r6)
	lwz	%r5, 0(%r5)
#elif defined(OF_BASEREL)
	addis	%r5, %r13, objc_taggedPointerSecret@drel@ha
	lwz	%r5, objc_taggedPointerSecret@drel@l(%r5)
#else
	lis	%r5, objc_taggedPointerSecret@ha
	lwz	%r5, objc_taggedPointerSecret@l(%r5)
#endif
	xor	%r5, %r3, %r5
	rlwinm	%r5, %r5, 1, 0x1C

#if defined(OF_PIC)
	lwz	%r6, .Lgot_objc_taggedPointerClasses-.Lbiased_got2(%r6)
#elif defined(OF_BASEREL)
	addis	%r6, %r13, objc_taggedPointerClasses@drel@ha
	addi	%r6, %r6, objc_taggedPointerClasses@drel@l
#else
	lis	%r6, objc_taggedPointerClasses@ha
	addi	%r6, %r6, objc_taggedPointerClasses@l
#endif
	lwzx	%r5, %r6, %r5
	lwz	%r5, 32(%r5)

	b	.Lmain_\name
.type \name, @function
.size \name, .-\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	mr	%r5, %r3
	lwz	%r3, 0(%r3)
	cmpwi	%r3, 0
	beq-	returnNilMethod

	lwz	%r5, 4(%r5)
	lwz	%r5, 32(%r5)

	b	.Lmain_\lookup
.type \name, @function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

returnNilMethod:
	mflr	%r0
	bl	getPC
	mtlr	%r0
0:
	addi	%r3, %r3, nilMethod-0b
	blr

nilMethod:
	li	%r3, 0
	blr

getPC:
	mflr	%r3
	blr

#ifdef OF_PIC
.section .got2, "aw"
.Lbiased_got2 = .+0x8000
.Lgot_objc_methodNotFound:
	.long objc_methodNotFound
.Lgot_objc_methodNotFound_stret:
	.long objc_methodNotFound_stret
.Lgot_objc_taggedPointerSecret:
	.long objc_taggedPointerSecret
.Lgot_objc_taggedPointerClasses:
	.long objc_taggedPointerClasses
#endif

#ifdef OF_LINUX
.section .note.GNU-stack, "", @progbits
#endif

Added src/runtime/lookup-asm/lookup-asm-powerpc64-elf.S version [8516ac090f].







































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "platform.h"

#if defined(_CALL_ELF) && _CALL_ELF == 2
.abiversion 2
#endif

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
#if defined(_CALL_ELF) && _CALL_ELF == 2
\name:
	addis	%r2, %r12, .TOC.-\name@ha
	addi	%r2, %r2, .TOC.-\name@l
.localentry \name, .-\name
#else
.section .opd, "aw", @progbits
\name:
	.p2align 3
	.quad .Lbegin_\name
	.quad .TOC.@tocbase
	.quad 0
.section .text
#endif
.Lbegin_\name:
	cmpdi	%r3, 0
	beq-	.LreturnNilMethod

	andi.	%r0, %r3, 1
	bne-	.LtaggedPointer_\name

	ld	%r5, 0(%r3)
	ld	%r5, 64(%r5)

.Lmain_\name:
	ld	%r8, 0(%r4)
#ifdef OF_SELUID24
	rlwinm	%r6, %r8, 19, 0x7F8
#endif
	rlwinm	%r7, %r8, 27, 0x7F8
	rlwinm	%r8, %r8, 3, 0x7F8

#ifdef OF_SELUID24
	ldx	%r5, %r5, %r6
#endif
	ldx	%r5, %r5, %r7
	ldx	%r5, %r5, %r8

	cmpdi	%r5, 0
	beq-	0f

	mr	%r3, %r5
	blr

0:
	mflr	%r0
	std	%r0, 16(%r1)
	stdu	%r1, -112(%r1)
	bl	\notFound
	nop
	addi	%r1, %r1, 112
	ld	%r0, 16(%r1)
	mtlr	%r0
	blr

.LtaggedPointer_\name:
	addis	%r5, %r2, objc_taggedPointerSecret@toc@ha
	ld	%r5, objc_taggedPointerSecret@toc@l(%r5)
	xor	%r5, %r3, %r5
	rlwinm	%r5, %r5, 2, 0x38

	addis	%r6, %r2, objc_taggedPointerClasses@toc@ha
	addi	%r6, %r6, objc_taggedPointerClasses@toc@l
	ldx	%r5, %r6, %r5
	ld	%r5, 64(%r5)

	b	.Lmain_\name
.type \name, @function
.size \name, .-.Lbegin_\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
#if defined(_CALL_ELF) && _CALL_ELF == 2
\name:
	addis	%r2, %r12, .TOC.-\name@ha
	addi	%r2, %r2, .TOC.-\name@l
.localentry \name, .-\name
#else
.section .opd, "aw", @progbits
\name:
	.p2align 3
	.quad .Lbegin_\name
	.quad .TOC.@tocbase
	.quad 0
.section .text
#endif
.Lbegin_\name:
	mr	%r5, %r3
	ld	%r3, 0(%r3)
	cmpdi	%r3, 0
	beq-	.LreturnNilMethod

	ld	%r5, 8(%r5)
	ld	%r5, 64(%r5)

	b	.Lmain_\lookup
.type \name, @function
.size \name, .-.Lbegin_\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

.LreturnNilMethod:
	addis	%r3, %r2, nilMethod@toc@ha
	addi	%r3, %r3, nilMethod@toc@l
	blr

#if defined(_CALL_ELF) && _CALL_ELF == 2
nilMethod:
	addis	%r2, %r12, .TOC.-nilMethod@ha
	addi	%r2, %r2, .TOC.-nilMethod@l
.localentry nilMethod, .-nilMethod
#else
.section .opd, "aw", @progbits
nilMethod:
	.p2align 3
	.quad .Lbegin_nilMethod
	.quad .TOC.@tocbase
	.quad 0
.section .text
#endif
.Lbegin_nilMethod:
	li	%r3, 0
	blr
.type nilMethod, @function
.size nilMethod, .-.Lbegin_nilMethod

#ifdef OF_LINUX
.section .note.GNU-stack, "", @progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-sparc-elf.S from [ee867480c0] to [52b9093b5b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	tst	%o0
	bz	ret_nil
	 btst	1, %o0
	bnz	.Ltagged_pointer_\name	
	 nop

	ld	[%o0], %o2
	ld	[%o2 + 32], %o2

.Lmain_\name:
#ifdef OF_SELUID24







|


|

|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	tst	%o0
	bz	returnNilMethod
	 btst	1, %o0
	bnz	.LtaggedPointer_\name	
	 nop

	ld	[%o0], %o2
	ld	[%o2 + 32], %o2

.Lmain_\name:
#ifdef OF_SELUID24
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
	 nop

	retl
	 mov	%o2, %o0

0:
	mov	%o7, %g1
	call	\not_found
	 mov	%g1, %o7

.Ltagged_pointer_\name:
#ifdef OF_PIC
	mov	%o7, %g1
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %o3
	call	0f
	 or	%o3, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o3
0:
	add	%o7, %o3, %o3
	mov	%g1, %o7
#endif

	sethi	%hi(objc_tagged_pointer_secret), %o2
	or	%o2, %lo(objc_tagged_pointer_secret), %o2
#ifdef OF_PIC
	ld	[%o3 + %o2], %o2
#endif
	ld	[%o2], %o2
	xor	%o0, %o2, %o0
	and	%o0, 0xE, %o0
	sll	%o0, 1, %o0

	sethi	%hi(objc_tagged_pointer_classes), %o2
	or	%o2, %lo(objc_tagged_pointer_classes), %o2
#ifdef OF_PIC
	ld	[%o3 + %o2], %o2
#endif

	ld	[%o2 + %o0], %o2
	ba	.Lmain_\name
	 ld	[%o2 + 32], %o2
.type \name, %function
.size \name, .-\name
.endm

.macro generate_lookup_super name lookup
\name:
	mov	%o0, %o2
	ld	[%o0], %o0
	cmp	%o0, 0
	be	ret_nil
	 nop

	ld	[%o2 + 4], %o2
	ba	.Lmain_\lookup
	 ld	[%o2 + 32], %o2
.type \name, %function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

ret_nil:
#ifdef OF_PIC
	mov	%o7, %g1

	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %o1
	call	0f
	 add	%o1, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o1
0:
	add	%o7, %o1, %o1

	sethi	%hi(nil_method), %o0
	or	%o0, %lo(nil_method), %o0

	jmpl	%g1 + 8, %g0
	 ld	[%o1 + %o0], %o0
#else
	sethi	%hi(nil_method), %o0
	retl
	 or	%o0, %lo(nil_method), %o0
#endif

nil_method:
	retl
	 clr	%o0

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif







|


|










|
|








|
|











|




|









|
|
|
|

|









|
|




|

|


|






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
	 nop

	retl
	 mov	%o2, %o0

0:
	mov	%o7, %g1
	call	\notFound
	 mov	%g1, %o7

.LtaggedPointer_\name:
#ifdef OF_PIC
	mov	%o7, %g1
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %o3
	call	0f
	 or	%o3, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o3
0:
	add	%o7, %o3, %o3
	mov	%g1, %o7
#endif

	sethi	%hi(objc_taggedPointerSecret), %o2
	or	%o2, %lo(objc_taggedPointerSecret), %o2
#ifdef OF_PIC
	ld	[%o3 + %o2], %o2
#endif
	ld	[%o2], %o2
	xor	%o0, %o2, %o0
	and	%o0, 0xE, %o0
	sll	%o0, 1, %o0

	sethi	%hi(objc_taggedPointerClasses), %o2
	or	%o2, %lo(objc_taggedPointerClasses), %o2
#ifdef OF_PIC
	ld	[%o3 + %o2], %o2
#endif

	ld	[%o2 + %o0], %o2
	ba	.Lmain_\name
	 ld	[%o2 + 32], %o2
.type \name, %function
.size \name, .-\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	mov	%o0, %o2
	ld	[%o0], %o0
	cmp	%o0, 0
	be	returnNilMethod
	 nop

	ld	[%o2 + 4], %o2
	ba	.Lmain_\lookup
	 ld	[%o2 + 32], %o2
.type \name, %function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

returnNilMethod:
#ifdef OF_PIC
	mov	%o7, %g1

	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %o1
	call	0f
	 add	%o1, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o1
0:
	add	%o7, %o1, %o1

	sethi	%hi(nilMethod), %o0
	or	%o0, %lo(nilMethod), %o0

	jmpl	%g1 + 8, %g0
	 ld	[%o1 + %o0], %o0
#else
	sethi	%hi(nilMethod), %o0
	retl
	 or	%o0, %lo(nilMethod), %o0
#endif

nilMethod:
	retl
	 clr	%o0

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-sparc64-elf.S from [b56d827aa1] to [1e61b50ef6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	brz,pn	%o0, ret_nil
	 and	%o0, 1, %o2
	brnz,pn	%o2, .Ltagged_pointer_\name
	 nop

	ldx	[%o0], %o2
	ldx	[%o2 + 64], %o2

.Lmain_\name:
#ifdef OF_SELUID24







|

|

|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	brz,pn	%o0, returnNilMethod
	 and	%o0, 1, %o2
	brnz,pn	%o2, .LtaggedPointer_\name
	 nop

	ldx	[%o0], %o2
	ldx	[%o2 + 64], %o2

.Lmain_\name:
#ifdef OF_SELUID24
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
	 nop

	retl
	 mov	%o2, %o0

0:
	mov	%o7, %g1
	call	\not_found
	 mov	%g1, %o7

.Ltagged_pointer_\name:
#ifdef OF_PIC
	mov	%o7, %g1
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %o3
	call	0f
	 or	%o3, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o3
0:
	add	%o7, %o3, %o3
	mov	%g1, %o7
#endif

	sethi	%hi(objc_tagged_pointer_secret), %o2
	or	%o2, %lo(objc_tagged_pointer_secret), %o2
#ifdef OF_PIC
	ldx	[%o3 + %o2], %o2
#endif
	ldx	[%o2], %o2
	xor	%o0, %o2, %o0
	and	%o0, 0xE, %o0
	sll	%o0, 2, %o0

	sethi	%hi(objc_tagged_pointer_classes), %o2
	or	%o2, %lo(objc_tagged_pointer_classes), %o2
#ifdef OF_PIC
	ldx	[%o3 + %o2], %o2
#endif

	ldx	[%o2 + %o0], %o2
	ba	.Lmain_\name
	 ldx	[%o2 + 64], %o2
.type \name, %function
.size \name, .-\name
.endm

.macro generate_lookup_super name lookup
\name:
	mov	%o0, %o2
	ldx	[%o0], %o0
	brz,pn	%o0, ret_nil
	 nop

	ldx	[%o2 + 8], %o2
	ba	.Lmain_\lookup
	 ldx	[%o2 + 64], %o2
.type \name, %function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

ret_nil:
#ifdef OF_PIC
	mov	%o7, %g1

	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %o1
	call	0f
	 or	%o1, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o1
0:
	add	%o7, %o1, %o1

	sethi	%hi(nil_method), %o0
	or	%o0, %lo(nil_method), %o0

	jmpl	%g1 + 8, %g0
	 ldx	[%o1 + %o0], %o0
#else
	sethi	%hi(nil_method), %o0
	retl
	 or	%o0, %lo(nil_method), %o0
#endif

nil_method:
	retl
	 clr	%o0

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif







|


|










|
|








|
|











|



|









|
|
|
|

|









|
|




|

|


|






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
	 nop

	retl
	 mov	%o2, %o0

0:
	mov	%o7, %g1
	call	\notFound
	 mov	%g1, %o7

.LtaggedPointer_\name:
#ifdef OF_PIC
	mov	%o7, %g1
	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %o3
	call	0f
	 or	%o3, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o3
0:
	add	%o7, %o3, %o3
	mov	%g1, %o7
#endif

	sethi	%hi(objc_taggedPointerSecret), %o2
	or	%o2, %lo(objc_taggedPointerSecret), %o2
#ifdef OF_PIC
	ldx	[%o3 + %o2], %o2
#endif
	ldx	[%o2], %o2
	xor	%o0, %o2, %o0
	and	%o0, 0xE, %o0
	sll	%o0, 2, %o0

	sethi	%hi(objc_taggedPointerClasses), %o2
	or	%o2, %lo(objc_taggedPointerClasses), %o2
#ifdef OF_PIC
	ldx	[%o3 + %o2], %o2
#endif

	ldx	[%o2 + %o0], %o2
	ba	.Lmain_\name
	 ldx	[%o2 + 64], %o2
.type \name, %function
.size \name, .-\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	mov	%o0, %o2
	ldx	[%o0], %o0
	brz,pn	%o0, returnNilMethod
	 nop

	ldx	[%o2 + 8], %o2
	ba	.Lmain_\lookup
	 ldx	[%o2 + 64], %o2
.type \name, %function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

returnNilMethod:
#ifdef OF_PIC
	mov	%o7, %g1

	sethi	%hi(_GLOBAL_OFFSET_TABLE_ - 4), %o1
	call	0f
	 or	%o1, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o1
0:
	add	%o7, %o1, %o1

	sethi	%hi(nilMethod), %o0
	or	%o0, %lo(nilMethod), %o0

	jmpl	%g1 + 8, %g0
	 ldx	[%o1 + %o0], %o0
#else
	sethi	%hi(nilMethod), %o0
	retl
	 or	%o0, %lo(nilMethod), %o0
#endif

nilMethod:
	retl
	 clr	%o0

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-x86-elf.S from [3e4a512a7a] to [fe04c47c7f].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.intel_syntax noprefix

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	mov	edx, [esp+4]
	test	edx, edx
	jz	short ret_nil

	bt	edx, 0
	jc	short .Ltagged_pointer_\name

	mov	edx, [edx]
	mov	edx, [edx+32]

.Lmain_\name:
	mov	eax, [esp+8]

#ifdef OF_SELUID24
	movzx	ecx, byte ptr [eax+2]
	mov	edx, [edx+ecx*4]
#endif
	movzx	ecx, byte ptr [eax+1]
	mov	edx, [edx+ecx*4]
	movzx	ecx, byte ptr [eax]
	mov	eax, [edx+ecx*4]

	test	eax, eax
	jz	short 0f

	ret

0:
	call	get_eip
	add	eax, offset _GLOBAL_OFFSET_TABLE_
	lea	eax, [eax+\not_found@GOTOFF]
	jmp	eax

.Ltagged_pointer_\name:
	call	get_eip
	add	eax, offset _GLOBAL_OFFSET_TABLE_

	lea	ecx, [eax+objc_tagged_pointer_secret@GOTOFF]
	xor	edx, [ecx]
	and	dl, 0xE
	movzx	edx, dl

	lea	eax, [eax+objc_tagged_pointer_classes@GOTOFF]
	mov	edx, [eax+edx*2]
	mov	edx, [edx+32]

	jmp	short .Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro generate_lookup_super name lookup
\name:

	mov	edx, [esp+4]
	mov	eax, [edx]
	test	eax, eax
	jz	short ret_nil

	mov	[esp+4], eax
	mov	edx, [edx+4]
	mov	edx, [edx+32]
	jmp	short .Lmain_\lookup
.type \name, %function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

ret_nil:
	call	get_eip
	add	eax, offset _GLOBAL_OFFSET_TABLE_
	lea	eax, [eax+nil_method@GOTOFF]
	ret

nil_method:
	xor	eax, eax
	ret

get_eip:
	mov	eax, [esp]
	ret

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

<
<
|

















<
<






|

|
|
|

|
|

|
|


|


|
|

|
|
|
|

|
|




|
|
|
|

|
|
|

|
|
|
|

|
|
|

|




|

>
|
|
<
|

|
|
|
|




|
|
|
|

|
|
|
|


|
|


|
|





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
/*


 * 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 "platform.h"



.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	movl	4(%esp), %edx
	testl	%edx, %edx
	jz	returnNilMethod

	testb	$1, %dl
	jnz	.LtaggedPointer_\name

	movl	(%edx), %edx
	movl	32(%edx), %edx

.Lmain_\name:
	movl	8(%esp), %eax

#ifdef OF_SELUID24
	movzbl	2(%eax), %ecx
	movl	(%edx,%ecx,4), %edx
#endif
	movzbl	1(%eax), %ecx
	movl	(%edx,%ecx,4), %edx
	movzbl	(%eax), %ecx
	movl	(%edx,%ecx,4), %eax

	testl	%eax, %eax
	jz	0f

	ret

0:
	call	getEIP
	addl	$_GLOBAL_OFFSET_TABLE_, %eax
	lea	\notFound@GOTOFF(%eax), %eax
	jmp	*%eax

.LtaggedPointer_\name:
	call	getEIP
	addl	$_GLOBAL_OFFSET_TABLE_, %eax

	leal	objc_taggedPointerSecret@GOTOFF(%eax), %ecx
	xorl	(%ecx), %edx
	andb	$0xE, %dl
	movzbl	%dl, %edx

	leal	objc_taggedPointerClasses@GOTOFF(%eax), %eax
	movl	(%eax,%edx,2), %edx
	movl	32(%edx), %edx

	jmp	.Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	movl	4(%esp), %edx
	movl	(%edx), %eax
	testl	%eax, %eax

	jz	returnNilMethod

	movl	%eax, 4(%esp)
	mov	4(%edx), %edx
	mov	32(%edx), %edx
	jmp	.Lmain_\lookup
.type \name, %function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

returnNilMethod:
	call	getEIP
	addl	$_GLOBAL_OFFSET_TABLE_, %eax
	leal	nilMethod@GOTOFF(%eax), %eax
	ret

nilMethod:
	xorl	%eax, %eax
	ret

getEIP:
	movl	(%esp), %eax
	ret

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-x86-win32.S from [5502257c39] to [0d663a9140].

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, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.intel_syntax noprefix

.globl _objc_msg_lookup
.globl _objc_msg_lookup_stret
.globl _objc_msg_lookup_super
.globl _objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	mov	edx, [esp+4]
	test	edx, edx
	jz	short ret_nil

	bt	edx, 0
	jc	short .Ltagged_pointer_\name

	mov	edx, [edx]
	mov	edx, [edx+32]

.Lmain_\name:
	mov	eax, [esp+8]

#ifdef OF_SELUID24
	movzx	ecx, byte ptr [eax+2]
	mov	edx, [edx+ecx*4]
#endif
	movzx	ecx, byte ptr [eax+1]
	mov	edx, [edx+ecx*4]
	movzx	ecx, byte ptr [eax]
	mov	eax, [edx+ecx*4]

	test	eax, eax
	jz	\not_found

	ret

.Ltagged_pointer_\name:
	xor	edx, _objc_tagged_pointer_secret
	and	dl, 0xE
	movzx	edx, dl

	mov	edx, [_objc_tagged_pointer_classes+edx*2]
	mov	edx, [edx+32]

	jmp	short .Lmain_\name




.endm


.macro generate_lookup_super name lookup
\name:
	mov	edx, [esp+4]
	mov	eax, [edx]
	test	eax, eax
	jz	short ret_nil

	mov	[esp+4], eax
	mov	edx, [edx+4]
	mov	edx, [edx+32]
	jmp	short .Lmain_\lookup




.endm

generate_lookup _objc_msg_lookup _objc_method_not_found
generate_lookup _objc_msg_lookup_stret _objc_method_not_found_stret
generate_lookup_super _objc_msg_lookup_super _objc_msg_lookup
generate_lookup_super _objc_msg_lookup_super_stret _objc_msg_lookup_stret

ret_nil:
	mov	eax, offset nil_method
	ret

nil_method:
	xor	eax, eax
	ret

<
<
|















<
<






|

|
|
|

|
|

|
|


|


|
|

|
|
|
|

|
|



|
|
|
|

|
|

|
>
>
>
>

>
|
<

|
|
|
|

|
|
|
|
>
>
>
>


|
|
|
|

|
|


|
|

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
/*


 * 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"



.globl _objc_msg_lookup
.globl _objc_msg_lookup_stret
.globl _objc_msg_lookup_super
.globl _objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	movl	4(%esp), %edx
	testl	%edx, %edx
	jz	returnNilMethod

	testb	$1, %dl
	jnz	.LtaggedPointer_\name

	movl	(%edx), %edx
	movl	32(%edx), %edx

.Lmain_\name:
	movl	8(%esp), %eax

#ifdef OF_SELUID24
	movzbl	2(%eax), %ecx
	movl	(%edx,%ecx,4), %edx
#endif
	movzbl	1(%eax), %ecx
	movl	(%edx,%ecx,4), %edx
	movzbl	(%eax), %ecx
	movl	(%edx,%ecx,4), %eax

	testl	%eax, %eax
	jz	\notFound

	ret

.LtaggedPointer_\name:
	xorl	_objc_taggedPointerSecret, %edx
	andb	$0xE, %dl
	movzbl	%dl, %edx

	movl	_objc_taggedPointerClasses(,%edx,2), %edx
	movl	32(%edx), %edx

	jmp	.Lmain_\name
.def \name
.scl 2
.type 32
.endef
.endm

.macro GENERATE_LOOKUP_SUPER name lookup

\name:
	movl	4(%esp), %edx
	movl	(%edx), %eax
	test	%eax, %eax
	jz	returnNilMethod

	movl	%eax, 4(%esp)
	movl	4(%edx), %edx
	movl	32(%edx), %edx
	jmp	.Lmain_\lookup
.def \name
.scl 2
.type 32
.endef
.endm

GENERATE_LOOKUP _objc_msg_lookup _objc_methodNotFound
GENERATE_LOOKUP _objc_msg_lookup_stret _objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER _objc_msg_lookup_super _objc_msg_lookup
GENERATE_LOOKUP_SUPER _objc_msg_lookup_super_stret _objc_msg_lookup_stret

returnNilMethod:
	movl	$nilMethod, %eax
	ret

nilMethod:
	xorl	%eax, %eax
	ret

Modified src/runtime/lookup-asm/lookup-asm-x86_64-elf.S from [1f89a766d7] to [63daf9ee68].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "platform.h"

.intel_syntax noprefix

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	test	rdi, rdi
	jz	short ret_nil

	bt	edi, 0
	jc	short .Ltagged_pointer_\name

	mov	r8, [rdi]
	mov	r8, [r8+64]

.Lmain_\name:
	mov	rax, [rsi]
	movzx	ecx, ah
	movzx	edx, al
#ifdef OF_SELUID24
	shr	eax, 16

	mov	r8,  [r8+rax*8]
#endif
	mov	r8,  [r8+rcx*8]
	mov	rax, [r8+rdx*8]

	test	rax, rax
	jz	short \not_found@PLT

	ret

.Ltagged_pointer_\name:
	mov	rax, [rip+objc_tagged_pointer_secret@GOTPCREL]
	xor	rdi, [rax]

	and	dil, 0xE
	movzx	r8, dil

	mov	rax, [rip+objc_tagged_pointer_classes@GOTPCREL]
	mov	r8, [rax+r8*4]
	mov	r8, [r8+64]

	jmp	short .Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro generate_lookup_super name lookup
\name:
	mov	r8, rdi
	mov	rdi, [rdi]
	test	rdi, rdi
	jz	short ret_nil

	mov	r8, [r8+8]
	mov	r8, [r8+64]
	jmp	short .Lmain_\lookup
.type \name, %function
.size \name, .-\name
.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

ret_nil:
	lea	rax, [rip+nil_method]
	ret

nil_method:
	xor	rax, rax
	ret

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

<
<
|

















<
<






|

|
|

|
|

|
|


|
|
|

|

|

|
|

|
|



|
|
|
>
|
|
|
<
|
|

|




|

|
|
|
|

|
|
|




|
|
|
|

|
|


|
|





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
/*


 * 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 "platform.h"



.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	testq	%rdi, %rdi
	jz	returnNilMethod

	testb	$1, %dil
	jnz	.LtaggedPointer_\name

	movq	(%rdi), %r8
	movq	64(%r8), %r8

.Lmain_\name:
	movq	(%rsi), %rax
	movzbl	%ah, %ecx
	movzbl	%al, %edx
#ifdef OF_SELUID24
	shrl	$16, %eax

	movq	(%r8,%rax,8), %r8
#endif
	movq	(%r8,%rcx,8), %r8
	movq	(%r8,%rdx,8), %rax

	testq	%rax, %rax
	jz	\notFound@PLT

	ret

.LtaggedPointer_\name:
	movq	objc_taggedPointerSecret@GOTPCREL(%rip), %rax
	xorq	(%rax), %rdi
	andb	$0xE, %dil
	movzbl	%dil, %r8d

	movq	objc_taggedPointerClasses@GOTPCREL(%rip), %rax

	movq	(%rax,%r8,4), %r8
	movq	64(%r8), %r8

	jmp	.Lmain_\name
.type \name, %function
.size \name, .-\name
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:
	movq	%rdi, %r8
	movq	(%rdi), %rdi
	testq	%rdi, %rdi
	jz	returnNilMethod

	movq	8(%r8), %r8
	movq	64(%r8), %r8
	jmp	.Lmain_\lookup
.type \name, %function
.size \name, .-\name
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

returnNilMethod:
	leaq	nilMethod(%rip), %rax
	ret

nilMethod:
	xorq	%rax, %rax
	ret

#ifdef OF_LINUX
.section .note.GNU-stack, "", %progbits
#endif

Modified src/runtime/lookup-asm/lookup-asm-x86_64-macho.S from [1d842ab593] to [1e72bfb083].

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, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.intel_syntax noprefix

.globl _objc_msg_lookup
.globl _objc_msg_lookup_stret
.globl _objc_msg_lookup_super
.globl _objc_msg_lookup_super_stret

.section __TEXT, __text, regular, pure_instructions
.macro generate_lookup
$0:
	test	rdi, rdi
	jz	ret_nil

	bt	edi, 0
	jc	Ltagged_pointer_$0

	mov	r8, [rdi]
	mov	r8, [r8+64]

Lmain_$0:
	mov	rax, [rsi]
	movzx	ecx, ah
	movzx	edx, al
#ifdef OF_SELUID24
	shr	eax, 16

	mov	r8, [r8+rax*8]
#endif
	mov	r8, [r8+rcx*8]
	mov	rax, [r8+rdx*8]

	test	rax, rax
	jz	$1

	ret

Ltagged_pointer_$0:
	mov	rax, [rip+_objc_tagged_pointer_secret@GOTPCREL]
	xor	rdi, [rax]
	and	dil, 0xE
	movzx	r8, dil

	mov	rax, [rip+_objc_tagged_pointer_classes@GOTPCREL]
	mov	r8, [rax+r8*4]
	mov	r8, [r8+64]

	jmp	Lmain_$0
.endmacro

.macro generate_lookup_super
$0:
	mov	r8, rdi
	mov	rdi, [rdi]
	test	rdi, rdi
	jz	ret_nil

	mov	r8, [r8+8]
	mov	r8, [r8+64]
	jmp	Lmain_$1
.endmacro

generate_lookup _objc_msg_lookup, _objc_method_not_found
generate_lookup _objc_msg_lookup_stret, _objc_method_not_found_stret
generate_lookup_super _objc_msg_lookup_super, _objc_msg_lookup
generate_lookup_super _objc_msg_lookup_super_stret, _objc_msg_lookup_stret

ret_nil:
	lea	rax, [rip+nil_method]
	ret

nil_method:
	mov	rax, rdi
	ret

<
<
|















<
<






|

|
|

|
|

|
|


|
|
|

|

|

|
|

|




|
|
|
|
|

|
|
|




|

|
|
|
|

|
|



|
|
|
|

|
|


|
|

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.
 */

#include "config.h"



.globl _objc_msg_lookup
.globl _objc_msg_lookup_stret
.globl _objc_msg_lookup_super
.globl _objc_msg_lookup_super_stret

.section __TEXT, __text, regular, pure_instructions
.macro GENERATE_LOOKUP
$0:
	testq	%rdi, %rdi
	jz	returnNilMethod

	testb	$$1, %dil
	jnz	LtaggedPointer_$0

	movq	(%rdi), %r8
	movq	64(%r8), %r8

Lmain_$0:
	movq	(%rsi), %rax
	movzbl	%ah, %ecx
	movzbl	%al, %edx
#ifdef OF_SELUID24
	shrl	$$16, %eax

	movq	(%r8,%rax,8), %r8
#endif
	movq	(%r8,%rcx,8), %r8
	movq	(%r8,%rdx,8), %rax

	testq	%rax, %rax
	jz	$1

	ret

LtaggedPointer_$0:
	movq	_objc_taggedPointerSecret@GOTPCREL(%rip), %rax
	xorq	(%rax), %rdi
	andb	$$0xE, %dil
	movzbl	%dil, %r8d

	movq	_objc_taggedPointerClasses@GOTPCREL(%rip), %rax
	movq	(%rax,%r8,4), %r8
	movq	64(%r8), %r8

	jmp	Lmain_$0
.endmacro

.macro GENERATE_LOOKUP_SUPER
$0:
	movq	%rdi, %r8
	movq	(%rdi), %rdi
	testq	%rdi, %rdi
	jz	returnNilMethod

	movq	8(%r8), %r8
	movq	64(%r8), %r8
	jmp	Lmain_$1
.endmacro

GENERATE_LOOKUP _objc_msg_lookup, _objc_methodNotFound
GENERATE_LOOKUP _objc_msg_lookup_stret, _objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER _objc_msg_lookup_super, _objc_msg_lookup
GENERATE_LOOKUP_SUPER _objc_msg_lookup_super_stret, _objc_msg_lookup_stret

returnNilMethod:
	leaq	nilMethod(%rip), %rax
	ret

nilMethod:
	xorq	%rax, %rax
	ret

Modified src/runtime/lookup-asm/lookup-asm-x86_64-win64.S from [fde9955f30] to [bb874856c9].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

.intel_syntax noprefix

.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro generate_lookup name not_found
\name:
	test	rcx, rcx
	jz	short ret_nil

	bt	ecx, 0
	jc	short .Ltagged_pointer_\name

	mov	r8, [rcx]
	mov	r8, [r8+56]

.Lmain_\name:
	mov	r10, rcx
	mov	r11, rdx

	mov	rax, [rdx]
	movzx	ecx, ah
	movzx	edx, al
#ifdef OF_SELUID24
	shr	eax, 16

	mov	r8,  [r8+rax*8]
#endif
	mov	r8,  [r8+rcx*8]
	mov	rax, [r8+rdx*8]

	test	rax, rax
	jz	short 0f

	ret

0:
	mov	rcx, r10
	mov	rdx, r11
	jmp	\not_found

.Ltagged_pointer_\name:
	xor	rcx, objc_tagged_pointer_secret
	and	cl, 0xE
	movzx	r8, cl

	mov	r8, [objc_tagged_pointer_classes+r8*4]

	mov	r8, [r8+56]

	jmp	short .Lmain_\name




.endm

.macro generate_lookup_super name lookup
\name:
	mov	r8, rcx
	mov	rcx, [rcx]
	test	rcx, rcx
	jz	short ret_nil




	mov	r8, [r8+8]
	mov	r8, [r8+56]
	jmp	short .Lmain_\lookup




.endm

generate_lookup objc_msg_lookup objc_method_not_found
generate_lookup objc_msg_lookup_stret objc_method_not_found_stret
generate_lookup_super objc_msg_lookup_super objc_msg_lookup
generate_lookup_super objc_msg_lookup_super_stret objc_msg_lookup_stret

ret_nil:
	mov	rax, offset nil_method
	ret

nil_method:
	xor	rax, rax
	ret

<
<
|















<
<






|

|
|

|
|

|
|


|
|

|
|
|

|

|

|
|

|
|




|
|
|

|
|
|
|

|
>
|

|
>
>
>
>


|

<
<
|
<
>
>
>

|
|
|
>
>
>
>


|
|
|
|

|
|


|
|

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
/*


 * 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"



.globl objc_msg_lookup
.globl objc_msg_lookup_stret
.globl objc_msg_lookup_super
.globl objc_msg_lookup_super_stret

.section .text
.macro GENERATE_LOOKUP name notFound
\name:
	testq	%rcx, %rcx
	jz	returnNilMethod

	testb	$1, %cl
	jnz	.LtaggedPointer_\name

	movq	(%rcx), %r8
	movq	56(%r8), %r8

.Lmain_\name:
	movq	%rcx, %r10
	movq	%rdx, %r11

	movq	(%rdx), %rax
	movzbl	%ah, %ecx
	movzbl	%al, %edx
#ifdef OF_SELUID24
	shrl	$16, %eax

	movq	(%r8,%rax,8), %r8
#endif
	movq	(%r8,%rcx,8), %r8
	movq	(%r8,%rdx,8), %rax

	testq	%rax, %rax
	jz	0f

	ret

0:
	movq	%r10, %rcx
	movq	%r11, %rdx
	jmp	\notFound

.LtaggedPointer_\name:
	xorq	objc_taggedPointerSecret(%rip), %rcx
	andb	$0xE, %cl
	movzbl	%cl, %r8d

	leaq	objc_taggedPointerClasses(%rip), %rax
	movq	(%rax,%r8,4), %r8
	movq	56(%r8), %r8

	jmp	.Lmain_\name
.def \name
.scl 2
.type 32
.endef
.endm

.macro GENERATE_LOOKUP_SUPER name lookup
\name:


	movq	%rcx, %r8

	movq	(%rcx), %rcx
	testq	%rcx, %rcx
	jz	returnNilMethod

	movq	8(%r8), %r8
	movq	56(%r8), %r8
	jmp	.Lmain_\lookup
.def \name
.scl 2
.type 32
.endef
.endm

GENERATE_LOOKUP objc_msg_lookup objc_methodNotFound
GENERATE_LOOKUP objc_msg_lookup_stret objc_methodNotFound_stret
GENERATE_LOOKUP_SUPER objc_msg_lookup_super objc_msg_lookup
GENERATE_LOOKUP_SUPER objc_msg_lookup_super_stret objc_msg_lookup_stret

returnNilMethod:
	leaq	nilMethod(%rip), %rax
	ret

nilMethod:
	xorq	%rax, %rax
	ret

Modified src/runtime/lookup-asm/lookup-asm.S from [4908be8ba8] to [31f647ad96].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
24
25
26
27
28
29
30


31
32
33
34
35
36
37
#  include "lookup-asm-x86_64-elf.S"
# elif defined(OF_X86)
#  include "lookup-asm-x86-elf.S"
# elif defined(OF_ARM64)
#  include "lookup-asm-arm64-elf.S"
# elif defined(OF_ARM)
#  include "lookup-asm-arm-elf.S"


# elif defined(OF_POWERPC)
#  include "lookup-asm-powerpc-elf.S"
# elif defined(OF_MIPS64_N64)
#  include "lookup-asm-mips64-n64-elf.S"
# elif defined(OF_MIPS)
#  include "lookup-asm-mips-elf.S"
# elif defined(OF_SPARC64)







>
>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#  include "lookup-asm-x86_64-elf.S"
# elif defined(OF_X86)
#  include "lookup-asm-x86-elf.S"
# elif defined(OF_ARM64)
#  include "lookup-asm-arm64-elf.S"
# elif defined(OF_ARM)
#  include "lookup-asm-arm-elf.S"
# elif defined(OF_POWERPC64)
#  include "lookup-asm-powerpc64-elf.S"
# elif defined(OF_POWERPC)
#  include "lookup-asm-powerpc-elf.S"
# elif defined(OF_MIPS64_N64)
#  include "lookup-asm-mips64-n64-elf.S"
# elif defined(OF_MIPS)
#  include "lookup-asm-mips-elf.S"
# elif defined(OF_SPARC64)

Modified src/runtime/lookup.m from [507e90722d] to [620b9cc132].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
	bool isClass =
	    object_getClass(object)->info & OBJC_CLASS_INFO_METACLASS;

	if (!(object_getClass(object)->info & OBJC_CLASS_INFO_INITIALIZED)) {
		Class class = (isClass
		    ? (Class)object : object_getClass(object));

		objc_initialize_class(class);

		if (!(class->info & OBJC_CLASS_INFO_SETUP))
			OBJC_ERROR("Could not dispatch message for incomplete "

			    "class %s!", class_getName(class));

		/*
		 * We don't need to handle the case that super was called.
		 * The reason for this is that a call to super is not possible
		 * before a message to the class has been sent and it thus has
		 * been initialized together with its superclasses.
		 */







|


|
>
|







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
	bool isClass =
	    object_getClass(object)->info & OBJC_CLASS_INFO_METACLASS;

	if (!(object_getClass(object)->info & OBJC_CLASS_INFO_INITIALIZED)) {
		Class class = (isClass
		    ? (Class)object : object_getClass(object));

		objc_initializeClass(class);

		if (!(class->info & OBJC_CLASS_INFO_SETUP))
			OBJC_ERROR("Could not dispatch message %s for "
			    "incomplete class %s!",
			    sel_getName(selector), class_getName(class));

		/*
		 * We don't need to handle the case that super was called.
		 * The reason for this is that a call to super is not possible
		 * before a message to the class has been sent and it thus has
		 * been initialized together with its superclasses.
		 */
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

	OBJC_ERROR("Selector %c[%s] is not implemented for class %s!",
	    (isClass ? '+' : '-'), sel_getName(selector),
	    object_getClassName(object));
}

IMP
objc_method_not_found(id object, SEL selector)
{
	return commonMethodNotFound(object, selector, objc_msg_lookup,
	    forwardHandler);
}

IMP
objc_method_not_found_stret(id object, SEL selector)
{
	return commonMethodNotFound(object, selector, objc_msg_lookup_stret,
	    stretForwardHandler);
}

void
objc_setForwardHandler(IMP forward, IMP stretForward)
{
	forwardHandler = forward;
	stretForwardHandler = stretForward;
}

bool
class_respondsToSelector(Class class, SEL selector)
{
	if (class == Nil)
		return false;

	return (objc_dtable_get(class->DTable,
	    (uint32_t)selector->UID) != (IMP)0);
}

#ifndef OF_ASM_LOOKUP
static id
nilMethod(id self, SEL _cmd)
{
	return nil;
}

static OF_INLINE IMP
commonLookup(id object, SEL selector, IMP (*notFound)(id, SEL))
{
	IMP imp;

	if (object == nil)
		return (IMP)nilMethod;

	imp = objc_dtable_get(object_getClass(object)->DTable,
	    (uint32_t)selector->UID);

	if (imp == (IMP)0)
		return notFound(object, selector);

	return imp;
}

IMP
objc_msg_lookup(id object, SEL selector)
{
	return commonLookup(object, selector, objc_method_not_found);
}

IMP
objc_msg_lookup_stret(id object, SEL selector)
{
	return commonLookup(object, selector, objc_method_not_found_stret);
}

static OF_INLINE IMP
commonSuperLookup(struct objc_super *super, SEL selector,
    IMP (*notFound)(id, SEL))
{
	IMP imp;

	if (super->self == nil)
		return (IMP)nilMethod;

	imp = objc_dtable_get(super->class->DTable, (uint32_t)selector->UID);

	if (imp == (IMP)0)
		return notFound(super->self, selector);

	return imp;
}

IMP
objc_msg_lookup_super(struct objc_super *super, SEL selector)
{
	return commonSuperLookup(super, selector, objc_method_not_found);
}

IMP
objc_msg_lookup_super_stret(struct objc_super *super, SEL selector)
{
	return commonSuperLookup(super, selector, objc_method_not_found_stret);
}
#endif







|






|


















|


















|











|





|











|










|





|


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

	OBJC_ERROR("Selector %c[%s] is not implemented for class %s!",
	    (isClass ? '+' : '-'), sel_getName(selector),
	    object_getClassName(object));
}

IMP
objc_methodNotFound(id object, SEL selector)
{
	return commonMethodNotFound(object, selector, objc_msg_lookup,
	    forwardHandler);
}

IMP
objc_methodNotFound_stret(id object, SEL selector)
{
	return commonMethodNotFound(object, selector, objc_msg_lookup_stret,
	    stretForwardHandler);
}

void
objc_setForwardHandler(IMP forward, IMP stretForward)
{
	forwardHandler = forward;
	stretForwardHandler = stretForward;
}

bool
class_respondsToSelector(Class class, SEL selector)
{
	if (class == Nil)
		return false;

	return (objc_dtable_get(class->dTable,
	    (uint32_t)selector->UID) != (IMP)0);
}

#ifndef OF_ASM_LOOKUP
static id
nilMethod(id self, SEL _cmd)
{
	return nil;
}

static OF_INLINE IMP
commonLookup(id object, SEL selector, IMP (*notFound)(id, SEL))
{
	IMP imp;

	if (object == nil)
		return (IMP)nilMethod;

	imp = objc_dtable_get(object_getClass(object)->dTable,
	    (uint32_t)selector->UID);

	if (imp == (IMP)0)
		return notFound(object, selector);

	return imp;
}

IMP
objc_msg_lookup(id object, SEL selector)
{
	return commonLookup(object, selector, objc_methodNotFound);
}

IMP
objc_msg_lookup_stret(id object, SEL selector)
{
	return commonLookup(object, selector, objc_methodNotFound_stret);
}

static OF_INLINE IMP
commonSuperLookup(struct objc_super *super, SEL selector,
    IMP (*notFound)(id, SEL))
{
	IMP imp;

	if (super->self == nil)
		return (IMP)nilMethod;

	imp = objc_dtable_get(super->class->dTable, (uint32_t)selector->UID);

	if (imp == (IMP)0)
		return notFound(super->self, selector);

	return imp;
}

IMP
objc_msg_lookup_super(struct objc_super *super, SEL selector)
{
	return commonSuperLookup(super, selector, objc_methodNotFound);
}

IMP
objc_msg_lookup_super_stret(struct objc_super *super, SEL selector)
{
	return commonSuperLookup(super, selector, objc_methodNotFound_stret);
}
#endif

Modified src/runtime/method.m from [f841e4b2e6] to [82646e77ef].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	if (class == Nil) {
		if (outCount != NULL)
			*outCount = 0;

		return NULL;
	}

	objc_global_mutex_lock();

	count = 0;
	for (iter = class->methodList; iter != NULL; iter = iter->next)
		count += iter->count;

	if (count == 0) {
		if (outCount != NULL)
			*outCount = 0;

		objc_global_mutex_unlock();
		return NULL;
	}

	if ((methods = malloc((count + 1) * sizeof(Method))) == NULL)
		OBJC_ERROR("Not enough memory to copy methods");

	i = 0;
	for (iter = class->methodList; iter != NULL; iter = iter->next)
		for (unsigned int j = 0; j < iter->count; j++)
			methods[i++] = &iter->methods[j];

	OF_ENSURE(i == count);


	methods[count] = NULL;

	if (outCount != NULL)
		*outCount = count;

	objc_global_mutex_unlock();

	return methods;
}

SEL
method_getName(Method method)
{







|









|










>
|
>
>





|







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
	if (class == Nil) {
		if (outCount != NULL)
			*outCount = 0;

		return NULL;
	}

	objc_globalMutex_lock();

	count = 0;
	for (iter = class->methodList; iter != NULL; iter = iter->next)
		count += iter->count;

	if (count == 0) {
		if (outCount != NULL)
			*outCount = 0;

		objc_globalMutex_unlock();
		return NULL;
	}

	if ((methods = malloc((count + 1) * sizeof(Method))) == NULL)
		OBJC_ERROR("Not enough memory to copy methods");

	i = 0;
	for (iter = class->methodList; iter != NULL; iter = iter->next)
		for (unsigned int j = 0; j < iter->count; j++)
			methods[i++] = &iter->methods[j];

	if (i != count)
		OBJC_ERROR("Fatal internal inconsistency!");

	methods[count] = NULL;

	if (outCount != NULL)
		*outCount = count;

	objc_globalMutex_unlock();

	return methods;
}

SEL
method_getName(Method method)
{

Modified src/runtime/misc.m from [a61bf65bf6] to [6b0956b061].

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




























































































/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdio.h>
#include <stdlib.h>

#include "ObjFWRT.h"
#include "private.h"
















static objc_enumeration_mutation_handler_t enumerationMutationHandler = NULL;

void
objc_enumerationMutation(id object)
{
	if (enumerationMutationHandler != NULL)
		enumerationMutationHandler(object);
	else
		OBJC_ERROR("Object was mutated during enumeration!");
}

void
objc_setEnumerationMutationHandler(objc_enumeration_mutation_handler_t handler)
{
	enumerationMutationHandler = handler;
}





























































































<
<
|















>






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|











|



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*


 * 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

#include "ObjFWRT.h"
#include "private.h"

#ifdef OF_WINDOWS
# include <windows.h>
#endif

#ifdef OF_AMIGAOS
# define USE_INLINE_STDARG
# include <proto/exec.h>
# include <clib/debug_protos.h>
# define __NOLIBBASE__
# define Class IntuitionClass
# include <proto/intuition.h>
# undef Class
# undef __NOLIBBASE__
#endif

static objc_enumeration_mutation_handler enumerationMutationHandler = NULL;

void
objc_enumerationMutation(id object)
{
	if (enumerationMutationHandler != NULL)
		enumerationMutationHandler(object);
	else
		OBJC_ERROR("Object was mutated during enumeration!");
}

void
objc_setEnumerationMutationHandler(objc_enumeration_mutation_handler handler)
{
	enumerationMutationHandler = handler;
}

void
objc_error(const char *title, const char *format, ...)
{
#if defined(OF_WINDOWS) || defined(OF_AMIGAOS)
# define messageLen 512
	char message[messageLen];
	int status;
	va_list args;

	va_start(args, format);
	status = vsnprintf(message, messageLen, format, args);
	if (status <= 0 || status >= messageLen)
		message[0] = '\0';
	va_end(args);
# undef BUF_LEN
#endif

#if defined(OF_WINDOWS)
	fprintf(stderr, "[%s] %s\n", title, message);
	fflush(stderr);

	MessageBoxA(NULL, message, title,
	    MB_OK | MB_SYSTEMMODAL | MB_ICONERROR);

	exit(EXIT_FAILURE);
#elif defined(OF_AMIGAOS)
	struct Library *IntuitionBase;
# ifdef OF_AMIGAOS4
	struct IntuitionIFace *IIntuition;
# endif
	struct EasyStruct easy;

# ifndef OF_AMIGAOS4
	kprintf("[%s] %s\n", title, message);
# endif

	if ((IntuitionBase = OpenLibrary("intuition.library", 0)) == NULL)
		exit(EXIT_FAILURE);

# ifdef OF_AMIGAOS4
	if ((IIntuition = (struct IntuitionIFace *)GetInterface(IntuitionBase,
	    "main", 1, NULL)) == NULL)
		exit(EXIT_FAILURE);
# endif

	easy.es_StructSize = sizeof(easy);
	easy.es_Flags = 0;
	easy.es_Title = (void *)title;
	easy.es_TextFormat = (void *)"%s";
	easy.es_GadgetFormat = (void *)"OK";

	EasyRequest(NULL, &easy, NULL, (ULONG)message);

# ifdef OF_AMIGAOS4
	DropInterface((struct Interface *)IIntuition);
# endif

	CloseLibrary(IntuitionBase);

	exit(EXIT_FAILURE);
#else
	va_list args;

	va_start(args, format);

	fprintf(stderr, "[%s] ", title);
	vfprintf(stderr, format, args);
	fprintf(stderr, "\n");
	fflush(stderr);

	va_end(args);

	abort();
#endif

	OF_UNREACHABLE
}

char *
objc_strdup(const char *string)
{
	char *copy;
	size_t length = strlen(string);

	if ((copy = (char *)malloc(length + 1)) == NULL)
		return NULL;

	memcpy(copy, string, length + 1);

	return copy;
}

Deleted src/runtime/morphos-clib.h version [252cb36e71].

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
/* The following function is only for the linklib. */
bool glue_objc_init(unsigned int, struct objc_libc *, FILE *, FILE *);
void glue___objc_exec_class(struct objc_module *);
IMP glue_objc_msg_lookup(id, SEL);
IMP glue_objc_msg_lookup_stret(id, SEL);
IMP glue_objc_msg_lookup_super(struct objc_super *, SEL);
IMP glue_objc_msg_lookup_super_stret(struct objc_super *, SEL);
Class glue_objc_lookUpClass(const char *);
Class glue_objc_getClass(const char *);
Class glue_objc_getRequiredClass(const char *);
Class glue_objc_lookup_class(const char *);
Class glue_objc_get_class(const char *);
void glue_objc_exception_throw(id);
int glue_objc_sync_enter(id);
int glue_objc_sync_exit(id);
id glue_objc_getProperty(id, SEL, ptrdiff_t, bool);
void glue_objc_setProperty(id, SEL, ptrdiff_t, id, bool, signed char);
void glue_objc_getPropertyStruct(void *, const void *, ptrdiff_t, bool, bool);
void glue_objc_setPropertyStruct(void *, const void *, ptrdiff_t, bool, bool);
void glue_objc_enumerationMutation(id);
int glue___gnu_objc_personality(int, int, uint64_t, void *, void *);
id glue_objc_retain(id);
id glue_objc_retainBlock(id);
id glue_objc_retainAutorelease(id);
void glue_objc_release(id);
id glue_objc_autorelease(id);
id glue_objc_autoreleaseReturnValue(id);
id glue_objc_retainAutoreleaseReturnValue(id);
id glue_objc_retainAutoreleasedReturnValue(id);
id glue_objc_storeStrong(id *, id);
id glue_objc_storeWeak(id *, id);
id glue_objc_loadWeakRetained(id *);
id glue_objc_initWeak(id *, id);
void glue_objc_destroyWeak(id *);
id glue_objc_loadWeak(id *);
void glue_objc_copyWeak(id *, id *);
void glue_objc_moveWeak(id *, id *);
SEL glue_sel_registerName(const char *);
const char *glue_sel_getName(SEL);
bool glue_sel_isEqual(SEL, SEL);
Class glue_objc_allocateClassPair(Class, const char *, size_t);
void glue_objc_registerClassPair(Class);
unsigned int glue_objc_getClassList(Class *, unsigned int);
Class *glue_objc_copyClassList(unsigned int *);
bool glue_class_isMetaClass(Class);
const char *glue_class_getName(Class);
Class glue_class_getSuperclass(Class);
unsigned long glue_class_getInstanceSize(Class);
bool glue_class_respondsToSelector(Class, SEL);
bool glue_class_conformsToProtocol(Class, Protocol *);
IMP glue_class_getMethodImplementation(Class, SEL);
IMP glue_class_getMethodImplementation_stret(Class, SEL);
Method glue_class_getInstanceMethod(Class, SEL);
bool glue_class_addMethod(Class, SEL, IMP, const char *);
IMP glue_class_replaceMethod(Class, SEL, IMP, const char *);
Class glue_object_getClass(id);
Class glue_object_setClass(id, Class);
const char *glue_object_getClassName(id);
const char *glue_protocol_getName(Protocol *);
bool glue_protocol_isEqual(Protocol *, Protocol *);
bool glue_protocol_conformsToProtocol(Protocol *, Protocol *);
objc_uncaught_exception_handler_t glue_objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler_t);
void glue_objc_setForwardHandler(IMP, IMP);
void glue_objc_setEnumerationMutationHandler(objc_enumeration_mutation_handler_t);
id glue_objc_constructInstance(Class, void *);
void glue_objc_exit(void);
Ivar *glue_class_copyIvarList(Class, unsigned int *);
const char *glue_ivar_getName(Ivar);
const char *glue_ivar_getTypeEncoding(Ivar);
ptrdiff_t glue_ivar_getOffset(Ivar);
Method *glue_class_copyMethodList(Class, unsigned int *);
SEL glue_method_getName(Method);
const char *glue_method_getTypeEncoding(Method);
objc_property_t *glue_class_copyPropertyList(Class, unsigned int *);
const char *glue_property_getName(objc_property_t);
char *glue_property_copyAttributeValue(objc_property_t, const char *);
void *glue_objc_destructInstance(id);
void *glue_objc_autoreleasePoolPush(void);
void glue_objc_autoreleasePoolPop(void *);
id glue__objc_rootAutorelease(id);
/* The following functions are private! Don't use! */
struct objc_hashtable *glue_objc_hashtable_new(objc_hashtable_hash_func, objc_hashtable_equal_func, uint32_t);
void glue_objc_hashtable_set(struct objc_hashtable *, const void *, const void *);
void *glue_objc_hashtable_get(struct objc_hashtable *, const void *);
void glue_objc_hashtable_delete(struct objc_hashtable *, const void *);
void glue_objc_hashtable_free(struct objc_hashtable *);
/* Public functions again */
void glue_objc_setTaggedPointerSecret(uintptr_t);
int glue_objc_registerTaggedPointerClass(Class);
bool glue_object_isTaggedPointer(id);
Class _Nullable glue_object_getTaggedPointerClass(id);
uintptr_t glue_object_getTaggedPointerValue(id);
id glue_objc_createTaggedPointer(int, uintptr_t);
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































Deleted src/runtime/morphos.fd version [881a580162].

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
##base _ObjFWRTBase
##bias 30
##public
* The following function is only for the linklib.
glue_objc_init(version,libc,stdout,stderr)(sysv,r12base)
glue___objc_exec_class(module)(sysv,r12base)
glue_objc_msg_lookup(object,selector)(sysv,r12base)
glue_objc_msg_lookup_stret(object,selector)(sysv,r12base)
glue_objc_msg_lookup_super(super,selector)(sysv,r12base)
glue_objc_msg_lookup_super_stret(super,selector)(sysv,r12base)
glue_objc_lookUpClass(name)(sysv,r12base)
glue_objc_getClass(name)(sysv,r12base)
glue_objc_getRequiredClass(name)(sysv,r12base)
glue_objc_lookup_class(name)(sysv,r12base)
glue_objc_get_class(name)(sysv,r12base)
glue_objc_exception_throw(object)(sysv,r12base)
glue_objc_sync_enter(object)(sysv,r12base)
glue_objc_sync_exit(object)(sysv,r12base)
glue_objc_getProperty(self,_cmd,offset,atomic)(sysv,r12base)
glue_objc_setProperty(self,_cmd,offset,value,atomic,copy)(sysv,r12base)
glue_objc_getPropertyStruct(dest,src,size,atomic,strong)(sysv,r12base)
glue_objc_setPropertyStruct(dest,src,size,atomic,strong)(sysv,r12base)
glue_objc_enumerationMutation(object)(sysv,r12base)
glue___gnu_objc_personality(version,actions,exClass,ex,ctx)(sysv,r12base)
glue_objc_retain(object)(sysv,r12base)
glue_objc_retainBlock(block)(sysv,r12base)
glue_objc_retainAutorelease(object)(sysv,r12base)
glue_objc_release(object)(sysv,r12base)
glue_objc_autorelease(object)(sysv,r12base)
glue_objc_autoreleaseReturnValue(object)(sysv,r12base)
glue_objc_retainAutoreleaseReturnValue(object)(sysv,r12base)
glue_objc_retainAutoreleasedReturnValue(object)(sysv,r12base)
glue_objc_storeStrong(object,value)(sysv,r12base)
glue_objc_storeWeak(object,value)(sysv,r12base)
glue_objc_loadWeakRetained(object)(sysv,r12base)
glue_objc_initWeak(object,value)(sysv,r12base)
glue_objc_destroyWeak(object)(sysv,r12base)
glue_objc_loadWeak(object)(sysv,r12base)
glue_objc_copyWeak(dest,src)(sysv,r12base)
glue_objc_moveWeak(dest,src)(sysv,r12base)
glue_sel_registerName(name)(sysv,r12base)
glue_sel_getName(selector)(sysv,r12base)
glue_sel_isEqual(selector1,selector2)(sysv,r12base)
glue_objc_allocateClassPair(superclass,name,extraBytes)(sysv,r12base)
glue_objc_registerClassPair(class_)(sysv,r12base)
glue_objc_getClassList(buffer,count)(sysv,r12base)
glue_objc_copyClassList(length)(sysv,r12base)
glue_class_isMetaClass(class_)(sysv,r12base)
glue_class_getName(class_)(sysv,r12base)
glue_class_getSuperclass(class_)(sysv,r12base)
glue_class_getInstanceSize(class_)(sysv,r12base)
glue_class_respondsToSelector(class_,selector)(sysv,r12base)
glue_class_conformsToProtocol(class_,p)(sysv,r12base)
glue_class_getMethodImplementation(class_,selector)(sysv,r12base)
glue_class_getMethodImplementation_stret(class_,selector)(sysv,r12base)
glue_class_getInstanceMethod(class_,selector)(sysv,r12base)
glue_class_addMethod(class_,selector,implementation,typeEncoding)(sysv,r12base)
glue_class_replaceMethod(class_,selector,implementation,typeEncoding)(sysv,r12base)
glue_object_getClass(object)(sysv,r12base)
glue_object_setClass(object,class_)(sysv,r12base)
glue_object_getClassName(object)(sysv,r12base)
glue_protocol_getName(protocol)(sysv,r12base)
glue_protocol_isEqual(protocol1,protocol2)(sysv,r12base)
glue_protocol_conformsToProtocol(protocol1,protocol2)(sysv,r12base)
glue_objc_setUncaughtExceptionHandler(handler)(sysv,r12base)
glue_objc_setForwardHandler(forward,stretForward)(sysv,r12base)
glue_objc_setEnumerationMutationHandler(handler)(sysv,r12base)
glue_objc_constructInstance(class_,bytes)(sysv,r12base)
glue_objc_exit()(sysv,r12base)
glue_class_copyIvarList(class_,outCount)(sysv,r12base)
glue_ivar_getName(ivar)(sysv,r12base)
glue_ivar_getTypeEncoding(ivar)(sysv,r12base)
glue_ivar_getOffset(ivar)(sysv,r12base)
glue_class_copyMethodList(class_,outCount)(sysv,r12base)
glue_method_getName(method)(sysv,r12base)
glue_method_getTypeEncoding(method)(sysv,r12base)
glue_class_copyPropertyList(class_,outCount)(sysv,r12base)
glue_property_getName(property)(sysv,r12base)
glue_property_copyAttributeValue(property,name)(sysv,r12base)
glue_objc_destructInstance(object)(sysv,r12base)
glue_objc_autoreleasePoolPush()(sysv,r12base)
glue_objc_autoreleasePoolPop(pool)(sysv,r12base)
glue__objc_rootAutorelease(object)(sysv,r12base)
* The following functions are private! Don't use!
glue_objc_hashtable_new(hash,equal,size)(sysv,r12base)
glue_objc_hashtable_set(table,key,object)(sysv,r12base)
glue_objc_hashtable_get(table,key)(sysv,r12base)
glue_objc_hashtable_delete(table,key)(sysv,r12base)
glue_objc_hashtable_free(table)(sysv,r12base)
* Public functions again
glue_objc_setTaggedPointerSecret(secret)(sysv,r12base)
glue_objc_registerTaggedPointerClass(class_)(sysv,r12base)
glue_object_isTaggedPointer(object)(sysv,r12base)
glue_object_getTaggedPointerClass(object)(sysv,r12base)
glue_object_getTaggedPointerValue(object)(sysv,r12base)
glue_objc_createTaggedPointer(class_,value)(sysv,r12base)
##end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































































































































Modified src/runtime/private.h from [7f0c22271c] to [4e60c9110b].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
	Class _Nullable superclass;
	const char *_Nonnull name;
	unsigned long version;
	unsigned long info;
	long instanceSize;
	struct objc_ivar_list *_Nullable ivars;
	struct objc_method_list *_Nullable methodList;
	struct objc_dtable *_Nonnull DTable;
	Class _Nullable *_Nullable subclassList;
	void *_Nullable siblingClass;
	struct objc_protocol_list *_Nullable protocols;
	void *_Nullable GCObjectType;
	unsigned long ABIVersion;
	int32_t *_Nonnull *_Nullable ivarOffsets;
	struct objc_property_list *_Nullable propertyList;







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
	Class _Nullable superclass;
	const char *_Nonnull name;
	unsigned long version;
	unsigned long info;
	long instanceSize;
	struct objc_ivar_list *_Nullable ivars;
	struct objc_method_list *_Nullable methodList;
	struct objc_dtable *_Nonnull dTable;
	Class _Nullable *_Nullable subclassList;
	void *_Nullable siblingClass;
	struct objc_protocol_list *_Nullable protocols;
	void *_Nullable GCObjectType;
	unsigned long ABIVersion;
	int32_t *_Nonnull *_Nullable ivarOffsets;
	struct objc_property_list *_Nullable propertyList;
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
	struct objc_hashtable_bucket *_Nonnull *_Nullable data;
};

struct objc_sparsearray {
	struct objc_sparsearray_data {
		void *_Nullable next[256];
	} *_Nonnull data;
	uint8_t indexSize;
};

struct objc_dtable {
	struct objc_dtable_level2 {
#ifdef OF_SELUID24
		struct objc_dtable_level3 {
			IMP _Nullable buckets[256];







|







197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
	struct objc_hashtable_bucket *_Nonnull *_Nullable data;
};

struct objc_sparsearray {
	struct objc_sparsearray_data {
		void *_Nullable next[256];
	} *_Nonnull data;
	uint8_t levels;
};

struct objc_dtable {
	struct objc_dtable_level2 {
#ifdef OF_SELUID24
		struct objc_dtable_level3 {
			IMP _Nullable buckets[256];
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
#if defined(OBJC_COMPILING_AMIGA_LIBRARY) || \
    defined(OBJC_COMPILING_AMIGA_LINKLIB)
struct objc_libc {
	void *_Nullable (*_Nonnull malloc)(size_t);
	void *_Nullable (*_Nonnull calloc)(size_t, size_t);
	void *_Nullable (*_Nonnull realloc)(void *_Nullable, size_t);
	void (*_Nonnull free)(void *_Nullable);
	int (*_Nonnull vfprintf)(FILE *_Nonnull, const char *_Nonnull, va_list);
	int (*_Nonnull fflush)(FILE *_Nonnull);
	void (*_Nonnull abort)(void);
# ifdef HAVE_SJLJ_EXCEPTIONS
	int (*_Nonnull _Unwind_SjLj_RaiseException)(void *_Nonnull);
# else
	int (*_Nonnull _Unwind_RaiseException)(void *_Nonnull);
# endif
	void (*_Nonnull _Unwind_DeleteException)(void *_Nonnull);
	void *_Nullable (*_Nonnull _Unwind_GetLanguageSpecificData)(
	    void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetRegionStart)(void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetDataRelBase)(void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetTextRelBase)(void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetIP)(void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetGR)(void *_Nonnull, int);
	void (*_Nonnull _Unwind_SetIP)(void *_Nonnull, uintptr_t);
	void (*_Nonnull _Unwind_SetGR)(void *_Nonnull, int, uintptr_t);
# ifdef HAVE_SJLJ_EXCEPTIONS
	void (*_Nonnull _Unwind_SjLj_Resume)(void *_Nonnull);
# else
	void (*_Nonnull _Unwind_Resume)(void *_Nonnull);
# endif

	void (*_Nonnull __register_frame_info)(const void *_Nonnull,
	    void *_Nonnull);
	void *(*_Nonnull __deregister_frame_info)(const void *_Nonnull);











};
#endif

#ifdef OBJC_COMPILING_AMIGA_LIBRARY
# if defined(__MORPHOS__)
#  include <ppcinline/macros.h>
#  define OBJC_M68K_ARG(type, name, reg) type name = (type)REG_##reg;
# else
#  define OBJC_M68K_ARG(type, name, reg)	\
	register type reg_##name __asm__(#reg);	\
	type name = reg_##name;
# endif
# undef stdout

# undef stderr
extern FILE *stdout, *stderr;






#endif

extern void objc_register_all_categories(struct objc_symtab *_Nonnull);
extern struct objc_category *_Nullable *_Nullable
    objc_categories_for_class(Class _Nonnull);
extern void objc_unregister_all_categories(void);
extern void objc_initialize_class(Class _Nonnull);
extern void objc_update_dtable(Class _Nonnull);
extern void objc_register_all_classes(struct objc_symtab *_Nonnull);
extern Class _Nullable objc_classname_to_class(const char *_Nonnull, bool);
extern void objc_unregister_class(Class _Nonnull);
extern void objc_unregister_all_classes(void);
extern uint32_t objc_hash_string(const void *_Nonnull);
extern bool objc_equal_string(const void *_Nonnull, const void *_Nonnull);
extern struct objc_hashtable *_Nonnull objc_hashtable_new(
    objc_hashtable_hash_func, objc_hashtable_equal_func, uint32_t);
extern struct objc_hashtable_bucket objc_deleted_bucket;
extern void objc_hashtable_set(struct objc_hashtable *_Nonnull,
    const void *_Nonnull, const void *_Nonnull);
extern void *_Nullable objc_hashtable_get(struct objc_hashtable *_Nonnull,
    const void *_Nonnull);
extern void objc_hashtable_delete(struct objc_hashtable *_Nonnull,
    const void *_Nonnull);
extern void objc_hashtable_free(struct objc_hashtable *_Nonnull);
extern void objc_register_selector(struct objc_selector *_Nonnull);
extern void objc_register_all_selectors(struct objc_symtab *_Nonnull);
extern void objc_unregister_all_selectors(void);
extern struct objc_sparsearray *_Nonnull objc_sparsearray_new(uint8_t);
extern void *_Nullable objc_sparsearray_get(struct objc_sparsearray *_Nonnull,
    uintptr_t);
extern void objc_sparsearray_set(struct objc_sparsearray *_Nonnull, uintptr_t,
    void *_Nullable);
extern void objc_sparsearray_free(struct objc_sparsearray *_Nonnull);
extern struct objc_dtable *_Nonnull objc_dtable_new(void);
extern void objc_dtable_copy(struct objc_dtable *_Nonnull,
    struct objc_dtable *_Nonnull);
extern void objc_dtable_set(struct objc_dtable *_Nonnull, uint32_t,
    IMP _Nullable);
extern void objc_dtable_free(struct objc_dtable *_Nonnull);
extern void objc_dtable_cleanup(void);
extern void objc_init_static_instances(struct objc_symtab *_Nonnull);
extern void objc_forget_pending_static_instances(void);
extern void objc_zero_weak_references(id _Nonnull);
extern Class _Nullable object_getTaggedPointerClass(id _Nonnull);
#ifdef OF_HAVE_THREADS
extern void objc_global_mutex_lock(void);
extern void objc_global_mutex_unlock(void);
extern void objc_global_mutex_free(void);
#else
# define objc_global_mutex_lock()
# define objc_global_mutex_unlock()
# define objc_global_mutex_free()
#endif


static inline IMP _Nullable
objc_dtable_get(const struct objc_dtable *_Nonnull dtable, uint32_t idx)
{
#ifdef OF_SELUID24
	uint8_t i = idx >> 16;
	uint8_t j = idx >> 8;
	uint8_t k = idx;

	return dtable->buckets[i]->buckets[j]->buckets[k];
#else
	uint8_t i = idx >> 8;
	uint8_t j = idx;

	return dtable->buckets[i]->buckets[j];
#endif
}







#if defined(OF_ELF)
# if defined(OF_X86_64) || defined(OF_X86) || defined(OF_POWERPC) || \

    defined(OF_ARM64) || defined(OF_ARM) || \
    defined(OF_MIPS64_N64) || defined(OF_MIPS) || \
    defined(OF_SPARC64) || defined(OF_SPARC)
#  define OF_ASM_LOOKUP
# endif
#elif defined(OF_MACH_O)
# if defined(OF_X86_64)
#  define OF_ASM_LOOKUP
# endif
#elif defined(OF_WINDOWS)
# if defined(OF_X86_64) || defined(OF_X86)
#  define OF_ASM_LOOKUP
# endif
#endif

#define OBJC_ERROR(...)							\
	{								\
		fprintf(stderr, "[objc @ " __FILE__ ":%d] ", __LINE__);	\
		fprintf(stderr, __VA_ARGS__);				\
		fprintf(stderr, "\n");					\
		fflush(stderr);						\
		abort();						\
		OF_UNREACHABLE						\
	}

@interface DummyObject
{
	Class _Nonnull isa;
}

@property (readonly, nonatomic) bool allowsWeakReference;








<
<
<




















>



>
>
>
>
>
>
>
>
>
>
>












|
>
|
<
>
>
>
>
>
>


|

|
|
|
|
|
|
|
|
|
|


|







|
|
|













|
|
|


|
|
|

|
|
|

>


















>
>
>
>
>
>

|
>















<
<
<
<
<
<
<
<
<
<







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
#if defined(OBJC_COMPILING_AMIGA_LIBRARY) || \
    defined(OBJC_COMPILING_AMIGA_LINKLIB)
struct objc_libc {
	void *_Nullable (*_Nonnull malloc)(size_t);
	void *_Nullable (*_Nonnull calloc)(size_t, size_t);
	void *_Nullable (*_Nonnull realloc)(void *_Nullable, size_t);
	void (*_Nonnull free)(void *_Nullable);



# ifdef HAVE_SJLJ_EXCEPTIONS
	int (*_Nonnull _Unwind_SjLj_RaiseException)(void *_Nonnull);
# else
	int (*_Nonnull _Unwind_RaiseException)(void *_Nonnull);
# endif
	void (*_Nonnull _Unwind_DeleteException)(void *_Nonnull);
	void *_Nullable (*_Nonnull _Unwind_GetLanguageSpecificData)(
	    void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetRegionStart)(void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetDataRelBase)(void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetTextRelBase)(void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetIP)(void *_Nonnull);
	uintptr_t (*_Nonnull _Unwind_GetGR)(void *_Nonnull, int);
	void (*_Nonnull _Unwind_SetIP)(void *_Nonnull, uintptr_t);
	void (*_Nonnull _Unwind_SetGR)(void *_Nonnull, int, uintptr_t);
# ifdef HAVE_SJLJ_EXCEPTIONS
	void (*_Nonnull _Unwind_SjLj_Resume)(void *_Nonnull);
# else
	void (*_Nonnull _Unwind_Resume)(void *_Nonnull);
# endif
# ifdef OF_AMIGAOS_M68K
	void (*_Nonnull __register_frame_info)(const void *_Nonnull,
	    void *_Nonnull);
	void *(*_Nonnull __deregister_frame_info)(const void *_Nonnull);
# endif
# ifdef OF_MORPHOS
	void (*_Nonnull __register_frame)(void *_Nonnull);
	void (*_Nonnull __deregister_frame)(void *_Nonnull);
# endif
# ifdef OF_AMIGAOS_M68K
	int (*_Nonnull vsnprintf)(char *restrict _Nonnull str, size_t size,
	    const char *_Nonnull restrict fmt, va_list args);
# endif
	int (*_Nonnull atexit)(void (*_Nonnull)(void));
	void (*_Nonnull exit)(int);
};
#endif

#ifdef OBJC_COMPILING_AMIGA_LIBRARY
# if defined(__MORPHOS__)
#  include <ppcinline/macros.h>
#  define OBJC_M68K_ARG(type, name, reg) type name = (type)REG_##reg;
# else
#  define OBJC_M68K_ARG(type, name, reg)	\
	register type reg_##name __asm__(#reg);	\
	type name = reg_##name;
# endif

extern bool objc_init(unsigned int, struct objc_libc *);
# ifdef HAVE_SJLJ_EXCEPTIONS

#  define __gnu_objc_personality(version, actions, exClass, ex, ctx)	\
	__gnu_objc_personality_sj0(version, actions, *exClass, ex, ctx)
# else
#  define __gnu_objc_personality(version, actions, exClass, ex, ctx)	\
	__gnu_objc_personality_v0(version, actions, *exClass, ex, ctx)
# endif
#endif

extern void objc_registerAllCategories(struct objc_symtab *_Nonnull);
extern struct objc_category *_Nullable *_Nullable
    objc_categoriesForClass(Class _Nonnull);
extern void objc_unregisterAllCategories(void);
extern void objc_initializeClass(Class _Nonnull);
extern void objc_updateDTable(Class _Nonnull);
extern void objc_registerAllClasses(struct objc_symtab *_Nonnull);
extern Class _Nullable objc_classnameToClass(const char *_Nonnull, bool);
extern void objc_unregisterClass(Class _Nonnull);
extern void objc_unregisterAllClasses(void);
extern uint32_t objc_string_hash(const void *_Nonnull);
extern bool objc_string_equal(const void *_Nonnull, const void *_Nonnull);
extern struct objc_hashtable *_Nonnull objc_hashtable_new(
    objc_hashtable_hash_func, objc_hashtable_equal_func, uint32_t);
extern struct objc_hashtable_bucket objc_deletedBucket;
extern void objc_hashtable_set(struct objc_hashtable *_Nonnull,
    const void *_Nonnull, const void *_Nonnull);
extern void *_Nullable objc_hashtable_get(struct objc_hashtable *_Nonnull,
    const void *_Nonnull);
extern void objc_hashtable_delete(struct objc_hashtable *_Nonnull,
    const void *_Nonnull);
extern void objc_hashtable_free(struct objc_hashtable *_Nonnull);
extern void objc_registerSelector(struct objc_selector *_Nonnull);
extern void objc_registerAllSelectors(struct objc_symtab *_Nonnull);
extern void objc_unregisterAllSelectors(void);
extern struct objc_sparsearray *_Nonnull objc_sparsearray_new(uint8_t);
extern void *_Nullable objc_sparsearray_get(struct objc_sparsearray *_Nonnull,
    uintptr_t);
extern void objc_sparsearray_set(struct objc_sparsearray *_Nonnull, uintptr_t,
    void *_Nullable);
extern void objc_sparsearray_free(struct objc_sparsearray *_Nonnull);
extern struct objc_dtable *_Nonnull objc_dtable_new(void);
extern void objc_dtable_copy(struct objc_dtable *_Nonnull,
    struct objc_dtable *_Nonnull);
extern void objc_dtable_set(struct objc_dtable *_Nonnull, uint32_t,
    IMP _Nullable);
extern void objc_dtable_free(struct objc_dtable *_Nonnull);
extern void objc_dtable_cleanup(void);
extern void objc_initStaticInstances(struct objc_symtab *_Nonnull);
extern void objc_forgetPendingStaticInstances(void);
extern void objc_zeroWeakReferences(id _Nonnull);
extern Class _Nullable object_getTaggedPointerClass(id _Nonnull);
#ifdef OF_HAVE_THREADS
extern void objc_globalMutex_lock(void);
extern void objc_globalMutex_unlock(void);
extern void objc_globalMutex_free(void);
#else
# define objc_globalMutex_lock()
# define objc_globalMutex_unlock()
# define objc_globalMutex_free()
#endif
extern char *_Nullable objc_strdup(const char *_Nonnull string);

static inline IMP _Nullable
objc_dtable_get(const struct objc_dtable *_Nonnull dtable, uint32_t idx)
{
#ifdef OF_SELUID24
	uint8_t i = idx >> 16;
	uint8_t j = idx >> 8;
	uint8_t k = idx;

	return dtable->buckets[i]->buckets[j]->buckets[k];
#else
	uint8_t i = idx >> 8;
	uint8_t j = idx;

	return dtable->buckets[i]->buckets[j];
#endif
}

extern void OF_NO_RETURN_FUNC objc_error(const char *_Nonnull title,
    const char *_Nonnull format, ...);
#define OBJC_ERROR(...)							\
	objc_error("ObjFWRT @ " __FILE__ ":" OF_STRINGIFY(__LINE__),	\
	    __VA_ARGS__)

#if defined(OF_ELF)
# if defined(OF_X86_64) || defined(OF_X86) || \
    defined(OF_POWERPC64) || defined(OF_POWERPC) || \
    defined(OF_ARM64) || defined(OF_ARM) || \
    defined(OF_MIPS64_N64) || defined(OF_MIPS) || \
    defined(OF_SPARC64) || defined(OF_SPARC)
#  define OF_ASM_LOOKUP
# endif
#elif defined(OF_MACH_O)
# if defined(OF_X86_64)
#  define OF_ASM_LOOKUP
# endif
#elif defined(OF_WINDOWS)
# if defined(OF_X86_64) || defined(OF_X86)
#  define OF_ASM_LOOKUP
# endif
#endif











@interface DummyObject
{
	Class _Nonnull isa;
}

@property (readonly, nonatomic) bool allowsWeakReference;

Modified src/runtime/property.m from [f51e3c1883] to [822a1641e1].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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

#include <string.h>

#import "ObjFWRT.h"
#import "private.h"

#ifdef OF_HAVE_THREADS
# import "mutex.h"
# define NUM_SPINLOCKS 8	/* needs to be a power of 2 */

# define SPINLOCK_HASH(p) ((unsigned)((uintptr_t)p >> 4) & (NUM_SPINLOCKS - 1))

static of_spinlock_t spinlocks[NUM_SPINLOCKS];



#endif

#ifdef OF_HAVE_THREADS
OF_CONSTRUCTOR()
{
	for (size_t i = 0; i < NUM_SPINLOCKS; i++)
		if (!of_spinlock_new(&spinlocks[i]))
			OBJC_ERROR("Failed to initialize spinlocks!")
}
#endif

id
objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, bool atomic)
{
	if (atomic) {
		id *ptr = (id *)(void *)((char *)self + offset);
#ifdef OF_HAVE_THREADS
		unsigned hash = SPINLOCK_HASH(ptr);

		OF_ENSURE(of_spinlock_lock(&spinlocks[hash]));

		@try {
			return [[*ptr retain] autorelease];
		} @finally {
			OF_ENSURE(of_spinlock_unlock(&spinlocks[hash]));

		}
#else
		return [[*ptr retain] autorelease];
#endif
	}

	return *(id *)(void *)((char *)self + offset);
}

void
objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id value, bool atomic,
    signed char copy)
{
	if (atomic) {
		id *ptr = (id *)(void *)((char *)self + offset);
#ifdef OF_HAVE_THREADS
		unsigned hash = SPINLOCK_HASH(ptr);

		OF_ENSURE(of_spinlock_lock(&spinlocks[hash]));

		@try {
#endif
			id old = *ptr;

			switch (copy) {
			case 0:
				*ptr = [value retain];
				break;
			case 2:
				*ptr = [value mutableCopy];
				break;
			default:
				*ptr = [value copy];
			}

			[old release];
#ifdef OF_HAVE_THREADS
		} @finally {
			OF_ENSURE(of_spinlock_unlock(&spinlocks[hash]));

		}
#endif

		return;
	}

	id *ptr = (id *)(void *)((char *)self + offset);







|
|
>
|
>
|
>
>
>





|
|
|









|

|
>



|
>
















|

|
>


















|
>







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

#include <string.h>

#import "ObjFWRT.h"
#import "private.h"

#ifdef OF_HAVE_THREADS
# import "OFPlainMutex.h"
# define numSpinlocks 8	/* needs to be a power of 2 */
static OFSpinlock spinlocks[numSpinlocks];

static OF_INLINE size_t
spinlockSlot(const void *ptr)
{
	return ((size_t)((uintptr_t)ptr >> 4) & (numSpinlocks - 1));
}
#endif

#ifdef OF_HAVE_THREADS
OF_CONSTRUCTOR()
{
	for (size_t i = 0; i < numSpinlocks; i++)
		if (OFSpinlockNew(&spinlocks[i]) != 0)
			OBJC_ERROR("Failed to create spinlocks!");
}
#endif

id
objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, bool atomic)
{
	if (atomic) {
		id *ptr = (id *)(void *)((char *)self + offset);
#ifdef OF_HAVE_THREADS
		size_t slot = spinlockSlot(ptr);

		if (OFSpinlockLock(&spinlocks[slot]) != 0)
			OBJC_ERROR("Failed to lock spinlock!");
		@try {
			return [[*ptr retain] autorelease];
		} @finally {
			if (OFSpinlockUnlock(&spinlocks[slot]) != 0)
				OBJC_ERROR("Failed to unlock spinlock!");
		}
#else
		return [[*ptr retain] autorelease];
#endif
	}

	return *(id *)(void *)((char *)self + offset);
}

void
objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id value, bool atomic,
    signed char copy)
{
	if (atomic) {
		id *ptr = (id *)(void *)((char *)self + offset);
#ifdef OF_HAVE_THREADS
		size_t slot = spinlockSlot(ptr);

		if (OFSpinlockLock(&spinlocks[slot]) != 0)
			OBJC_ERROR("Failed to lock spinlock!");
		@try {
#endif
			id old = *ptr;

			switch (copy) {
			case 0:
				*ptr = [value retain];
				break;
			case 2:
				*ptr = [value mutableCopy];
				break;
			default:
				*ptr = [value copy];
			}

			[old release];
#ifdef OF_HAVE_THREADS
		} @finally {
			if (OFSpinlockUnlock(&spinlocks[slot]) != 0)
				OBJC_ERROR("Failed to unlock spinlock!");
		}
#endif

		return;
	}

	id *ptr = (id *)(void *)((char *)self + offset);
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
/* The following methods are only required for GCC >= 4.6 */
void
objc_getPropertyStruct(void *dest, const void *src, ptrdiff_t size, bool atomic,
    bool strong)
{
	if (atomic) {
#ifdef OF_HAVE_THREADS
		unsigned hash = SPINLOCK_HASH(src);

		OF_ENSURE(of_spinlock_lock(&spinlocks[hash]));

#endif
		memcpy(dest, src, size);
#ifdef OF_HAVE_THREADS
		OF_ENSURE(of_spinlock_unlock(&spinlocks[hash]));

#endif

		return;
	}

	memcpy(dest, src, size);
}

void
objc_setPropertyStruct(void *dest, const void *src, ptrdiff_t size, bool atomic,
    bool strong)
{
	if (atomic) {
#ifdef OF_HAVE_THREADS
		unsigned hash = SPINLOCK_HASH(src);

		OF_ENSURE(of_spinlock_lock(&spinlocks[hash]));

#endif
		memcpy(dest, src, size);
#ifdef OF_HAVE_THREADS
		OF_ENSURE(of_spinlock_unlock(&spinlocks[hash]));

#endif

		return;
	}

	memcpy(dest, src, size);
}







|

|
>



|
>














|

|
>



|
>







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
/* The following methods are only required for GCC >= 4.6 */
void
objc_getPropertyStruct(void *dest, const void *src, ptrdiff_t size, bool atomic,
    bool strong)
{
	if (atomic) {
#ifdef OF_HAVE_THREADS
		size_t slot = spinlockSlot(src);

		if (OFSpinlockLock(&spinlocks[slot]) != 0)
			OBJC_ERROR("Failed to lock spinlock!");
#endif
		memcpy(dest, src, size);
#ifdef OF_HAVE_THREADS
		if (OFSpinlockUnlock(&spinlocks[slot]) != 0)
			OBJC_ERROR("Failed to unlock spinlock!");
#endif

		return;
	}

	memcpy(dest, src, size);
}

void
objc_setPropertyStruct(void *dest, const void *src, ptrdiff_t size, bool atomic,
    bool strong)
{
	if (atomic) {
#ifdef OF_HAVE_THREADS
		size_t slot = spinlockSlot(src);

		if (OFSpinlockLock(&spinlocks[slot]) != 0)
			OBJC_ERROR("Failed to lock spinlock!");
#endif
		memcpy(dest, src, size);
#ifdef OF_HAVE_THREADS
		if (OFSpinlockUnlock(&spinlocks[slot]) != 0)
			OBJC_ERROR("Failed to unlock spinlock!");
#endif

		return;
	}

	memcpy(dest, src, size);
}
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
	if (class == Nil) {
		if (outCount != NULL)
			*outCount = 0;

		return NULL;
	}

	objc_global_mutex_lock();

	count = 0;
	if (class->info & OBJC_CLASS_INFO_NEW_ABI)
		for (iter = class->propertyList; iter != NULL;
		    iter = iter->next)
			count += iter->count;

	if (count == 0) {
		if (outCount != NULL)
			*outCount = 0;

		objc_global_mutex_unlock();
		return NULL;
	}

	properties = malloc((count + 1) * sizeof(objc_property_t));
	if (properties == NULL)
		OBJC_ERROR("Not enough memory to copy properties");

	i = 0;
	for (iter = class->propertyList; iter != NULL; iter = iter->next)
		for (unsigned int j = 0; j < iter->count; j++)
			properties[i++] = &iter->properties[j];

	OF_ENSURE(i == count);


	properties[count] = NULL;

	if (outCount != NULL)
		*outCount = count;

	objc_global_mutex_unlock();

	return properties;
}

const char *
property_getName(objc_property_t property)
{







|











|











>
|
>
>





|







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
	if (class == Nil) {
		if (outCount != NULL)
			*outCount = 0;

		return NULL;
	}

	objc_globalMutex_lock();

	count = 0;
	if (class->info & OBJC_CLASS_INFO_NEW_ABI)
		for (iter = class->propertyList; iter != NULL;
		    iter = iter->next)
			count += iter->count;

	if (count == 0) {
		if (outCount != NULL)
			*outCount = 0;

		objc_globalMutex_unlock();
		return NULL;
	}

	properties = malloc((count + 1) * sizeof(objc_property_t));
	if (properties == NULL)
		OBJC_ERROR("Not enough memory to copy properties");

	i = 0;
	for (iter = class->propertyList; iter != NULL; iter = iter->next)
		for (unsigned int j = 0; j < iter->count; j++)
			properties[i++] = &iter->properties[j];

	if (i != count)
		OBJC_ERROR("Fatal internal inconsistency!");

	properties[count] = NULL;

	if (outCount != NULL)
		*outCount = count;

	objc_globalMutex_unlock();

	return properties;
}

const char *
property_getName(objc_property_t property)
{
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
	bool nullIsError = false;

	if (strlen(name) != 1)
		return NULL;

	switch (*name) {
	case 'T':
		ret = of_strdup(property->getter.typeEncoding);
		nullIsError = true;
		break;
	case 'G':
		if (property->attributes & OBJC_PROPERTY_GETTER) {
			ret = of_strdup(property->getter.name);
			nullIsError = true;
		}
		break;
	case 'S':
		if (property->attributes & OBJC_PROPERTY_SETTER) {
			ret = of_strdup(property->setter.name);
			nullIsError = true;
		}
		break;
#define BOOL_CASE(name, field, flag)		\
	case name:				\
		if (property->field & flag) {	\
			ret = calloc(1, 1);	\







|




|





|







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
	bool nullIsError = false;

	if (strlen(name) != 1)
		return NULL;

	switch (*name) {
	case 'T':
		ret = objc_strdup(property->getter.typeEncoding);
		nullIsError = true;
		break;
	case 'G':
		if (property->attributes & OBJC_PROPERTY_GETTER) {
			ret = objc_strdup(property->getter.name);
			nullIsError = true;
		}
		break;
	case 'S':
		if (property->attributes & OBJC_PROPERTY_SETTER) {
			ret = objc_strdup(property->setter.name);
			nullIsError = true;
		}
		break;
#define BOOL_CASE(name, field, flag)		\
	case name:				\
		if (property->field & flag) {	\
			ret = calloc(1, 1);	\
251
252
253
254
255
256
257
258
259
260
261
	BOOL_CASE('D', extendedAttributes, OBJC_PROPERTY_DYNAMIC)
	BOOL_CASE('W', extendedAttributes, OBJC_PROPERTY_WEAK)
#undef BOOL_CASE
	}

	if (nullIsError && ret == NULL)
		OBJC_ERROR("Not enough memory to copy property attribute "
		    "value");

	return ret;
}







|



265
266
267
268
269
270
271
272
273
274
275
	BOOL_CASE('D', extendedAttributes, OBJC_PROPERTY_DYNAMIC)
	BOOL_CASE('W', extendedAttributes, OBJC_PROPERTY_WEAK)
#undef BOOL_CASE
	}

	if (nullIsError && ret == NULL)
		OBJC_ERROR("Not enough memory to copy property attribute "
		    "value!");

	return ret;
}

Modified src/runtime/protocol.m from [1569f85418] to [30a1ffceef].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	for (struct objc_protocol_list *protocolList = class->protocols;
	    protocolList != NULL; protocolList = protocolList->next)
		for (long i = 0; i < protocolList->count; i++)
			if (protocol_conformsToProtocol(protocolList->list[i],
			    protocol))
				return true;

	objc_global_mutex_lock();

	if ((categories = objc_categories_for_class(class)) == NULL) {
		objc_global_mutex_unlock();
		return false;
	}

	for (long i = 0; categories[i] != NULL; i++) {
		for (struct objc_protocol_list *protocolList =
		    categories[i]->protocols; protocolList != NULL;
		    protocolList = protocolList->next) {
			for (long j = 0; j < protocolList->count; j++) {
				if (protocol_conformsToProtocol(
				    protocolList->list[j], protocol)) {
					objc_global_mutex_unlock();
					return true;
				}
			}
		}
	}

	objc_global_mutex_unlock();

	return false;
}







|

|
|










|






|



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
	for (struct objc_protocol_list *protocolList = class->protocols;
	    protocolList != NULL; protocolList = protocolList->next)
		for (long i = 0; i < protocolList->count; i++)
			if (protocol_conformsToProtocol(protocolList->list[i],
			    protocol))
				return true;

	objc_globalMutex_lock();

	if ((categories = objc_categoriesForClass(class)) == NULL) {
		objc_globalMutex_unlock();
		return false;
	}

	for (long i = 0; categories[i] != NULL; i++) {
		for (struct objc_protocol_list *protocolList =
		    categories[i]->protocols; protocolList != NULL;
		    protocolList = protocolList->next) {
			for (long j = 0; j < protocolList->count; j++) {
				if (protocol_conformsToProtocol(
				    protocolList->list[j], protocol)) {
					objc_globalMutex_unlock();
					return true;
				}
			}
		}
	}

	objc_globalMutex_unlock();

	return false;
}

Modified src/runtime/selector.m from [6cde6e59e2] to [9a44ebdc88].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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

#import "ObjFWRT.h"
#import "private.h"

#import "macros.h"

#ifdef OF_SELUID24
# define SEL_MAX 0xFFFFFF
# define SEL_SIZE 3
#else
# define SEL_MAX 0xFFFF
# define SEL_SIZE 2
#endif

static struct objc_hashtable *selectors = NULL;
static uint32_t selectorsCount = 0;
static struct objc_sparsearray *selectorNames = NULL;
static void **freeList = NULL;
static size_t freeListCount = 0;

void
objc_register_selector(struct objc_selector *selector)
{
	SEL existingSelector;
	const char *name;

	if (selectorsCount > SEL_MAX)
		OBJC_ERROR("Out of selector slots!");

	if (selectors == NULL)
		selectors = objc_hashtable_new(
		    objc_hash_string, objc_equal_string, 2);
	else if ((existingSelector = objc_hashtable_get(selectors,
	    (const char *)selector->UID)) != NULL) {
		selector->UID = existingSelector->UID;
		return;
	}

	if (selectorNames == NULL)
		selectorNames = objc_sparsearray_new(SEL_SIZE);

	name = (const char *)selector->UID;
	selector->UID = selectorsCount++;

	objc_hashtable_set(selectors, name, selector);
	objc_sparsearray_set(selectorNames, (uint32_t)selector->UID,
	    (void *)name);
}

SEL
sel_registerName(const char *name)
{
	struct objc_selector *selector;

	objc_global_mutex_lock();

	if (selectors != NULL &&
	    (selector = objc_hashtable_get(selectors, name)) != NULL) {
		objc_global_mutex_unlock();
		return (SEL)selector;
	}

	if ((selector = malloc(sizeof(*selector))) == NULL)
		OBJC_ERROR("Not enough memory to allocate selector!");

	if ((selector->UID = (uintptr_t)of_strdup(name)) == 0)
		OBJC_ERROR("Not enough memory to allocate selector!");

	selector->typeEncoding = NULL;

	if ((freeList = realloc(freeList,
	    sizeof(void *) * (freeListCount + 2))) == NULL)
		OBJC_ERROR("Not enough memory to allocate selector!");

	freeList[freeListCount++] = selector;
	freeList[freeListCount++] = (char *)selector->UID;

	objc_register_selector(selector);

	objc_global_mutex_unlock();
	return (SEL)selector;
}

void
objc_register_all_selectors(struct objc_symtab *symtab)
{
	struct objc_selector *selector;

	if (symtab->selectorRefs == NULL)
		return;

	for (selector = symtab->selectorRefs; selector->UID != 0; selector++)
		objc_register_selector(selector);
}

const char *
sel_getName(SEL selector)
{
	const char *ret;

	objc_global_mutex_lock();
	ret = objc_sparsearray_get(selectorNames, (uint32_t)selector->UID);
	objc_global_mutex_unlock();

	return ret;
}

bool
sel_isEqual(SEL selector1, SEL selector2)
{
	return (selector1->UID == selector2->UID);
}

void
objc_unregister_all_selectors(void)
{
	objc_hashtable_free(selectors);
	objc_sparsearray_free(selectorNames);

	if (freeList != NULL) {
		for (size_t i = 0; i < freeListCount; i++)
			free(freeList[i]);







|
|

|
|









|




|




|







|














|



|



|
<
<
|











|

|




|







|







|

|











|







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

#import "ObjFWRT.h"
#import "private.h"

#import "macros.h"

#ifdef OF_SELUID24
static const uint32_t maxSel = 0xFFFFFF;
static const uint8_t selLevels = 3;
#else
static const uint32_t maxSel = 0xFFFF;
static const uint8_t selLevels = 2;
#endif

static struct objc_hashtable *selectors = NULL;
static uint32_t selectorsCount = 0;
static struct objc_sparsearray *selectorNames = NULL;
static void **freeList = NULL;
static size_t freeListCount = 0;

void
objc_registerSelector(struct objc_selector *selector)
{
	SEL existingSelector;
	const char *name;

	if (selectorsCount > maxSel)
		OBJC_ERROR("Out of selector slots!");

	if (selectors == NULL)
		selectors = objc_hashtable_new(
		    objc_string_hash, objc_string_equal, 2);
	else if ((existingSelector = objc_hashtable_get(selectors,
	    (const char *)selector->UID)) != NULL) {
		selector->UID = existingSelector->UID;
		return;
	}

	if (selectorNames == NULL)
		selectorNames = objc_sparsearray_new(selLevels);

	name = (const char *)selector->UID;
	selector->UID = selectorsCount++;

	objc_hashtable_set(selectors, name, selector);
	objc_sparsearray_set(selectorNames, (uint32_t)selector->UID,
	    (void *)name);
}

SEL
sel_registerName(const char *name)
{
	struct objc_selector *selector;

	objc_globalMutex_lock();

	if (selectors != NULL &&
	    (selector = objc_hashtable_get(selectors, name)) != NULL) {
		objc_globalMutex_unlock();
		return (SEL)selector;
	}

	if ((selector = malloc(sizeof(*selector))) == NULL ||


	    (selector->UID = (uintptr_t)objc_strdup(name)) == 0)
		OBJC_ERROR("Not enough memory to allocate selector!");

	selector->typeEncoding = NULL;

	if ((freeList = realloc(freeList,
	    sizeof(void *) * (freeListCount + 2))) == NULL)
		OBJC_ERROR("Not enough memory to allocate selector!");

	freeList[freeListCount++] = selector;
	freeList[freeListCount++] = (char *)selector->UID;

	objc_registerSelector(selector);

	objc_globalMutex_unlock();
	return (SEL)selector;
}

void
objc_registerAllSelectors(struct objc_symtab *symtab)
{
	struct objc_selector *selector;

	if (symtab->selectorRefs == NULL)
		return;

	for (selector = symtab->selectorRefs; selector->UID != 0; selector++)
		objc_registerSelector(selector);
}

const char *
sel_getName(SEL selector)
{
	const char *ret;

	objc_globalMutex_lock();
	ret = objc_sparsearray_get(selectorNames, (uint32_t)selector->UID);
	objc_globalMutex_unlock();

	return ret;
}

bool
sel_isEqual(SEL selector1, SEL selector2)
{
	return (selector1->UID == selector2->UID);
}

void
objc_unregisterAllSelectors(void)
{
	objc_hashtable_free(selectors);
	objc_sparsearray_free(selectorNames);

	if (freeList != NULL) {
		for (size_t i = 0; i < freeListCount; i++)
			free(freeList[i]);

Modified src/runtime/sparsearray.m from [454092d9a3] to [5acb08c6c8].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#include <stdio.h>
#include <stdlib.h>

#import "ObjFWRT.h"
#import "private.h"

struct objc_sparsearray *
objc_sparsearray_new(uint8_t indexSize)
{
	struct objc_sparsearray *sparsearray;

	if ((sparsearray = calloc(1, sizeof(*sparsearray))) == NULL)
		OBJC_ERROR("Failed to allocate memory for sparse array!");

	if ((sparsearray->data = calloc(1, sizeof(*sparsearray->data))) == NULL)
		OBJC_ERROR("Failed to allocate memory for sparse array!");

	sparsearray->indexSize = indexSize;

	return sparsearray;
}

void *
objc_sparsearray_get(struct objc_sparsearray *sparsearray, uintptr_t idx)
{
	struct objc_sparsearray_data *iter = sparsearray->data;

	for (uint8_t i = 0; i < sparsearray->indexSize - 1; i++) {
		uintptr_t j =
		    (idx >> ((sparsearray->indexSize - i - 1) * 8)) & 0xFF;

		if ((iter = iter->next[j]) == NULL)
			return NULL;
	}

	return iter->next[idx & 0xFF];
}

void
objc_sparsearray_set(struct objc_sparsearray *sparsearray, uintptr_t idx,
    void *value)
{
	struct objc_sparsearray_data *iter = sparsearray->data;

	for (uint8_t i = 0; i < sparsearray->indexSize - 1; i++) {
		uintptr_t j =
		    (idx >> ((sparsearray->indexSize - i - 1) * 8)) & 0xFF;

		if (iter->next[j] == NULL)
			if ((iter->next[j] = calloc(1,
			    sizeof(struct objc_sparsearray_data))) == NULL)
				OBJC_ERROR("Failed to allocate memory for "
				    "sparse array!");








|



|
<
<
|


|









|

|














|

|







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
#include <stdio.h>
#include <stdlib.h>

#import "ObjFWRT.h"
#import "private.h"

struct objc_sparsearray *
objc_sparsearray_new(uint8_t levels)
{
	struct objc_sparsearray *sparsearray;

	if ((sparsearray = calloc(1, sizeof(*sparsearray))) == NULL ||


	    (sparsearray->data = calloc(1, sizeof(*sparsearray->data))) == NULL)
		OBJC_ERROR("Failed to allocate memory for sparse array!");

	sparsearray->levels = levels;

	return sparsearray;
}

void *
objc_sparsearray_get(struct objc_sparsearray *sparsearray, uintptr_t idx)
{
	struct objc_sparsearray_data *iter = sparsearray->data;

	for (uint8_t i = 0; i < sparsearray->levels - 1; i++) {
		uintptr_t j =
		    (idx >> ((sparsearray->levels - i - 1) * 8)) & 0xFF;

		if ((iter = iter->next[j]) == NULL)
			return NULL;
	}

	return iter->next[idx & 0xFF];
}

void
objc_sparsearray_set(struct objc_sparsearray *sparsearray, uintptr_t idx,
    void *value)
{
	struct objc_sparsearray_data *iter = sparsearray->data;

	for (uint8_t i = 0; i < sparsearray->levels - 1; i++) {
		uintptr_t j =
		    (idx >> ((sparsearray->levels - i - 1) * 8)) & 0xFF;

		if (iter->next[j] == NULL)
			if ((iter->next[j] = calloc(1,
			    sizeof(struct objc_sparsearray_data))) == NULL)
				OBJC_ERROR("Failed to allocate memory for "
				    "sparse array!");

88
89
90
91
92
93
94
95
96
97

	free(data);
}

void
objc_sparsearray_free(struct objc_sparsearray *sparsearray)
{
	freeSparsearrayData(sparsearray->data, sparsearray->indexSize);
	free(sparsearray);
}







|


84
85
86
87
88
89
90
91
92
93

	free(data);
}

void
objc_sparsearray_free(struct objc_sparsearray *sparsearray)
{
	freeSparsearrayData(sparsearray->data, sparsearray->levels);
	free(sparsearray);
}

Modified src/runtime/static-instances.m from [314b61ed5d] to [a4b6846d46].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#import "ObjFWRT.h"
#import "private.h"

static struct objc_static_instances **staticInstancesList = NULL;
static size_t staticInstancesCount = 0;

void
objc_init_static_instances(struct objc_symtab *symtab)
{
	struct objc_static_instances **staticInstances;

	/* Check if the class for a static instance became available */
	for (size_t i = 0; i < staticInstancesCount; i++) {
		Class class = objc_lookUpClass(
		    staticInstancesList[i]->className);







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#import "ObjFWRT.h"
#import "private.h"

static struct objc_static_instances **staticInstancesList = NULL;
static size_t staticInstancesCount = 0;

void
objc_initStaticInstances(struct objc_symtab *symtab)
{
	struct objc_static_instances **staticInstances;

	/* Check if the class for a static instance became available */
	for (size_t i = 0; i < staticInstancesCount; i++) {
		Class class = objc_lookUpClass(
		    staticInstancesList[i]->className);
93
94
95
96
97
98
99
100
101
102
103
104
105
			staticInstancesList[staticInstancesCount++] =
			    *staticInstances;
		}
	}
}

void
objc_forget_pending_static_instances()
{
	free(staticInstancesList);
	staticInstancesList = NULL;
	staticInstancesCount = 0;
}







|





91
92
93
94
95
96
97
98
99
100
101
102
103
			staticInstancesList[staticInstancesCount++] =
			    *staticInstances;
		}
	}
}

void
objc_forgetPendingStaticInstances()
{
	free(staticInstancesList);
	staticInstancesList = NULL;
	staticInstancesCount = 0;
}

Modified src/runtime/synchronized.m from [dcf43cf9c5] to [2eb69df2c4].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#include <stdio.h>
#include <stdlib.h>

#import "ObjFWRT.h"
#import "private.h"

#ifdef OF_HAVE_THREADS
# import "mutex.h"

static struct lock_s {
	id	      object;
	int	      count;
	of_rmutex_t   rmutex;
	struct lock_s *next;
} *locks = NULL;

static of_mutex_t mutex;

OF_CONSTRUCTOR()
{
	if (!of_mutex_new(&mutex))
		OBJC_ERROR("Failed to create mutex!")
}
#endif

int
objc_sync_enter(id object)
{
	if (object == nil)
		return 0;

#ifdef OF_HAVE_THREADS
	struct lock_s *lock;

	if (!of_mutex_lock(&mutex))
		OBJC_ERROR("Failed to lock mutex!");

	/* Look if we already have a lock */
	for (lock = locks; lock != NULL; lock = lock->next) {
		if (lock->object != object)
			continue;

		lock->count++;

		if (!of_mutex_unlock(&mutex))
			OBJC_ERROR("Failed to unlock mutex!");

		if (!of_rmutex_lock(&lock->rmutex))
			OBJC_ERROR("Failed to lock mutex!");

		return 0;
	}

	/* Create a new lock */
	if ((lock = malloc(sizeof(*lock))) == NULL)
		OBJC_ERROR("Failed to allocate memory for mutex!");

	if (!of_rmutex_new(&lock->rmutex))
		OBJC_ERROR("Failed to create mutex!");

	lock->object = object;
	lock->count = 1;
	lock->next = locks;

	locks = lock;

	if (!of_mutex_unlock(&mutex))
		OBJC_ERROR("Failed to unlock mutex!");

	if (!of_rmutex_lock(&lock->rmutex))
		OBJC_ERROR("Failed to lock mutex!");
#endif

	return 0;
}

int
objc_sync_exit(id object)
{
	if (object == nil)
		return 0;

#ifdef OF_HAVE_THREADS
	struct lock_s *lock, *last = NULL;

	if (!of_mutex_lock(&mutex))
		OBJC_ERROR("Failed to lock mutex!");

	for (lock = locks; lock != NULL; lock = lock->next) {
		if (lock->object != object) {
			last = lock;
			continue;
		}

		if (!of_rmutex_unlock(&lock->rmutex))
			OBJC_ERROR("Failed to unlock mutex!");

		if (--lock->count == 0) {
			if (!of_rmutex_free(&lock->rmutex))
				OBJC_ERROR("Failed to destroy mutex!");

			if (last != NULL)
				last->next = lock->next;
			if (locks == lock)
				locks = lock->next;

			free(lock);
		}

		if (!of_mutex_unlock(&mutex))
			OBJC_ERROR("Failed to unlock mutex!");

		return 0;
	}

	OBJC_ERROR("objc_sync_exit() was called for an object not locked!");
#else
	return 0;
#endif
}







|

|
|
|
|
|


|



|
|










|

|









|


|









|








|


|













|

|








|



|










|





|




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
#include <stdio.h>
#include <stdlib.h>

#import "ObjFWRT.h"
#import "private.h"

#ifdef OF_HAVE_THREADS
# import "OFPlainMutex.h"

static struct Lock {
	id object;
	int count;
	OFPlainRecursiveMutex rmutex;
	struct Lock *next;
} *locks = NULL;

static OFPlainMutex mutex;

OF_CONSTRUCTOR()
{
	if (OFPlainMutexNew(&mutex) != 0)
		OBJC_ERROR("Failed to create mutex!");
}
#endif

int
objc_sync_enter(id object)
{
	if (object == nil)
		return 0;

#ifdef OF_HAVE_THREADS
	struct Lock *lock;

	if (OFPlainMutexLock(&mutex) != 0)
		OBJC_ERROR("Failed to lock mutex!");

	/* Look if we already have a lock */
	for (lock = locks; lock != NULL; lock = lock->next) {
		if (lock->object != object)
			continue;

		lock->count++;

		if (OFPlainMutexUnlock(&mutex) != 0)
			OBJC_ERROR("Failed to unlock mutex!");

		if (OFPlainRecursiveMutexLock(&lock->rmutex) != 0)
			OBJC_ERROR("Failed to lock mutex!");

		return 0;
	}

	/* Create a new lock */
	if ((lock = malloc(sizeof(*lock))) == NULL)
		OBJC_ERROR("Failed to allocate memory for mutex!");

	if (OFPlainRecursiveMutexNew(&lock->rmutex) != 0)
		OBJC_ERROR("Failed to create mutex!");

	lock->object = object;
	lock->count = 1;
	lock->next = locks;

	locks = lock;

	if (OFPlainMutexUnlock(&mutex) != 0)
		OBJC_ERROR("Failed to unlock mutex!");

	if (OFPlainRecursiveMutexLock(&lock->rmutex) != 0)
		OBJC_ERROR("Failed to lock mutex!");
#endif

	return 0;
}

int
objc_sync_exit(id object)
{
	if (object == nil)
		return 0;

#ifdef OF_HAVE_THREADS
	struct Lock *lock, *last = NULL;

	if (OFPlainMutexLock(&mutex) != 0)
		OBJC_ERROR("Failed to lock mutex!");

	for (lock = locks; lock != NULL; lock = lock->next) {
		if (lock->object != object) {
			last = lock;
			continue;
		}

		if (OFPlainRecursiveMutexUnlock(&lock->rmutex) != 0)
			OBJC_ERROR("Failed to unlock mutex!");

		if (--lock->count == 0) {
			if (OFPlainRecursiveMutexFree(&lock->rmutex) != 0)
				OBJC_ERROR("Failed to destroy mutex!");

			if (last != NULL)
				last->next = lock->next;
			if (locks == lock)
				locks = lock->next;

			free(lock);
		}

		if (OFPlainMutexUnlock(&mutex) != 0)
			OBJC_ERROR("Failed to unlock mutex!");

		return 0;
	}

	OBJC_ERROR("objc_sync_exit() was called for an unlocked object!");
#else
	return 0;
#endif
}

Modified src/runtime/tagged-pointer.m from [347f048030] to [747427397b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "ObjFWRT.h"

#import "private.h"

#define TAGGED_POINTER_BITS 4
#define NUM_TAGGED_POINTER_CLASSES (1 << (TAGGED_POINTER_BITS - 1))

Class objc_tagged_pointer_classes[NUM_TAGGED_POINTER_CLASSES];
static int taggedPointerClassesCount;
uintptr_t objc_tagged_pointer_secret;

void
objc_setTaggedPointerSecret(uintptr_t secret)
{
	objc_tagged_pointer_secret = secret & ~(uintptr_t)1;
}

int
objc_registerTaggedPointerClass(Class class)
{
	int i;

	objc_global_mutex_lock();

	if (taggedPointerClassesCount == NUM_TAGGED_POINTER_CLASSES) {
		objc_global_mutex_unlock();
		return -1;
	}

	i = taggedPointerClassesCount++;
	objc_tagged_pointer_classes[i] = class;

	objc_global_mutex_unlock();

	return i;
}

bool
object_isTaggedPointer(id object)
{
	uintptr_t pointer = (uintptr_t)object;

	return pointer & 1;
}

Class
object_getTaggedPointerClass(id object)
{
	uintptr_t pointer = (uintptr_t)object ^ objc_tagged_pointer_secret;

	pointer &= (1 << TAGGED_POINTER_BITS) - 1;
	pointer >>= 1;

	if (pointer >= NUM_TAGGED_POINTER_CLASSES)
		return Nil;

	return objc_tagged_pointer_classes[pointer];
}

uintptr_t
object_getTaggedPointerValue(id object)
{
	uintptr_t pointer = (uintptr_t)object ^ objc_tagged_pointer_secret;

	pointer >>= TAGGED_POINTER_BITS;

	return pointer;
}

id
objc_createTaggedPointer(int class, uintptr_t value)
{
	uintptr_t pointer;

	if (class < 0 || class >= NUM_TAGGED_POINTER_CLASSES)
		return nil;

	if (value > (UINTPTR_MAX >> TAGGED_POINTER_BITS))
		return nil;

	pointer = (class << 1) | 1;
	pointer |= (value << TAGGED_POINTER_BITS);

	return (id)(pointer ^ objc_tagged_pointer_secret);
}

<
<
|

















|
|

|

|




|







|

|
|




|

|















|

|


|


|





|

|









|


|



|

|

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
/*


 * 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 "ObjFWRT.h"

#import "private.h"

#define numTaggedPointerBits 4
#define maxNumTaggedPointerClasses (1 << (numTaggedPointerBits - 1))

Class objc_taggedPointerClasses[maxNumTaggedPointerClasses];
static int taggedPointerClassesCount;
uintptr_t objc_taggedPointerSecret;

void
objc_setTaggedPointerSecret(uintptr_t secret)
{
	objc_taggedPointerSecret = secret & ~(uintptr_t)1;
}

int
objc_registerTaggedPointerClass(Class class)
{
	int i;

	objc_globalMutex_lock();

	if (taggedPointerClassesCount == maxNumTaggedPointerClasses) {
		objc_globalMutex_unlock();
		return -1;
	}

	i = taggedPointerClassesCount++;
	objc_taggedPointerClasses[i] = class;

	objc_globalMutex_unlock();

	return i;
}

bool
object_isTaggedPointer(id object)
{
	uintptr_t pointer = (uintptr_t)object;

	return pointer & 1;
}

Class
object_getTaggedPointerClass(id object)
{
	uintptr_t pointer = (uintptr_t)object ^ objc_taggedPointerSecret;

	pointer &= (1 << numTaggedPointerBits) - 1;
	pointer >>= 1;

	if (pointer >= maxNumTaggedPointerClasses)
		return Nil;

	return objc_taggedPointerClasses[pointer];
}

uintptr_t
object_getTaggedPointerValue(id object)
{
	uintptr_t pointer = (uintptr_t)object ^ objc_taggedPointerSecret;

	pointer >>= numTaggedPointerBits;

	return pointer;
}

id
objc_createTaggedPointer(int class, uintptr_t value)
{
	uintptr_t pointer;

	if (class < 0 || class >= maxNumTaggedPointerClasses)
		return nil;

	if (value > (UINTPTR_MAX >> numTaggedPointerBits))
		return nil;

	pointer = (class << 1) | 1;
	pointer |= (value << numTaggedPointerBits);

	return (id)(pointer ^ objc_taggedPointerSecret);
}

Modified src/runtime/threading.m from [e792c643b3] to [f4000ae0f7].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdio.h>
#include <stdlib.h>

#import "ObjFWRT.h"
#import "private.h"

#import "mutex.h"
#import "once.h"

static of_rmutex_t globalMutex;

static void
init(void)
{
	if (!of_rmutex_new(&globalMutex))
		OBJC_ERROR("Failed to create global mutex!");
}

void
objc_global_mutex_lock(void)
{
	static of_once_t once_control = OF_ONCE_INIT;
	of_once(&once_control, init);

	if (!of_rmutex_lock(&globalMutex))
		OBJC_ERROR("Failed to lock global mutex!");
}

void
objc_global_mutex_unlock(void)
{
	if (!of_rmutex_unlock(&globalMutex))
		OBJC_ERROR("Failed to unlock global mutex!");
}

<
<
|




















>
|
|

|




|




|

|
|

|




|

|


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
/*


 * 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 <stdio.h>
#include <stdlib.h>

#import "ObjFWRT.h"
#import "private.h"

#import "OFOnce.h"
#import "OFPlainMutex.h"

static OFPlainRecursiveMutex globalMutex;

static void
init(void)
{
	if (OFPlainRecursiveMutexNew(&globalMutex) != 0)
		OBJC_ERROR("Failed to create global mutex!");
}

void
objc_globalMutex_lock(void)
{
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFOnce(&onceControl, init);

	if (OFPlainRecursiveMutexLock(&globalMutex) != 0)
		OBJC_ERROR("Failed to lock global mutex!");
}

void
objc_globalMutex_unlock(void)
{
	if (OFPlainRecursiveMutexUnlock(&globalMutex) != 0)
		OBJC_ERROR("Failed to unlock global mutex!");
}

Added src/tls/Info.plist.in version [06bf405764].













































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleExecutable</key>
	<string>ObjFWTLS</string>
	<key>CFBundleName</key>
	<string>ObjFWTLS</string>
	<key>CFBundleIdentifier</key>
	<string>im.nil.objfw.tls</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundlePackageType</key>
	<string>FMWK</string>
	<key>CFBundleVersion</key>
	<string>@BUNDLE_VERSION@</string>
	<key>CFBundleShortVersionString</key>
	<string>@BUNDLE_SHORT_VERSION@</string>
	<key>MinimumOSVersion</key>
	<string>9.0</string>
</dict>
</plist>

Added src/tls/Makefile version [a6c2c67827].















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
include ../../extra.mk

DISTCLEAN = Info.plist

SHARED_LIB = ${OBJFWTLS_SHARED_LIB}
STATIC_LIB = ${OBJFWTLS_STATIC_LIB}
FRAMEWORK = ${OBJFWTLS_FRAMEWORK}
LIB_MAJOR = ${OBJFW_LIB_MAJOR}
LIB_MINOR = ${OBJFW_LIB_MINOR}

INCLUDES := ObjFWTLS.h
SRCS = ${OF_GNUTLS_TLS_STREAM_M}		\
       ${OF_OPENSSL_TLS_STREAM_M}		\
       ${OF_SECURE_TRANSPORT_TLS_STREAM_M}

includesubdir = ObjFWTLS

include ../../buildsys.mk

CPPFLAGS += -I. -I.. -I../.. -I../exceptions -I../runtime ${TLS_CPPFLAGS}
LD = ${OBJC}
FRAMEWORK_LIBS := ${TLS_LIBS} -F.. -framework ObjFW ${LIBS}
LIBS := ${TLS_LIBS} -L.. -lobjfw -L../runtime ${RUNTIME_LIBS} ${LIBS}

Added src/tls/OFGnuTLSTLSStream.h version [d33b11b9cb].





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFTLSStream.h"

#include <gnutls/gnutls.h>

OF_ASSUME_NONNULL_BEGIN

@interface OFGnuTLSTLSStream: OFTLSStream <OFStreamDelegate>
{
	bool _initialized, _handshakeDone;
	gnutls_session_t _session;
	OFString *_host;
}
@end

OF_ASSUME_NONNULL_END

Added src/tls/OFGnuTLSTLSStream.m version [fad8797bf6].









































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
/*
 * 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 "OFGnuTLSTLSStream.h"
#import "OFData.h"

#import "OFAlreadyConnectedException.h"
#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFReadFailedException.h"
#import "OFTLSHandshakeFailedException.h"
#import "OFWriteFailedException.h"

int _ObjFWTLS_reference;
static gnutls_certificate_credentials_t systemTrustCreds;

#ifndef GNUTLS_SAFE_PADDING_CHECK
/* Some older versions don't have it. */
# define GNUTLS_SAFE_PADDING_CHECK 0
#endif

@implementation OFGnuTLSTLSStream
static ssize_t
readFunc(gnutls_transport_ptr_t transport, void *buffer, size_t length)
{
	OFGnuTLSTLSStream *stream = (OFGnuTLSTLSStream *)transport;

	@try {
		length = [stream.underlyingStream readIntoBuffer: buffer
							  length: length];
	} @catch (OFReadFailedException *e) {
		gnutls_transport_set_errno(stream->_session, e.errNo);
		return -1;
	}

	if (length == 0 && !stream.underlyingStream.atEndOfStream) {
		gnutls_transport_set_errno(stream->_session, EAGAIN);
		return -1;
	}

	return length;
}

static ssize_t
writeFunc(gnutls_transport_ptr_t transport, const void *buffer, size_t length)
{
	OFGnuTLSTLSStream *stream = (OFGnuTLSTLSStream *)transport;

	@try {
		[stream.underlyingStream writeBuffer: buffer length: length];
	} @catch (OFWriteFailedException *e) {
		gnutls_transport_set_errno(stream->_session, e.errNo);

		if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN)
			return e.bytesWritten;

		return -1;
	}

	return length;
}

+ (void)load
{
	if (OFTLSStreamImplementation == Nil)
		OFTLSStreamImplementation = self;
}

+ (void)initialize
{
	if (self != [OFGnuTLSTLSStream class])
		return;

	if (gnutls_certificate_allocate_credentials(&systemTrustCreds) !=
	    GNUTLS_E_SUCCESS ||
	    gnutls_certificate_set_x509_system_trust(systemTrustCreds) < 0)
		@throw [OFInitializationFailedException exception];
}

- (instancetype)initWithStream: (OFStream <OFReadyForReadingObserving,
				     OFReadyForWritingObserving> *)stream
{
	self = [super initWithStream: stream];

	@try {
		_underlyingStream.delegate = self;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_initialized)
		[self close];

	[_host release];

	[super dealloc];
}

- (void)close
{
	if (!_initialized)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_handshakeDone)
		gnutls_bye(_session, GNUTLS_SHUT_WR);

	gnutls_deinit(_session);
	_initialized = false;

	[_host release];
	_host = nil;

	[super close];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
{
	ssize_t ret;

	if (!_handshakeDone)
		@throw [OFNotOpenException exceptionWithObject: self];

	if ((ret = gnutls_record_recv(_session, buffer, length)) < 0) {
		/*
		 * The underlying stream might have had data ready, but not
		 * enough for GnuTLS to return decrypted data. This means the
		 * caller might have observed the TLS stream for reading, got a
		 * ready signal and read - and expects the read to succeed, not
		 * to fail with EWOULDBLOCK/EAGAIN, as it was signaled ready.
		 * Therefore, return 0, as we could read 0 decrypted bytes, but
		 * cleared the ready signal of the underlying stream.
		 */
		if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
			return 0;

		/* FIXME: Translate error to errNo */
		@throw [OFReadFailedException exceptionWithObject: self
						  requestedLength: length
							    errNo: 0];
	}

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
{
	ssize_t ret;

	if (!_handshakeDone)
		@throw [OFNotOpenException exceptionWithObject: self];

	if ((ret = gnutls_record_send(_session, buffer, length)) < 0) {
		if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
			return 0;

		/* FIXME: Translate error to errNo */
		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: ret
							     errNo: 0];
	}

	return ret;
}

- (bool)hasDataInReadBuffer
{
	if (gnutls_record_check_pending(_session) > 0)
		return true;

	return super.hasDataInReadBuffer;
}

- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
				runLoopMode: (OFRunLoopMode)runLoopMode
{
	static const OFTLSStreamErrorCode initFailedErrorCode =
	    OFTLSStreamErrorCodeInitializationFailed;
	id exception = nil;
	int status;

	if (_initialized)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if (gnutls_init(&_session, GNUTLS_CLIENT | GNUTLS_NONBLOCK |
	    GNUTLS_SAFE_PADDING_CHECK) != GNUTLS_E_SUCCESS)
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];

	_initialized = true;

	gnutls_transport_set_ptr(_session, self);
	gnutls_transport_set_pull_function(_session, readFunc);
	gnutls_transport_set_push_function(_session, writeFunc);

	if (gnutls_set_default_priority(_session) != GNUTLS_E_SUCCESS ||
	    gnutls_credentials_set(_session, GNUTLS_CRD_CERTIFICATE,
	    systemTrustCreds) != GNUTLS_E_SUCCESS)
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];

	_host = [host copy];

	if (gnutls_server_name_set(_session, GNUTLS_NAME_DNS,
	    _host.UTF8String, _host.UTF8StringLength) != GNUTLS_E_SUCCESS)
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];

	if (_verifiesCertificates)
		gnutls_session_set_verify_cert(_session, _host.UTF8String, 0);

	status = gnutls_handshake(_session);

	if (status == GNUTLS_E_INTERRUPTED || status == GNUTLS_E_AGAIN) {
		if (gnutls_record_get_direction(_session) == 1)
			[_underlyingStream
			    asyncWriteData: [OFData dataWithItems: "" count: 0]
			       runLoopMode: runLoopMode];
		else
			[_underlyingStream asyncReadIntoBuffer: (void *)""
							length: 0
						   runLoopMode: runLoopMode];

		[_delegate retain];
		return;
	}

	if (status == GNUTLS_E_SUCCESS)
		_handshakeDone = true;
	else
		/* FIXME: Map to better errors */
		exception = [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: OFTLSStreamErrorCodeUnknown];

	if ([_delegate respondsToSelector:
	    @selector(stream:didPerformClientHandshakeWithHost:exception:)])
		[_delegate		       stream: self
		    didPerformClientHandshakeWithHost: host
					    exception: exception];
}

-      (bool)stream: (OFStream *)stream
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (nullable id)exception
{
	if (exception == nil) {
		int status = gnutls_handshake(_session);

		if (status == GNUTLS_E_INTERRUPTED ||
		    status == GNUTLS_E_AGAIN) {
			if (gnutls_record_get_direction(_session) == 1) {
				OFData *data = [OFData dataWithItems: ""
							       count: 0];
				OFRunLoopMode runLoopMode =
				    [OFRunLoop currentRunLoop].currentMode;
				[_underlyingStream asyncWriteData: data
						      runLoopMode: runLoopMode];
				return false;
			} else
				return true;
		}

		if (status == GNUTLS_E_SUCCESS)
			_handshakeDone = true;
		else
			exception = [OFTLSHandshakeFailedException
			    exceptionWithStream: self
					   host: _host
				      errorCode: OFTLSStreamErrorCodeUnknown];
	}

	if ([_delegate respondsToSelector:
	    @selector(stream:didPerformClientHandshakeWithHost:exception:)])
		[_delegate		       stream: self
		    didPerformClientHandshakeWithHost: _host
					    exception: exception];

	[_delegate release];

	return false;
}

- (OFData *)stream: (OFStream *)stream
      didWriteData: (OFData *)data
      bytesWritten: (size_t)bytesWritten
	 exception: (id)exception
{
	if (exception == nil) {
		int status = gnutls_handshake(_session);

		if (status == GNUTLS_E_INTERRUPTED ||
		    status == GNUTLS_E_AGAIN) {
			if (gnutls_record_get_direction(_session) == 1)
				return data;
			else {
				OFRunLoopMode runLoopMode =
				    [OFRunLoop currentRunLoop].currentMode;
				[_underlyingStream
				    asyncReadIntoBuffer: (void *)""
						 length: 0
					    runLoopMode: runLoopMode];
				return nil;
			}
		}

		if (status == GNUTLS_E_SUCCESS)
			_handshakeDone = true;
		else
			exception = [OFTLSHandshakeFailedException
			    exceptionWithStream: self
					   host: _host
				      errorCode: OFTLSStreamErrorCodeUnknown];
	}

	if ([_delegate respondsToSelector:
	    @selector(stream:didPerformClientHandshakeWithHost:exception:)])
		[_delegate		       stream: self
		    didPerformClientHandshakeWithHost: _host
					    exception: exception];

	[_delegate release];

	return nil;
}
@end

Added src/tls/OFOpenSSLTLSStream.h version [5bdb0a26b6].







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFTLSStream.h"

#include <openssl/bio.h>
#include <openssl/ssl.h>

OF_ASSUME_NONNULL_BEGIN

#define OFOpenSSLTLSStreamBufferSize 512

@interface OFOpenSSLTLSStream: OFTLSStream <OFStreamDelegate>
{
	bool _handshakeDone;
	SSL *_SSL;
	BIO *_readBIO, *_writeBIO;
	OFString *_host;
	char _buffer[OFOpenSSLTLSStreamBufferSize];
}
@end

OF_ASSUME_NONNULL_END

Added src/tls/OFOpenSSLTLSStream.m version [f8706b398d].

























































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
/*
 * 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 "OFOpenSSLTLSStream.h"
#import "OFData.h"

#import "OFAlreadyConnectedException.h"
#import "OFInitializationFailedException.h"
#import "OFNotOpenException.h"
#import "OFReadFailedException.h"
#import "OFTLSHandshakeFailedException.h"
#import "OFWriteFailedException.h"

#define bufferSize OFOpenSSLTLSStreamBufferSize

int _ObjFWTLS_reference;
static SSL_CTX *clientContext;

@implementation OFOpenSSLTLSStream
+ (void)load
{
	if (OFTLSStreamImplementation == Nil)
		OFTLSStreamImplementation = self;
}

+ (void)initialize
{
	if (self != [OFOpenSSLTLSStream class])
		return;

	SSL_load_error_strings();
	SSL_library_init();

	if ((clientContext = SSL_CTX_new(TLS_client_method())) == NULL ||
	    SSL_CTX_set_default_verify_paths(clientContext) != 1)
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

- (instancetype)initWithStream: (OFStream <OFReadyForReadingObserving,
				     OFReadyForWritingObserving> *)stream
{
	self = [super initWithStream: stream];

	@try {
		_underlyingStream.delegate = self;
		/*
		 * Buffer writes so that nothing gets lost if we write more
		 * than the underlying stream can write.
		 */
		_underlyingStream.buffersWrites = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_SSL != NULL)
		[self close];

	[_host release];

	[super dealloc];
}

- (void)close
{
	if (_SSL == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_handshakeDone)
		SSL_shutdown(_SSL);

	SSL_free(_SSL);
	_SSL = NULL;

	[_host release];
	_host = nil;

	[super close];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
{
	int ret;
	size_t bytesRead;

	if (!_handshakeDone)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (BIO_ctrl_pending(_readBIO) < 1) {
		@try {
			size_t tmp = [_underlyingStream
			    readIntoBuffer: _buffer
				    length: bufferSize];

			OFEnsure(tmp <= INT_MAX);
			/* Writing to a memory BIO must never fail. */
			OFEnsure(BIO_write(_readBIO, _buffer, (int)tmp) ==
			    (int)tmp);
		} @catch (OFReadFailedException *e) {
			if (e.errNo != EWOULDBLOCK && e.errNo != EAGAIN)
				@throw e;
		}
	}

	ret = SSL_read_ex(_SSL, buffer, length, &bytesRead);

	while (BIO_ctrl_pending(_writeBIO) > 0) {
		int tmp = BIO_read(_writeBIO, _buffer, bufferSize);

		OFEnsure(tmp >= 0);

		[_underlyingStream writeBuffer: _buffer length: tmp];
		[_underlyingStream flushWriteBuffer];
	}

	if (ret != 1) {
		/*
		 * The underlying stream might have had data ready, but not
		 * enough for OpenSSL to return decrypted data. This means the
		 * caller might have observed the TLS stream for reading, got a
		 * ready signal and read - and expects the read to succeed, not
		 * to fail with EWOULDBLOCK, as it was signaled ready.
		 * Therefore, return 0, as we could read 0 decrypted bytes, but
		 * cleared the ready signal of the underlying stream.
		 */
		if (SSL_get_error(_SSL, ret) == SSL_ERROR_WANT_READ)
			return 0;

		/* FIXME: Translate error to errNo */
		@throw [OFReadFailedException exceptionWithObject: self
						  requestedLength: length
							    errNo: 0];
	}

	return bytesRead;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
{
	int ret;
	size_t bytesWritten;

	if (!_handshakeDone)
		@throw [OFNotOpenException exceptionWithObject: self];

	if ((ret = SSL_write_ex(_SSL, buffer, length, &bytesWritten)) != 1) {
		/* FIXME: Translate error to errNo */
		int errNo = 0;

		if (SSL_get_error(_SSL, ret) == SSL_ERROR_WANT_WRITE)
			return bytesWritten;

		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: bytesWritten
							     errNo: errNo];
	}

	while (BIO_ctrl_pending(_writeBIO) > 0) {
		int tmp = BIO_read(_writeBIO, _buffer, bufferSize);

		OFEnsure(tmp >= 0);

		[_underlyingStream writeBuffer: _buffer length: tmp];
		[_underlyingStream flushWriteBuffer];
	}

	return bytesWritten;
}

- (bool)hasDataInReadBuffer
{
	if (SSL_has_pending(_SSL) || BIO_ctrl_pending(_readBIO) > 0)
		return true;

	return super.hasDataInReadBuffer;
}

- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
				runLoopMode: (OFRunLoopMode)runLoopMode
{
	static const OFTLSStreamErrorCode initFailedErrorCode =
	    OFTLSStreamErrorCodeInitializationFailed;
	id exception = nil;
	int status;

	if (_SSL != NULL)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	if ((_readBIO = BIO_new(BIO_s_mem())) == NULL)
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];

	if ((_writeBIO = BIO_new(BIO_s_mem())) == NULL) {
		BIO_free(_readBIO);
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];
	}

	BIO_set_mem_eof_return(_readBIO, -1);
	BIO_set_mem_eof_return(_writeBIO, -1);

	if ((_SSL = SSL_new(clientContext)) == NULL) {
		BIO_free(_readBIO);
		BIO_free(_writeBIO);
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];
	}

	SSL_set_bio(_SSL, _readBIO, _writeBIO);
	SSL_set_connect_state(_SSL);

	_host = [host copy];

	if (SSL_set_tlsext_host_name(_SSL, _host.UTF8String) != 1)
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];

	if (_verifiesCertificates) {
		SSL_set_verify(_SSL, SSL_VERIFY_PEER, NULL);

		if (SSL_set1_host(_SSL, _host.UTF8String) != 1)
			@throw [OFTLSHandshakeFailedException
			    exceptionWithStream: self
					   host: host
				      errorCode: initFailedErrorCode];
	}

	status = SSL_do_handshake(_SSL);

	while (BIO_ctrl_pending(_writeBIO) > 0) {
		int tmp = BIO_read(_writeBIO, _buffer, bufferSize);

		OFEnsure(tmp >= 0);

		[_underlyingStream writeBuffer: _buffer length: tmp];
		[_underlyingStream flushWriteBuffer];
	}

	if (status == 1)
		_handshakeDone = true;
	else {
		switch (SSL_get_error(_SSL, status)) {
		case SSL_ERROR_WANT_READ:
			[_underlyingStream asyncReadIntoBuffer: _buffer
							length: bufferSize
						   runLoopMode: runLoopMode];
			[_delegate retain];
			return;
		case SSL_ERROR_WANT_WRITE:
			[_underlyingStream
			    asyncWriteData: [OFData dataWithItems: "" count: 0]
			       runLoopMode: runLoopMode];
			[_delegate retain];
			return;
		default:
			/* FIXME: Map to better errors */
			exception = [OFTLSHandshakeFailedException
			    exceptionWithStream: self
					   host: host
				      errorCode: OFTLSStreamErrorCodeUnknown];
			break;
		}
	}

	if ([_delegate respondsToSelector:
	    @selector(stream:didPerformClientHandshakeWithHost:exception:)])
		[_delegate		       stream: self
		    didPerformClientHandshakeWithHost: host
					    exception: exception];
}

-      (bool)stream: (OFStream *)stream
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (nullable id)exception
{
	if (exception == nil) {
		static const OFTLSStreamErrorCode unknownErrorCode =
		    OFTLSStreamErrorCodeUnknown;
		int status;
		OFData *data;

		OFEnsure(length <= INT_MAX);
		OFEnsure(BIO_write(_readBIO, buffer, (int)length) ==
		    (int)length);

		status = SSL_do_handshake(_SSL);

		while (BIO_ctrl_pending(_writeBIO) > 0) {
			int tmp = BIO_read(_writeBIO, buffer, bufferSize);

			OFEnsure(tmp >= 0);

			[_underlyingStream writeBuffer: _buffer length: tmp];
			[_underlyingStream flushWriteBuffer];
		}

		if (status == 1)
			_handshakeDone = true;
		else {
			switch (SSL_get_error(_SSL, status)) {
			case SSL_ERROR_WANT_READ:
				return true;
			case SSL_ERROR_WANT_WRITE:
				data = [OFData dataWithItems: "" count: 0];
				OFRunLoopMode runLoopMode =
				    [OFRunLoop currentRunLoop].currentMode;
				[_underlyingStream asyncWriteData: data
						      runLoopMode: runLoopMode];
				return false;
			default:
				exception = [OFTLSHandshakeFailedException
				    exceptionWithStream: self
						   host: _host
					      errorCode: unknownErrorCode];
				break;
			}
		}
	}

	if ([_delegate respondsToSelector:
	    @selector(stream:didPerformClientHandshakeWithHost:exception:)])
		[_delegate		       stream: self
		    didPerformClientHandshakeWithHost: _host
					    exception: exception];

	[_delegate release];

	return false;
}

- (OFData *)stream: (OFStream *)stream
      didWriteData: (OFData *)data
      bytesWritten: (size_t)bytesWritten
	 exception: (id)exception
{
	if (exception == nil) {
		static const OFTLSStreamErrorCode unknownErrorCode =
		    OFTLSStreamErrorCodeUnknown;
		int status;
		OFRunLoopMode runLoopMode;

		while (BIO_ctrl_pending(_writeBIO) > 0) {
			int tmp = BIO_read(_writeBIO, _buffer, bufferSize);

			OFEnsure(tmp >= 0);

			[_underlyingStream writeBuffer: _buffer length: tmp];
			[_underlyingStream flushWriteBuffer];
		}

		status = SSL_do_handshake(_SSL);

		while (BIO_ctrl_pending(_writeBIO) > 0) {
			int tmp = BIO_read(_writeBIO, _buffer, bufferSize);

			OFEnsure(tmp >= 0);

			[_underlyingStream writeBuffer: _buffer length: tmp];
			[_underlyingStream flushWriteBuffer];
		}

		if (status == 1)
			_handshakeDone = true;
		else {
			switch (SSL_get_error(_SSL, status)) {
			case SSL_ERROR_WANT_READ:
				runLoopMode =
				    [OFRunLoop currentRunLoop].currentMode;
				[_underlyingStream
				    asyncReadIntoBuffer: _buffer
						 length: bufferSize
					    runLoopMode: runLoopMode];
				return nil;
			case SSL_ERROR_WANT_WRITE:
				return data;
			default:
				exception = [OFTLSHandshakeFailedException
				    exceptionWithStream: self
						   host: _host
					      errorCode: unknownErrorCode];
				break;
			}
		}
	}

	if ([_delegate respondsToSelector:
	    @selector(stream:didPerformClientHandshakeWithHost:exception:)])
		[_delegate		       stream: self
		    didPerformClientHandshakeWithHost: _host
					    exception: exception];

	[_delegate release];

	return nil;
}
@end

Added src/tls/OFSecureTransportTLSStream.h version [6f1a3f10c3].



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "OFTLSStream.h"

#include <Security/SecureTransport.h>

OF_ASSUME_NONNULL_BEGIN

@interface OFSecureTransportTLSStream: OFTLSStream <OFStreamDelegate>
{
	SSLContextRef _context;
	OFString *_host;
}
@end

OF_ASSUME_NONNULL_END

Added src/tls/OFSecureTransportTLSStream.m version [d93ffbbd51].



















































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/*
 * 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 "OFSecureTransportTLSStream.h"

#import "OFAlreadyConnectedException.h"
#import "OFNotOpenException.h"
#import "OFReadFailedException.h"
#import "OFTLSHandshakeFailedException.h"
#import "OFWriteFailedException.h"

int _ObjFWTLS_reference;

static OSStatus
readFunc(SSLConnectionRef connection, void *data, size_t *dataLength)
{
	bool incomplete;
	size_t length;

	@try {
		length = [((OFTLSStream *)connection).underlyingStream
		    readIntoBuffer: data
			    length: *dataLength];
	} @catch (OFReadFailedException *e) {
		if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN) {
			*dataLength = 0;
			return errSSLWouldBlock;
		}

		@throw e;
	}

	incomplete = (length < *dataLength);
	*dataLength = length;

	return (incomplete ? errSSLWouldBlock : noErr);
}

static OSStatus
writeFunc(SSLConnectionRef connection, const void *data, size_t *dataLength)
{
	@try {
		[((OFTLSStream *)connection).underlyingStream
		    writeBuffer: data
			 length: *dataLength];
	} @catch (OFWriteFailedException *e) {
		*dataLength = e.bytesWritten;

		if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN)
			return errSSLWouldBlock;

		@throw e;
	}

	return noErr;
}

/*
 * Apple deprecated Secure Transport without providing a replacement that can
 * work with any socket. On top of that, their replacement, Network.framework,
 * doesn't support STARTTLS at all.
 */
#if OF_GCC_VERSION >= 402
# pragma GCC diagnostic ignored "-Wdeprecated"
#endif

@implementation OFSecureTransportTLSStream
+ (void)load
{
	if (OFTLSStreamImplementation == Nil)
		OFTLSStreamImplementation = self;
}

- (instancetype)initWithStream: (OFStream <OFReadyForReadingObserving,
				     OFReadyForWritingObserving> *)stream
{
	self = [super initWithStream: stream];

	@try {
		_underlyingStream.delegate = self;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_context != NULL)
		[self close];

	[_host release];

	[super dealloc];
}

- (void)close
{
	if (_context == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	[_host release];
	_host = nil;

	SSLClose(_context);
#ifdef HAVE_SSLCREATECONTEXT
	CFRelease(_context);
#else
	SSLDisposeContext(_context);
#endif
	_context = NULL;

	[super close];
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
{
	OSStatus status;
	size_t ret;

	if (_context == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	status = SSLRead(_context, buffer, length, &ret);
	if (status != noErr && status != errSSLWouldBlock)
		/* FIXME: Translate status to errNo */
		@throw [OFReadFailedException exceptionWithObject: self
						  requestedLength: length
							    errNo: 0];

	return ret;
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
{
	OSStatus status;
	size_t bytesWritten = 0;

	if (_context == NULL)
		@throw [OFNotOpenException exceptionWithObject: self];

	status = SSLWrite(_context, buffer, length, &bytesWritten);
	if (status != noErr && status != errSSLWouldBlock)
		/* FIXME: Translate status to errNo */
		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: bytesWritten
							     errNo: 0];

	return bytesWritten;
}

- (bool)hasDataInReadBuffer
{
	size_t bufferSize;

	if (SSLGetBufferedReadSize(_context, &bufferSize) == noErr &&
	    bufferSize > 0)
		return true;

	return super.hasDataInReadBuffer;
}

- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
				runLoopMode: (OFRunLoopMode)runLoopMode
{
	static const OFTLSStreamErrorCode initFailedErrorCode =
	    OFTLSStreamErrorCodeInitializationFailed;
	id exception = nil;
	OSStatus status;

	if (_context != NULL)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

#ifdef HAVE_SSLCREATECONTEXT
	if ((_context = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
	    kSSLStreamType)) == NULL)
#else
	if (SSLNewContext(false, &_context) != noErr)
#endif
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];

	if (SSLSetIOFuncs(_context, readFunc, writeFunc) != noErr ||
	    SSLSetConnection(_context, self) != noErr)
		@throw [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: host
			      errorCode: initFailedErrorCode];

	_host = [host copy];

	if (_verifiesCertificates)
		if (SSLSetPeerDomainName(_context,
		    _host.UTF8String, _host.UTF8StringLength) != noErr)
			@throw [OFTLSHandshakeFailedException
			    exceptionWithStream: self
					   host: _host
				      errorCode: initFailedErrorCode];

	status = SSLHandshake(_context);

	if (status == errSSLWouldBlock) {
		/*
		 * Theoretically it is possible we block because Secure
		 * Transport cannot write without blocking. But unfortunately,
		 * Secure Transport does not tell us whether it's blocked on
		 * reading or writing. Waiting for the stream to be either
		 * readable or writable doesn't work either, as the stream is
		 * almost always at least ready for one of the two.
		 */
		[_underlyingStream asyncReadIntoBuffer: (void *)"" 
						length: 0
					   runLoopMode: runLoopMode];
		[_delegate retain];
		return;
	}

	if (status != noErr)
		/* FIXME: Map to better errors */
		exception = [OFTLSHandshakeFailedException
		    exceptionWithStream: self
				   host: _host
			      errorCode: OFTLSStreamErrorCodeUnknown];

	if ([_delegate respondsToSelector:
	    @selector(stream:didPerformClientHandshakeWithHost:exception:)])
		[_delegate		       stream: self
		    didPerformClientHandshakeWithHost: _host
					    exception: exception];
}

-      (bool)stream: (OFStream *)stream
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (nullable id)exception
{
	if (exception == nil) {
		OSStatus status = SSLHandshake(_context);

		if (status == errSSLWouldBlock)
			return true;

		if (status != noErr)
			exception = [OFTLSHandshakeFailedException
			    exceptionWithStream: self
					   host: _host
				      errorCode: OFTLSStreamErrorCodeUnknown];
	}

	if ([_delegate respondsToSelector:
	    @selector(stream:didPerformClientHandshakeWithHost:exception:)])
		[_delegate		       stream: self
		    didPerformClientHandshakeWithHost: _host
					    exception: exception];

	[_delegate release];

	return false;
}
@end

Added src/tls/ObjFWTLS.h version [498108c5a2].

















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * 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 "macros.h"

#ifdef __cplusplus
extern "C" {
#endif
extern int _ObjFWTLS_reference;
#ifdef __cplusplus
}
#endif

Modified src/unicode.h from [5f9cf58b43] to [2eea427f38].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFString.h"

#define OF_UNICODE_UPPERCASE_TABLE_SIZE 0x1EA
#define OF_UNICODE_LOWERCASE_TABLE_SIZE 0x1EA
#define OF_UNICODE_TITLECASE_TABLE_SIZE 0x1EA
#define OF_UNICODE_CASEFOLDING_TABLE_SIZE 0x1EA
#define OF_UNICODE_DECOMPOSITION_TABLE_SIZE 0x2FB
#define OF_UNICODE_DECOMPOSITION_COMPAT_TABLE_SIZE 0x2FB

#ifdef __cplusplus
extern "C" {
#endif
extern const of_unichar_t *const _Nonnull
    of_unicode_uppercase_table[OF_UNICODE_UPPERCASE_TABLE_SIZE];
extern const of_unichar_t *const _Nonnull
    of_unicode_lowercase_table[OF_UNICODE_LOWERCASE_TABLE_SIZE];
extern const of_unichar_t *const _Nonnull
    of_unicode_titlecase_table[OF_UNICODE_TITLECASE_TABLE_SIZE];
extern const of_unichar_t *const _Nonnull
    of_unicode_casefolding_table[OF_UNICODE_CASEFOLDING_TABLE_SIZE];
extern const char *const _Nullable *const _Nonnull
    of_unicode_decomposition_table[OF_UNICODE_DECOMPOSITION_TABLE_SIZE];
extern const char *const _Nullable *const _Nonnull
    of_unicode_decomposition_compat_table[OF_UNICODE_DECOMPOSITION_COMPAT_TABLE_SIZE];
#ifdef __cplusplus
}
#endif

<
<
|















|
|
|
|
|
|




|
|
|
|
|
|
|
|

|

|



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
/*


 * 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 "OFString.h"

#define OFUnicodeUppercaseTableSize 0x1EA
#define OFUnicodeLowercaseTableSize 0x1EA
#define OFUnicodeTitlecaseTableSize 0x1EA
#define OFUnicodeCaseFoldingTableSize 0x1EA
#define OFUnicodeDecompositionTableSize 0x2FB
#define OFUnicodeDecompositionCompatTableSize 0x2FB

#ifdef __cplusplus
extern "C" {
#endif
extern const OFUnichar *const _Nonnull
    OFUnicodeUppercaseTable[OFUnicodeUppercaseTableSize];
extern const OFUnichar *const _Nonnull
    OFUnicodeLowercaseTable[OFUnicodeLowercaseTableSize];
extern const OFUnichar *const _Nonnull
    OFUnicodeTitlecaseTable[OFUnicodeTitlecaseTableSize];
extern const OFUnichar *const _Nonnull
    OFUnicodeCaseFoldingTable[OFUnicodeCaseFoldingTableSize];
extern const char *const _Nullable *const _Nonnull
    OFUnicodeDecompositionTable[OFUnicodeDecompositionTableSize];
extern const char *const _Nullable *const _Nonnull
    OFUnicodeDecompositionCompatTable[OFUnicodeDecompositionCompatTableSize];
#ifdef __cplusplus
}
#endif

Modified src/unicode.m from [8d99c0a504] to [1934fadafb].

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, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFString.h"

static const of_unichar_t emptyPage[0x100] = { 0 };
static const char *emptyDecompositionPage[0x100] = { NULL };

static const of_unichar_t uppercasePage0[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,

<
<
|

















|


|







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
/*


 * 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"

#import "OFString.h"

static const OFUnichar emptyPage[0x100] = { 0 };
static const char *emptyDecompositionPage[0x100] = { NULL };

static const OFUnichar uppercasePage0[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
	0, 0, 0, 0, 0, 0, 0, 0,
	192, 193, 194, 195, 196, 197, 198, 199,
	200, 201, 202, 203, 204, 205, 206, 207,
	208, 209, 210, 211, 212, 213, 214, 0,
	216, 217, 218, 219, 220, 221, 222, 376,
};

static const of_unichar_t uppercasePage1[0x100] = {
	0, 256, 0, 258, 0, 260, 0, 262,
	0, 264, 0, 266, 0, 268, 0, 270,
	0, 272, 0, 274, 0, 276, 0, 278,
	0, 280, 0, 282, 0, 284, 0, 286,
	0, 288, 0, 290, 0, 292, 0, 294,
	0, 296, 0, 298, 0, 300, 0, 302,
	0, 73, 0, 306, 0, 308, 0, 310,







|







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	0, 0, 0, 0, 0, 0, 0, 0,
	192, 193, 194, 195, 196, 197, 198, 199,
	200, 201, 202, 203, 204, 205, 206, 207,
	208, 209, 210, 211, 212, 213, 214, 0,
	216, 217, 218, 219, 220, 221, 222, 376,
};

static const OFUnichar uppercasePage1[0x100] = {
	0, 256, 0, 258, 0, 260, 0, 262,
	0, 264, 0, 266, 0, 268, 0, 270,
	0, 272, 0, 274, 0, 276, 0, 278,
	0, 280, 0, 282, 0, 284, 0, 286,
	0, 288, 0, 290, 0, 292, 0, 294,
	0, 296, 0, 298, 0, 300, 0, 302,
	0, 73, 0, 306, 0, 308, 0, 310,
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
	471, 0, 473, 0, 475, 398, 0, 478,
	0, 480, 0, 482, 0, 484, 0, 486,
	0, 488, 0, 490, 0, 492, 0, 494,
	0, 0, 497, 497, 0, 500, 0, 0,
	0, 504, 0, 506, 0, 508, 0, 510,
};

static const of_unichar_t uppercasePage2[0x100] = {
	0, 512, 0, 514, 0, 516, 0, 518,
	0, 520, 0, 522, 0, 524, 0, 526,
	0, 528, 0, 530, 0, 532, 0, 534,
	0, 536, 0, 538, 0, 540, 0, 542,
	0, 0, 0, 546, 0, 548, 0, 550,
	0, 552, 0, 554, 0, 556, 0, 558,
	0, 560, 0, 562, 0, 0, 0, 0,







|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
	471, 0, 473, 0, 475, 398, 0, 478,
	0, 480, 0, 482, 0, 484, 0, 486,
	0, 488, 0, 490, 0, 492, 0, 494,
	0, 0, 497, 497, 0, 500, 0, 0,
	0, 504, 0, 506, 0, 508, 0, 510,
};

static const OFUnichar uppercasePage2[0x100] = {
	0, 512, 0, 514, 0, 516, 0, 518,
	0, 520, 0, 522, 0, 524, 0, 526,
	0, 528, 0, 530, 0, 532, 0, 534,
	0, 536, 0, 538, 0, 540, 0, 542,
	0, 0, 0, 546, 0, 548, 0, 550,
	0, 552, 0, 554, 0, 556, 0, 558,
	0, 560, 0, 562, 0, 0, 0, 0,
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage3[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage3[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
	0, 984, 0, 986, 0, 988, 0, 990,
	0, 992, 0, 994, 0, 996, 0, 998,
	0, 1000, 0, 1002, 0, 1004, 0, 1006,
	922, 929, 1017, 895, 0, 917, 0, 0,
	1015, 0, 0, 1018, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage4[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047,







|







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
	0, 984, 0, 986, 0, 988, 0, 990,
	0, 992, 0, 994, 0, 996, 0, 998,
	0, 1000, 0, 1002, 0, 1004, 0, 1006,
	922, 929, 1017, 895, 0, 917, 0, 0,
	1015, 0, 0, 1018, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage4[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047,
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
	0, 1240, 0, 1242, 0, 1244, 0, 1246,
	0, 1248, 0, 1250, 0, 1252, 0, 1254,
	0, 1256, 0, 1258, 0, 1260, 0, 1262,
	0, 1264, 0, 1266, 0, 1268, 0, 1270,
	0, 1272, 0, 1274, 0, 1276, 0, 1278,
};

static const of_unichar_t uppercasePage5[0x100] = {
	0, 1280, 0, 1282, 0, 1284, 0, 1286,
	0, 1288, 0, 1290, 0, 1292, 0, 1294,
	0, 1296, 0, 1298, 0, 1300, 0, 1302,
	0, 1304, 0, 1306, 0, 1308, 0, 1310,
	0, 1312, 0, 1314, 0, 1316, 0, 1318,
	0, 1320, 0, 1322, 0, 1324, 0, 1326,
	0, 0, 0, 0, 0, 0, 0, 0,







|







191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
	0, 1240, 0, 1242, 0, 1244, 0, 1246,
	0, 1248, 0, 1250, 0, 1252, 0, 1254,
	0, 1256, 0, 1258, 0, 1260, 0, 1262,
	0, 1264, 0, 1266, 0, 1268, 0, 1270,
	0, 1272, 0, 1274, 0, 1276, 0, 1278,
};

static const OFUnichar uppercasePage5[0x100] = {
	0, 1280, 0, 1282, 0, 1284, 0, 1286,
	0, 1288, 0, 1290, 0, 1292, 0, 1294,
	0, 1296, 0, 1298, 0, 1300, 0, 1302,
	0, 1304, 0, 1306, 0, 1308, 0, 1310,
	0, 1312, 0, 1314, 0, 1316, 0, 1318,
	0, 1320, 0, 1322, 0, 1324, 0, 1326,
	0, 0, 0, 0, 0, 0, 0, 0,
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage16[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage16[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
	7320, 7321, 7322, 7323, 7324, 7325, 7326, 7327,
	7328, 7329, 7330, 7331, 7332, 7333, 7334, 7335,
	7336, 7337, 7338, 7339, 7340, 7341, 7342, 7343,
	7344, 7345, 7346, 7347, 7348, 7349, 7350, 7351,
	7352, 7353, 7354, 0, 0, 7357, 7358, 7359,
};

static const of_unichar_t uppercasePage19[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
	7320, 7321, 7322, 7323, 7324, 7325, 7326, 7327,
	7328, 7329, 7330, 7331, 7332, 7333, 7334, 7335,
	7336, 7337, 7338, 7339, 7340, 7341, 7342, 7343,
	7344, 7345, 7346, 7347, 7348, 7349, 7350, 7351,
	7352, 7353, 7354, 0, 0, 7357, 7358, 7359,
};

static const OFUnichar uppercasePage19[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	5104, 5105, 5106, 5107, 5108, 5109, 0, 0,
};

static const of_unichar_t uppercasePage28[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	5104, 5105, 5106, 5107, 5108, 5109, 0, 0,
};

static const OFUnichar uppercasePage28[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage29[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage29[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage30[0x100] = {
	0, 7680, 0, 7682, 0, 7684, 0, 7686,
	0, 7688, 0, 7690, 0, 7692, 0, 7694,
	0, 7696, 0, 7698, 0, 7700, 0, 7702,
	0, 7704, 0, 7706, 0, 7708, 0, 7710,
	0, 7712, 0, 7714, 0, 7716, 0, 7718,
	0, 7720, 0, 7722, 0, 7724, 0, 7726,
	0, 7728, 0, 7730, 0, 7732, 0, 7734,







|







366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage30[0x100] = {
	0, 7680, 0, 7682, 0, 7684, 0, 7686,
	0, 7688, 0, 7690, 0, 7692, 0, 7694,
	0, 7696, 0, 7698, 0, 7700, 0, 7702,
	0, 7704, 0, 7706, 0, 7708, 0, 7710,
	0, 7712, 0, 7714, 0, 7716, 0, 7718,
	0, 7720, 0, 7722, 0, 7724, 0, 7726,
	0, 7728, 0, 7730, 0, 7732, 0, 7734,
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
	0, 7896, 0, 7898, 0, 7900, 0, 7902,
	0, 7904, 0, 7906, 0, 7908, 0, 7910,
	0, 7912, 0, 7914, 0, 7916, 0, 7918,
	0, 7920, 0, 7922, 0, 7924, 0, 7926,
	0, 7928, 0, 7930, 0, 7932, 0, 7934,
};

static const of_unichar_t uppercasePage31[0x100] = {
	7944, 7945, 7946, 7947, 7948, 7949, 7950, 7951,
	0, 0, 0, 0, 0, 0, 0, 0,
	7960, 7961, 7962, 7963, 7964, 7965, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	7976, 7977, 7978, 7979, 7980, 7981, 7982, 7983,
	0, 0, 0, 0, 0, 0, 0, 0,
	7992, 7993, 7994, 7995, 7996, 7997, 7998, 7999,







|







401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
	0, 7896, 0, 7898, 0, 7900, 0, 7902,
	0, 7904, 0, 7906, 0, 7908, 0, 7910,
	0, 7912, 0, 7914, 0, 7916, 0, 7918,
	0, 7920, 0, 7922, 0, 7924, 0, 7926,
	0, 7928, 0, 7930, 0, 7932, 0, 7934,
};

static const OFUnichar uppercasePage31[0x100] = {
	7944, 7945, 7946, 7947, 7948, 7949, 7950, 7951,
	0, 0, 0, 0, 0, 0, 0, 0,
	7960, 7961, 7962, 7963, 7964, 7965, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	7976, 7977, 7978, 7979, 7980, 7981, 7982, 7983,
	0, 0, 0, 0, 0, 0, 0, 0,
	7992, 7993, 7994, 7995, 7996, 7997, 7998, 7999,
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
	0, 0, 0, 0, 0, 0, 0, 0,
	8168, 8169, 0, 0, 0, 8172, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 8188, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage33[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
	0, 0, 0, 0, 0, 0, 0, 0,
	8168, 8169, 0, 0, 0, 8172, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 8188, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage33[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage36[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage36[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
	9406, 9407, 9408, 9409, 9410, 9411, 9412, 9413,
	9414, 9415, 9416, 9417, 9418, 9419, 9420, 9421,
	9422, 9423, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage44[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	11264, 11265, 11266, 11267, 11268, 11269, 11270, 11271,







|







506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
	9406, 9407, 9408, 9409, 9410, 9411, 9412, 9413,
	9414, 9415, 9416, 9417, 9418, 9419, 9420, 9421,
	9422, 9423, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage44[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	11264, 11265, 11266, 11267, 11268, 11269, 11270, 11271,
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
	0, 11480, 0, 11482, 0, 11484, 0, 11486,
	0, 11488, 0, 11490, 0, 0, 0, 0,
	0, 0, 0, 0, 11499, 0, 11501, 0,
	0, 0, 0, 11506, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage45[0x100] = {
	4256, 4257, 4258, 4259, 4260, 4261, 4262, 4263,
	4264, 4265, 4266, 4267, 4268, 4269, 4270, 4271,
	4272, 4273, 4274, 4275, 4276, 4277, 4278, 4279,
	4280, 4281, 4282, 4283, 4284, 4285, 4286, 4287,
	4288, 4289, 4290, 4291, 4292, 4293, 0, 4295,
	0, 0, 0, 0, 0, 4301, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
	0, 11480, 0, 11482, 0, 11484, 0, 11486,
	0, 11488, 0, 11490, 0, 0, 0, 0,
	0, 0, 0, 0, 11499, 0, 11501, 0,
	0, 0, 0, 11506, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage45[0x100] = {
	4256, 4257, 4258, 4259, 4260, 4261, 4262, 4263,
	4264, 4265, 4266, 4267, 4268, 4269, 4270, 4271,
	4272, 4273, 4274, 4275, 4276, 4277, 4278, 4279,
	4280, 4281, 4282, 4283, 4284, 4285, 4286, 4287,
	4288, 4289, 4290, 4291, 4292, 4293, 0, 4295,
	0, 0, 0, 0, 0, 4301, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage166[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage166[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage167[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 42786, 0, 42788, 0, 42790,
	0, 42792, 0, 42794, 0, 42796, 0, 42798,
	0, 0, 0, 42802, 0, 42804, 0, 42806,







|







611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage167[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 42786, 0, 42788, 0, 42790,
	0, 42792, 0, 42794, 0, 42796, 0, 42798,
	0, 0, 0, 42802, 0, 42804, 0, 42806,
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 42997, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage171[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 42997, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage171[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage255[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage255[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage260[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	66560, 66561, 66562, 66563, 66564, 66565, 66566, 66567,
	66568, 66569, 66570, 66571, 66572, 66573, 66574, 66575,







|







716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage260[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	66560, 66561, 66562, 66563, 66564, 66565, 66566, 66567,
	66568, 66569, 66570, 66571, 66572, 66573, 66574, 66575,
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
	66736, 66737, 66738, 66739, 66740, 66741, 66742, 66743,
	66744, 66745, 66746, 66747, 66748, 66749, 66750, 66751,
	66752, 66753, 66754, 66755, 66756, 66757, 66758, 66759,
	66760, 66761, 66762, 66763, 66764, 66765, 66766, 66767,
	66768, 66769, 66770, 66771, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage268[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
	66736, 66737, 66738, 66739, 66740, 66741, 66742, 66743,
	66744, 66745, 66746, 66747, 66748, 66749, 66750, 66751,
	66752, 66753, 66754, 66755, 66756, 66757, 66758, 66759,
	66760, 66761, 66762, 66763, 66764, 66765, 66766, 66767,
	66768, 66769, 66770, 66771, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage268[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
	68760, 68761, 68762, 68763, 68764, 68765, 68766, 68767,
	68768, 68769, 68770, 68771, 68772, 68773, 68774, 68775,
	68776, 68777, 68778, 68779, 68780, 68781, 68782, 68783,
	68784, 68785, 68786, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage280[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
	68760, 68761, 68762, 68763, 68764, 68765, 68766, 68767,
	68768, 68769, 68770, 68771, 68772, 68773, 68774, 68775,
	68776, 68777, 68778, 68779, 68780, 68781, 68782, 68783,
	68784, 68785, 68786, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage280[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
	71864, 71865, 71866, 71867, 71868, 71869, 71870, 71871,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage366[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
	71864, 71865, 71866, 71867, 71868, 71869, 71870, 71871,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage366[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t uppercasePage489[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 125184, 125185, 125186, 125187, 125188, 125189,
	125190, 125191, 125192, 125193, 125194, 125195, 125196, 125197,
	125198, 125199, 125200, 125201, 125202, 125203, 125204, 125205,







|







856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar uppercasePage489[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 125184, 125185, 125186, 125187, 125188, 125189,
	125190, 125191, 125192, 125193, 125194, 125195, 125196, 125197,
	125198, 125199, 125200, 125201, 125202, 125203, 125204, 125205,
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage0[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage0[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
	248, 249, 250, 251, 252, 253, 254, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage1[0x100] = {
	257, 0, 259, 0, 261, 0, 263, 0,
	265, 0, 267, 0, 269, 0, 271, 0,
	273, 0, 275, 0, 277, 0, 279, 0,
	281, 0, 283, 0, 285, 0, 287, 0,
	289, 0, 291, 0, 293, 0, 295, 0,
	297, 0, 299, 0, 301, 0, 303, 0,
	105, 0, 307, 0, 309, 0, 311, 0,







|







926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
	248, 249, 250, 251, 252, 253, 254, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage1[0x100] = {
	257, 0, 259, 0, 261, 0, 263, 0,
	265, 0, 267, 0, 269, 0, 271, 0,
	273, 0, 275, 0, 277, 0, 279, 0,
	281, 0, 283, 0, 285, 0, 287, 0,
	289, 0, 291, 0, 293, 0, 295, 0,
	297, 0, 299, 0, 301, 0, 303, 0,
	105, 0, 307, 0, 309, 0, 311, 0,
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
	0, 474, 0, 476, 0, 0, 479, 0,
	481, 0, 483, 0, 485, 0, 487, 0,
	489, 0, 491, 0, 493, 0, 495, 0,
	0, 499, 499, 0, 501, 0, 405, 447,
	505, 0, 507, 0, 509, 0, 511, 0,
};

static const of_unichar_t lowercasePage2[0x100] = {
	513, 0, 515, 0, 517, 0, 519, 0,
	521, 0, 523, 0, 525, 0, 527, 0,
	529, 0, 531, 0, 533, 0, 535, 0,
	537, 0, 539, 0, 541, 0, 543, 0,
	414, 0, 547, 0, 549, 0, 551, 0,
	553, 0, 555, 0, 557, 0, 559, 0,
	561, 0, 563, 0, 0, 0, 0, 0,







|







961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
	0, 474, 0, 476, 0, 0, 479, 0,
	481, 0, 483, 0, 485, 0, 487, 0,
	489, 0, 491, 0, 493, 0, 495, 0,
	0, 499, 499, 0, 501, 0, 405, 447,
	505, 0, 507, 0, 509, 0, 511, 0,
};

static const OFUnichar lowercasePage2[0x100] = {
	513, 0, 515, 0, 517, 0, 519, 0,
	521, 0, 523, 0, 525, 0, 527, 0,
	529, 0, 531, 0, 533, 0, 535, 0,
	537, 0, 539, 0, 541, 0, 543, 0,
	414, 0, 547, 0, 549, 0, 551, 0,
	553, 0, 555, 0, 557, 0, 559, 0,
	561, 0, 563, 0, 0, 0, 0, 0,
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage3[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage3[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
	985, 0, 987, 0, 989, 0, 991, 0,
	993, 0, 995, 0, 997, 0, 999, 0,
	1001, 0, 1003, 0, 1005, 0, 1007, 0,
	0, 0, 0, 0, 952, 0, 0, 1016,
	0, 1010, 1019, 0, 0, 891, 892, 893,
};

static const of_unichar_t lowercasePage4[0x100] = {
	1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111,
	1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119,
	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,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
	985, 0, 987, 0, 989, 0, 991, 0,
	993, 0, 995, 0, 997, 0, 999, 0,
	1001, 0, 1003, 0, 1005, 0, 1007, 0,
	0, 0, 0, 0, 952, 0, 0, 1016,
	0, 1010, 1019, 0, 0, 891, 892, 893,
};

static const OFUnichar lowercasePage4[0x100] = {
	1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111,
	1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119,
	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,
	0, 0, 0, 0, 0, 0, 0, 0,
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
	1241, 0, 1243, 0, 1245, 0, 1247, 0,
	1249, 0, 1251, 0, 1253, 0, 1255, 0,
	1257, 0, 1259, 0, 1261, 0, 1263, 0,
	1265, 0, 1267, 0, 1269, 0, 1271, 0,
	1273, 0, 1275, 0, 1277, 0, 1279, 0,
};

static const of_unichar_t lowercasePage5[0x100] = {
	1281, 0, 1283, 0, 1285, 0, 1287, 0,
	1289, 0, 1291, 0, 1293, 0, 1295, 0,
	1297, 0, 1299, 0, 1301, 0, 1303, 0,
	1305, 0, 1307, 0, 1309, 0, 1311, 0,
	1313, 0, 1315, 0, 1317, 0, 1319, 0,
	1321, 0, 1323, 0, 1325, 0, 1327, 0,
	0, 1377, 1378, 1379, 1380, 1381, 1382, 1383,







|







1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
	1241, 0, 1243, 0, 1245, 0, 1247, 0,
	1249, 0, 1251, 0, 1253, 0, 1255, 0,
	1257, 0, 1259, 0, 1261, 0, 1263, 0,
	1265, 0, 1267, 0, 1269, 0, 1271, 0,
	1273, 0, 1275, 0, 1277, 0, 1279, 0,
};

static const OFUnichar lowercasePage5[0x100] = {
	1281, 0, 1283, 0, 1285, 0, 1287, 0,
	1289, 0, 1291, 0, 1293, 0, 1295, 0,
	1297, 0, 1299, 0, 1301, 0, 1303, 0,
	1305, 0, 1307, 0, 1309, 0, 1311, 0,
	1313, 0, 1315, 0, 1317, 0, 1319, 0,
	1321, 0, 1323, 0, 1325, 0, 1327, 0,
	0, 1377, 1378, 1379, 1380, 1381, 1382, 1383,
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage16[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage16[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage19[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage19[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
	43944, 43945, 43946, 43947, 43948, 43949, 43950, 43951,
	43952, 43953, 43954, 43955, 43956, 43957, 43958, 43959,
	43960, 43961, 43962, 43963, 43964, 43965, 43966, 43967,
	5112, 5113, 5114, 5115, 5116, 5117, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage28[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
	43944, 43945, 43946, 43947, 43948, 43949, 43950, 43951,
	43952, 43953, 43954, 43955, 43956, 43957, 43958, 43959,
	43960, 43961, 43962, 43963, 43964, 43965, 43966, 43967,
	5112, 5113, 5114, 5115, 5116, 5117, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage28[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage30[0x100] = {
	7681, 0, 7683, 0, 7685, 0, 7687, 0,
	7689, 0, 7691, 0, 7693, 0, 7695, 0,
	7697, 0, 7699, 0, 7701, 0, 7703, 0,
	7705, 0, 7707, 0, 7709, 0, 7711, 0,
	7713, 0, 7715, 0, 7717, 0, 7719, 0,
	7721, 0, 7723, 0, 7725, 0, 7727, 0,
	7729, 0, 7731, 0, 7733, 0, 7735, 0,







|







1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage30[0x100] = {
	7681, 0, 7683, 0, 7685, 0, 7687, 0,
	7689, 0, 7691, 0, 7693, 0, 7695, 0,
	7697, 0, 7699, 0, 7701, 0, 7703, 0,
	7705, 0, 7707, 0, 7709, 0, 7711, 0,
	7713, 0, 7715, 0, 7717, 0, 7719, 0,
	7721, 0, 7723, 0, 7725, 0, 7727, 0,
	7729, 0, 7731, 0, 7733, 0, 7735, 0,
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
	7897, 0, 7899, 0, 7901, 0, 7903, 0,
	7905, 0, 7907, 0, 7909, 0, 7911, 0,
	7913, 0, 7915, 0, 7917, 0, 7919, 0,
	7921, 0, 7923, 0, 7925, 0, 7927, 0,
	7929, 0, 7931, 0, 7933, 0, 7935, 0,
};

static const of_unichar_t lowercasePage31[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943,
	0, 0, 0, 0, 0, 0, 0, 0,
	7952, 7953, 7954, 7955, 7956, 7957, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	7968, 7969, 7970, 7971, 7972, 7973, 7974, 7975,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
	7897, 0, 7899, 0, 7901, 0, 7903, 0,
	7905, 0, 7907, 0, 7909, 0, 7911, 0,
	7913, 0, 7915, 0, 7917, 0, 7919, 0,
	7921, 0, 7923, 0, 7925, 0, 7927, 0,
	7929, 0, 7931, 0, 7933, 0, 7935, 0,
};

static const OFUnichar lowercasePage31[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943,
	0, 0, 0, 0, 0, 0, 0, 0,
	7952, 7953, 7954, 7955, 7956, 7957, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	7968, 7969, 7970, 7971, 7972, 7973, 7974, 7975,
	0, 0, 0, 0, 0, 0, 0, 0,
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
	8144, 8145, 8054, 8055, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	8160, 8161, 8058, 8059, 8165, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	8056, 8057, 8060, 8061, 8179, 0, 0, 0,
};

static const of_unichar_t lowercasePage33[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 969, 0,
	0, 0, 107, 229, 0, 0, 0, 0,
	0, 0, 8526, 0, 0, 0, 0, 0,







|







1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
	8144, 8145, 8054, 8055, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	8160, 8161, 8058, 8059, 8165, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	8056, 8057, 8060, 8061, 8179, 0, 0, 0,
};

static const OFUnichar lowercasePage33[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 969, 0,
	0, 0, 107, 229, 0, 0, 0, 0,
	0, 0, 8526, 0, 0, 0, 0, 0,
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage36[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage36[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage44[0x100] = {
	11312, 11313, 11314, 11315, 11316, 11317, 11318, 11319,
	11320, 11321, 11322, 11323, 11324, 11325, 11326, 11327,
	11328, 11329, 11330, 11331, 11332, 11333, 11334, 11335,
	11336, 11337, 11338, 11339, 11340, 11341, 11342, 11343,
	11344, 11345, 11346, 11347, 11348, 11349, 11350, 11351,
	11352, 11353, 11354, 11355, 11356, 11357, 11358, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage44[0x100] = {
	11312, 11313, 11314, 11315, 11316, 11317, 11318, 11319,
	11320, 11321, 11322, 11323, 11324, 11325, 11326, 11327,
	11328, 11329, 11330, 11331, 11332, 11333, 11334, 11335,
	11336, 11337, 11338, 11339, 11340, 11341, 11342, 11343,
	11344, 11345, 11346, 11347, 11348, 11349, 11350, 11351,
	11352, 11353, 11354, 11355, 11356, 11357, 11358, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
	11481, 0, 11483, 0, 11485, 0, 11487, 0,
	11489, 0, 11491, 0, 0, 0, 0, 0,
	0, 0, 0, 11500, 0, 11502, 0, 0,
	0, 0, 11507, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage166[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
	11481, 0, 11483, 0, 11485, 0, 11487, 0,
	11489, 0, 11491, 0, 0, 0, 0, 0,
	0, 0, 0, 11500, 0, 11502, 0, 0,
	0, 0, 11507, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage166[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage167[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 42787, 0, 42789, 0, 42791, 0,
	42793, 0, 42795, 0, 42797, 0, 42799, 0,
	0, 0, 42803, 0, 42805, 0, 42807, 0,







|







1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage167[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 42787, 0, 42789, 0, 42791, 0,
	42793, 0, 42795, 0, 42797, 0, 42799, 0,
	0, 0, 42803, 0, 42805, 0, 42807, 0,
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 42998, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage255[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 65345, 65346, 65347, 65348, 65349, 65350, 65351,
	65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359,
	65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367,







|







1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 42998, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage255[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 65345, 65346, 65347, 65348, 65349, 65350, 65351,
	65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359,
	65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367,
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage260[0x100] = {
	66600, 66601, 66602, 66603, 66604, 66605, 66606, 66607,
	66608, 66609, 66610, 66611, 66612, 66613, 66614, 66615,
	66616, 66617, 66618, 66619, 66620, 66621, 66622, 66623,
	66624, 66625, 66626, 66627, 66628, 66629, 66630, 66631,
	66632, 66633, 66634, 66635, 66636, 66637, 66638, 66639,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage260[0x100] = {
	66600, 66601, 66602, 66603, 66604, 66605, 66606, 66607,
	66608, 66609, 66610, 66611, 66612, 66613, 66614, 66615,
	66616, 66617, 66618, 66619, 66620, 66621, 66622, 66623,
	66624, 66625, 66626, 66627, 66628, 66629, 66630, 66631,
	66632, 66633, 66634, 66635, 66636, 66637, 66638, 66639,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage268[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage268[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage280[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage280[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage366[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage366[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t lowercasePage489[0x100] = {
	125218, 125219, 125220, 125221, 125222, 125223, 125224, 125225,
	125226, 125227, 125228, 125229, 125230, 125231, 125232, 125233,
	125234, 125235, 125236, 125237, 125238, 125239, 125240, 125241,
	125242, 125243, 125244, 125245, 125246, 125247, 125248, 125249,
	125250, 125251, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar lowercasePage489[0x100] = {
	125218, 125219, 125220, 125221, 125222, 125223, 125224, 125225,
	125226, 125227, 125228, 125229, 125230, 125231, 125232, 125233,
	125234, 125235, 125236, 125237, 125238, 125239, 125240, 125241,
	125242, 125243, 125244, 125245, 125246, 125247, 125248, 125249,
	125250, 125251, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t titlecasePage1[0x100] = {
	0, 256, 0, 258, 0, 260, 0, 262,
	0, 264, 0, 266, 0, 268, 0, 270,
	0, 272, 0, 274, 0, 276, 0, 278,
	0, 280, 0, 282, 0, 284, 0, 286,
	0, 288, 0, 290, 0, 292, 0, 294,
	0, 296, 0, 298, 0, 300, 0, 302,
	0, 73, 0, 306, 0, 308, 0, 310,







|







1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar titlecasePage1[0x100] = {
	0, 256, 0, 258, 0, 260, 0, 262,
	0, 264, 0, 266, 0, 268, 0, 270,
	0, 272, 0, 274, 0, 276, 0, 278,
	0, 280, 0, 282, 0, 284, 0, 286,
	0, 288, 0, 290, 0, 292, 0, 294,
	0, 296, 0, 298, 0, 300, 0, 302,
	0, 73, 0, 306, 0, 308, 0, 310,
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
	471, 0, 473, 0, 475, 398, 0, 478,
	0, 480, 0, 482, 0, 484, 0, 486,
	0, 488, 0, 490, 0, 492, 0, 494,
	0, 498, 498, 498, 0, 500, 0, 0,
	0, 504, 0, 506, 0, 508, 0, 510,
};

static const of_unichar_t titlecasePage16[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
	471, 0, 473, 0, 475, 398, 0, 478,
	0, 480, 0, 482, 0, 484, 0, 486,
	0, 488, 0, 490, 0, 492, 0, 494,
	0, 498, 498, 498, 0, 500, 0, 0,
	0, 504, 0, 506, 0, 508, 0, 510,
};

static const OFUnichar titlecasePage16[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
	4312, 4313, 4314, 4315, 4316, 4317, 4318, 4319,
	4320, 4321, 4322, 4323, 4324, 4325, 4326, 4327,
	4328, 4329, 4330, 4331, 4332, 4333, 4334, 4335,
	4336, 4337, 4338, 4339, 4340, 4341, 4342, 4343,
	4344, 4345, 4346, 0, 0, 4349, 4350, 4351,
};

static const of_unichar_t casefoldingPage0[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
	4312, 4313, 4314, 4315, 4316, 4317, 4318, 4319,
	4320, 4321, 4322, 4323, 4324, 4325, 4326, 4327,
	4328, 4329, 4330, 4331, 4332, 4333, 4334, 4335,
	4336, 4337, 4338, 4339, 4340, 4341, 4342, 4343,
	4344, 4345, 4346, 0, 0, 4349, 4350, 4351,
};

static const OFUnichar caseFoldingPage0[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
	248, 249, 250, 251, 252, 253, 254, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t casefoldingPage1[0x100] = {
	257, 0, 259, 0, 261, 0, 263, 0,
	265, 0, 267, 0, 269, 0, 271, 0,
	273, 0, 275, 0, 277, 0, 279, 0,
	281, 0, 283, 0, 285, 0, 287, 0,
	289, 0, 291, 0, 293, 0, 295, 0,
	297, 0, 299, 0, 301, 0, 303, 0,
	0, 0, 307, 0, 309, 0, 311, 0,







|







1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
	248, 249, 250, 251, 252, 253, 254, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar caseFoldingPage1[0x100] = {
	257, 0, 259, 0, 261, 0, 263, 0,
	265, 0, 267, 0, 269, 0, 271, 0,
	273, 0, 275, 0, 277, 0, 279, 0,
	281, 0, 283, 0, 285, 0, 287, 0,
	289, 0, 291, 0, 293, 0, 295, 0,
	297, 0, 299, 0, 301, 0, 303, 0,
	0, 0, 307, 0, 309, 0, 311, 0,
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
	0, 474, 0, 476, 0, 0, 479, 0,
	481, 0, 483, 0, 485, 0, 487, 0,
	489, 0, 491, 0, 493, 0, 495, 0,
	0, 499, 499, 0, 501, 0, 405, 447,
	505, 0, 507, 0, 509, 0, 511, 0,
};

static const of_unichar_t casefoldingPage3[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
	0, 474, 0, 476, 0, 0, 479, 0,
	481, 0, 483, 0, 485, 0, 487, 0,
	489, 0, 491, 0, 493, 0, 495, 0,
	0, 499, 499, 0, 501, 0, 405, 447,
	505, 0, 507, 0, 509, 0, 511, 0,
};

static const OFUnichar caseFoldingPage3[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
	985, 0, 987, 0, 989, 0, 991, 0,
	993, 0, 995, 0, 997, 0, 999, 0,
	1001, 0, 1003, 0, 1005, 0, 1007, 0,
	954, 961, 0, 0, 952, 949, 0, 1016,
	0, 1010, 1019, 0, 0, 891, 892, 893,
};

static const of_unichar_t casefoldingPage19[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
	985, 0, 987, 0, 989, 0, 991, 0,
	993, 0, 995, 0, 997, 0, 999, 0,
	1001, 0, 1003, 0, 1005, 0, 1007, 0,
	954, 961, 0, 0, 952, 949, 0, 1016,
	0, 1010, 1019, 0, 0, 891, 892, 893,
};

static const OFUnichar caseFoldingPage19[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	5104, 5105, 5106, 5107, 5108, 5109, 0, 0,
};

static const of_unichar_t casefoldingPage28[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	5104, 5105, 5106, 5107, 5108, 5109, 0, 0,
};

static const OFUnichar caseFoldingPage28[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const of_unichar_t casefoldingPage30[0x100] = {
	7681, 0, 7683, 0, 7685, 0, 7687, 0,
	7689, 0, 7691, 0, 7693, 0, 7695, 0,
	7697, 0, 7699, 0, 7701, 0, 7703, 0,
	7705, 0, 7707, 0, 7709, 0, 7711, 0,
	7713, 0, 7715, 0, 7717, 0, 7719, 0,
	7721, 0, 7723, 0, 7725, 0, 7727, 0,
	7729, 0, 7731, 0, 7733, 0, 7735, 0,







|







1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
};

static const OFUnichar caseFoldingPage30[0x100] = {
	7681, 0, 7683, 0, 7685, 0, 7687, 0,
	7689, 0, 7691, 0, 7693, 0, 7695, 0,
	7697, 0, 7699, 0, 7701, 0, 7703, 0,
	7705, 0, 7707, 0, 7709, 0, 7711, 0,
	7713, 0, 7715, 0, 7717, 0, 7719, 0,
	7721, 0, 7723, 0, 7725, 0, 7727, 0,
	7729, 0, 7731, 0, 7733, 0, 7735, 0,
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
	7897, 0, 7899, 0, 7901, 0, 7903, 0,
	7905, 0, 7907, 0, 7909, 0, 7911, 0,
	7913, 0, 7915, 0, 7917, 0, 7919, 0,
	7921, 0, 7923, 0, 7925, 0, 7927, 0,
	7929, 0, 7931, 0, 7933, 0, 7935, 0,
};

static const of_unichar_t casefoldingPage31[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943,
	0, 0, 0, 0, 0, 0, 0, 0,
	7952, 7953, 7954, 7955, 7956, 7957, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	7968, 7969, 7970, 7971, 7972, 7973, 7974, 7975,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
	7897, 0, 7899, 0, 7901, 0, 7903, 0,
	7905, 0, 7907, 0, 7909, 0, 7911, 0,
	7913, 0, 7915, 0, 7917, 0, 7919, 0,
	7921, 0, 7923, 0, 7925, 0, 7927, 0,
	7929, 0, 7931, 0, 7933, 0, 7935, 0,
};

static const OFUnichar caseFoldingPage31[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943,
	0, 0, 0, 0, 0, 0, 0, 0,
	7952, 7953, 7954, 7955, 7956, 7957, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	7968, 7969, 7970, 7971, 7972, 7973, 7974, 7975,
	0, 0, 0, 0, 0, 0, 0, 0,
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
	8144, 8145, 8054, 8055, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	8160, 8161, 8058, 8059, 8165, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	8056, 8057, 8060, 8061, 8179, 0, 0, 0,
};

static const of_unichar_t casefoldingPage171[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,







|







1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
	8144, 8145, 8054, 8055, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	8160, 8161, 8058, 8059, 8165, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	8056, 8057, 8060, 8061, 8179, 0, 0, 0,
};

static const OFUnichar caseFoldingPage171[0x100] = {
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
12362
12363
12364
12365
12366
12367
12368
12369
12370
12371
12372
12373
12374
12375
12376
	"\x36", "\x37",
	"\x38", "\x39",
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
};

const of_unichar_t *const of_unicode_uppercase_table[0x1EA] = {
	uppercasePage0, uppercasePage1, uppercasePage2, uppercasePage3,
	uppercasePage4, uppercasePage5, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	uppercasePage16, emptyPage, emptyPage, uppercasePage19,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,







|







12360
12361
12362
12363
12364
12365
12366
12367
12368
12369
12370
12371
12372
12373
12374
	"\x36", "\x37",
	"\x38", "\x39",
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
};

const OFUnichar *const OFUnicodeUppercaseTable[0x1EA] = {
	uppercasePage0, uppercasePage1, uppercasePage2, uppercasePage3,
	uppercasePage4, uppercasePage5, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	uppercasePage16, emptyPage, emptyPage, uppercasePage19,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
12488
12489
12490
12491
12492
12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, uppercasePage489
};

const of_unichar_t *const of_unicode_lowercase_table[0x1EA] = {
	lowercasePage0, lowercasePage1, lowercasePage2, lowercasePage3,
	lowercasePage4, lowercasePage5, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	lowercasePage16, emptyPage, emptyPage, lowercasePage19,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,







|







12486
12487
12488
12489
12490
12491
12492
12493
12494
12495
12496
12497
12498
12499
12500
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, uppercasePage489
};

const OFUnichar *const OFUnicodeLowercaseTable[0x1EA] = {
	lowercasePage0, lowercasePage1, lowercasePage2, lowercasePage3,
	lowercasePage4, lowercasePage5, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	lowercasePage16, emptyPage, emptyPage, lowercasePage19,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
12614
12615
12616
12617
12618
12619
12620
12621
12622
12623
12624
12625
12626
12627
12628
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, lowercasePage489
};

const of_unichar_t *const of_unicode_titlecase_table[0x1EA] = {
	uppercasePage0, titlecasePage1, uppercasePage2, uppercasePage3,
	uppercasePage4, uppercasePage5, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	titlecasePage16, emptyPage, emptyPage, uppercasePage19,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,







|







12612
12613
12614
12615
12616
12617
12618
12619
12620
12621
12622
12623
12624
12625
12626
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, lowercasePage489
};

const OFUnichar *const OFUnicodeTitlecaseTable[0x1EA] = {
	uppercasePage0, titlecasePage1, uppercasePage2, uppercasePage3,
	uppercasePage4, uppercasePage5, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	titlecasePage16, emptyPage, emptyPage, uppercasePage19,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
12740
12741
12742
12743
12744
12745
12746
12747
12748
12749
12750
12751
12752
12753
12754
12755
12756
12757
12758
12759
12760
12761
12762
12763
12764
12765
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, uppercasePage489
};

const of_unichar_t *const of_unicode_casefolding_table[0x1EA] = {
	casefoldingPage0, casefoldingPage1, lowercasePage2,
	casefoldingPage3, lowercasePage4, lowercasePage5,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, lowercasePage16, emptyPage,
	emptyPage, casefoldingPage19, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, casefoldingPage28, emptyPage,
	casefoldingPage30, casefoldingPage31, emptyPage,
	lowercasePage33, emptyPage, emptyPage,
	lowercasePage36, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, lowercasePage44,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,







|
|
|




|


|
|







12738
12739
12740
12741
12742
12743
12744
12745
12746
12747
12748
12749
12750
12751
12752
12753
12754
12755
12756
12757
12758
12759
12760
12761
12762
12763
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage, emptyPage,
	emptyPage, uppercasePage489
};

const OFUnichar *const OFUnicodeCaseFoldingTable[0x1EA] = {
	caseFoldingPage0, caseFoldingPage1, lowercasePage2,
	caseFoldingPage3, lowercasePage4, lowercasePage5,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, lowercasePage16, emptyPage,
	emptyPage, caseFoldingPage19, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, caseFoldingPage28, emptyPage,
	caseFoldingPage30, caseFoldingPage31, emptyPage,
	lowercasePage33, emptyPage, emptyPage,
	lowercasePage36, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, lowercasePage44,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
12798
12799
12800
12801
12802
12803
12804
12805
12806
12807
12808
12809
12810
12811
12812
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, lowercasePage166, lowercasePage167,
	emptyPage, emptyPage, emptyPage,
	casefoldingPage171, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,







|







12796
12797
12798
12799
12800
12801
12802
12803
12804
12805
12806
12807
12808
12809
12810
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, lowercasePage166, lowercasePage167,
	emptyPage, emptyPage, emptyPage,
	caseFoldingPage171, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
12907
12908
12909
12910
12911
12912
12913
12914
12915
12916
12917
12918
12919
12920
12921
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	lowercasePage489
};

const char *const *of_unicode_decomposition_table[0x2FB] = {
	decompositionPage0, decompositionPage1, decompositionPage2,
	decompositionPage3, decompositionPage4, emptyDecompositionPage,
	decompositionPage6, emptyDecompositionPage, emptyDecompositionPage,
	decompositionPage9, decompositionPage10, decompositionPage11,
	decompositionPage12, decompositionPage13, emptyDecompositionPage,
	decompositionPage15, decompositionPage16, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,







|







12905
12906
12907
12908
12909
12910
12911
12912
12913
12914
12915
12916
12917
12918
12919
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	emptyPage, emptyPage, emptyPage,
	lowercasePage489
};

const char *const *OFUnicodeDecompositionTable[0x2FB] = {
	decompositionPage0, decompositionPage1, decompositionPage2,
	decompositionPage3, decompositionPage4, emptyDecompositionPage,
	decompositionPage6, emptyDecompositionPage, emptyDecompositionPage,
	decompositionPage9, decompositionPage10, decompositionPage11,
	decompositionPage12, decompositionPage13, emptyDecompositionPage,
	decompositionPage15, decompositionPage16, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
13165
13166
13167
13168
13169
13170
13171
13172
13173
13174
13175
13176
13177
13178
13179
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, decompositionPage760, decompositionPage761,
	decompositionPage762
};

const char *const *of_unicode_decomposition_compat_table[0x2FB] = {
	decompCompatPage0, decompCompatPage1, decompCompatPage2,
	decompCompatPage3, decompositionPage4, decompCompatPage5,
	decompCompatPage6, emptyDecompositionPage, emptyDecompositionPage,
	decompositionPage9, decompositionPage10, decompositionPage11,
	decompCompatPage12, decompCompatPage13, decompCompatPage14,
	decompCompatPage15, decompCompatPage16, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,







|







13163
13164
13165
13166
13167
13168
13169
13170
13171
13172
13173
13174
13175
13176
13177
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, decompositionPage760, decompositionPage761,
	decompositionPage762
};

const char *const *OFUnicodeDecompositionCompatTable[0x2FB] = {
	decompCompatPage0, decompCompatPage1, decompCompatPage2,
	decompCompatPage3, decompositionPage4, decompCompatPage5,
	decompCompatPage6, emptyDecompositionPage, emptyDecompositionPage,
	decompositionPage9, decompositionPage10, decompositionPage11,
	decompCompatPage12, decompCompatPage13, decompCompatPage14,
	decompCompatPage15, decompCompatPage16, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,

Modified src/unistd_wrapper.h from [b988d01088] to [10576ea4f7].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *

Modified tests/ForwardingTests.m from [193bc733e3] to [de62832427].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

#define FMT @"%@ %@ %@ %@ %@ %@ %@ %@ %@ %g %g %g %g %g %g %g %g %g"
#define ARGS @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", \
	    1.5, 2.25, 3.125, 4.0625, 5.03125, 6.5, 7.25, 8.0, 9.0
#define RESULT @"a b c d e f g h i 1.5 2.25 3.125 4.0625 5.03125 6.5 7.25 8 9"

static OFString *module = @"Forwarding";
static size_t forwardings = 0;
static bool success = false;
static id target = nil;

struct stret_test {
	char s[1024];
};

@interface ForwardingTest: OFObject
@end

@interface ForwardingTest (Test)
+ (void)test;
- (void)test;
- (uint32_t)forwardingTargetTest: (intptr_t)a0
				: (intptr_t)a1
				: (double)a2
				: (double)a3;
- (OFString *)forwardingTargetVarArgTest: (OFConstantString *)fmt, ...;
- (long double)forwardingTargetFPRetTest;
- (struct stret_test)forwardingTargetStRetTest;
- (void)forwardingTargetNilTest;
- (void)forwardingTargetSelfTest;
- (struct stret_test)forwardingTargetNilStRetTest;
- (struct stret_test)forwardingTargetSelfStRetTest;
@end

@interface ForwardingTarget: OFObject
@end

static void
test(id self, SEL _cmd)
{
	success = true;
}

@implementation ForwardingTest
+ (bool)resolveClassMethod: (SEL)selector
{
	forwardings++;

	if (sel_isEqual(selector, @selector(test))) {
		class_replaceMethod(object_getClass(self), @selector(test),
		    (IMP)test, "v#:");
		return YES;
	}

	return NO;
}

+ (bool)resolveInstanceMethod: (SEL)selector
{
	forwardings++;

	if (sel_isEqual(selector, @selector(test))) {
		class_replaceMethod(self, @selector(test), (IMP)test, "v@:");
		return YES;
	}

	return NO;

<
<
|



















|




|
|



|
|












|

|


|
|














|












|







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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

#define FORMAT @"%@ %@ %@ %@ %@ %@ %@ %@ %@ %g %g %g %g %g %g %g %g %g"
#define ARGS @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", \
	    1.5, 2.25, 3.125, 4.0625, 5.03125, 6.5, 7.25, 8.0, 9.0
#define RESULT @"a b c d e f g h i 1.5 2.25 3.125 4.0625 5.03125 6.5 7.25 8 9"

static OFString *const module = @"Forwarding";
static size_t forwardingsCount = 0;
static bool success = false;
static id target = nil;

struct StretTest {
	char buffer[1024];
};

@interface ForwardingTest: OFObject
@end

@interface ForwardingTest (Test)
+ (void)test;
- (void)test;
- (uint32_t)forwardingTargetTest: (intptr_t)a0
				: (intptr_t)a1
				: (double)a2
				: (double)a3;
- (OFString *)forwardingTargetVarArgTest: (OFConstantString *)format, ...;
- (long double)forwardingTargetFPRetTest;
- (struct StretTest)forwardingTargetStRetTest;
- (void)forwardingTargetNilTest;
- (void)forwardingTargetSelfTest;
- (struct StretTest)forwardingTargetNilStRetTest;
- (struct StretTest)forwardingTargetSelfStRetTest;
@end

@interface ForwardingTarget: OFObject
@end

static void
test(id self, SEL _cmd)
{
	success = true;
}

@implementation ForwardingTest
+ (bool)resolveClassMethod: (SEL)selector
{
	forwardingsCount++;

	if (sel_isEqual(selector, @selector(test))) {
		class_replaceMethod(object_getClass(self), @selector(test),
		    (IMP)test, "v#:");
		return YES;
	}

	return NO;
}

+ (bool)resolveInstanceMethod: (SEL)selector
{
	forwardingsCount++;

	if (sel_isEqual(selector, @selector(test))) {
		class_replaceMethod(self, @selector(test), (IMP)test, "v@:");
		return YES;
	}

	return NO;
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

@implementation ForwardingTarget
- (uint32_t)forwardingTargetTest: (intptr_t)a0
				: (intptr_t)a1
				: (double)a2
				: (double)a3
{
	OF_ENSURE(self == target);

	if (a0 != (intptr_t)0xDEADBEEF)
		return 0;
	if (a1 != -1)
		return 0;
	if (a2 != 1.25)
		return 0;
	if (a3 != 2.75)
		return 0;

	return 0x12345678;
}

- (OFString *)forwardingTargetVarArgTest: (OFConstantString *)fmt, ...
{
	va_list args;
	OFString *ret;

	OF_ENSURE(self == target);

	va_start(args, fmt);
	ret = [[[OFString alloc] initWithFormat: fmt
				      arguments: args] autorelease];
	va_end(args);

	return ret;
}

- (long double)forwardingTargetFPRetTest
{
	OF_ENSURE(self == target);

	return 12345678.00006103515625;
}

- (struct stret_test)forwardingTargetStRetTest
{
	struct stret_test ret = { { 0 } };

	OF_ENSURE(self == target);

	memcpy(ret.s, "abcdefghijklmnopqrstuvwxyz", 27);

	return ret;
}
@end

@implementation TestsAppDelegate (ForwardingTests)
- (void)forwardingTests
{
	void *pool = objc_autoreleasePoolPush();

	TEST(@"Forwarding a message and adding a class method",
	    R([ForwardingTest test]) && success &&
	    R([ForwardingTest test]) && forwardings == 1);


	ForwardingTest *t = [[[ForwardingTest alloc] init] autorelease];

	success = false;
	forwardings = 0;

	TEST(@"Forwarding a message and adding an instance method",
	    R([t test]) && success && R([t test]) && forwardings == 1);


#ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
	target = [[[ForwardingTarget alloc] init] autorelease];
	TEST(@"-[forwardingTargetForSelector:]",
	    [t forwardingTargetTest: 0xDEADBEEF
				   : -1
				   : 1.25
				   : 2.75] == 0x12345678)
	TEST(@"-[forwardingTargetForSelector:] variable arguments",
	    [[t forwardingTargetVarArgTest: FMT, ARGS] isEqual: RESULT])

	/*
	 * Don't try fpret on Win64 if we don't have stret forwarding, as
	 * long double is handled as a struct there.
	 */
# if !defined(OF_WINDOWS) || !defined(OF_X86_64) || \
	defined(OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET)
	TEST(@"-[forwardingTargetForSelector:] fp return",
	    [t forwardingTargetFPRetTest] == 12345678.00006103515625)
# endif
# ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
	TEST(@"-[forwardingTargetForSelector:] struct return",
	    !memcmp([t forwardingTargetStRetTest].s,
	    "abcdefghijklmnopqrstuvwxyz", 27))
# endif
	EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] nil target",
	    OFNotImplementedException, [t forwardingTargetNilTest])
	EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] self target",
	    OFNotImplementedException, [t forwardingTargetSelfTest])
# ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
	EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] nil target + "
	    @"stret", OFNotImplementedException,
	    [t forwardingTargetNilStRetTest])
	EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] self target + "
	    @"stret", OFNotImplementedException,
	    [t forwardingTargetSelfStRetTest])
# endif
#endif

	objc_autoreleasePoolPop(pool);
}
@end







|













|




|

|
|








|




|

|

|

|












|

>
|


|


|
>




|
|
|
|

|
>







|



|



|

|



|


|






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

@implementation ForwardingTarget
- (uint32_t)forwardingTargetTest: (intptr_t)a0
				: (intptr_t)a1
				: (double)a2
				: (double)a3
{
	OFEnsure(self == target);

	if (a0 != (intptr_t)0xDEADBEEF)
		return 0;
	if (a1 != -1)
		return 0;
	if (a2 != 1.25)
		return 0;
	if (a3 != 2.75)
		return 0;

	return 0x12345678;
}

- (OFString *)forwardingTargetVarArgTest: (OFConstantString *)format, ...
{
	va_list args;
	OFString *ret;

	OFEnsure(self == target);

	va_start(args, format);
	ret = [[[OFString alloc] initWithFormat: format
				      arguments: args] autorelease];
	va_end(args);

	return ret;
}

- (long double)forwardingTargetFPRetTest
{
	OFEnsure(self == target);

	return 12345678.00006103515625;
}

- (struct StretTest)forwardingTargetStRetTest
{
	struct StretTest ret = { { 0 } };

	OFEnsure(self == target);

	memcpy(ret.buffer, "abcdefghijklmnopqrstuvwxyz", 27);

	return ret;
}
@end

@implementation TestsAppDelegate (ForwardingTests)
- (void)forwardingTests
{
	void *pool = objc_autoreleasePoolPush();

	TEST(@"Forwarding a message and adding a class method",
	    R([ForwardingTest test]) && success &&
	    R([ForwardingTest test]) && forwardingsCount == 1);

	ForwardingTest *testObject =
	    [[[ForwardingTest alloc] init] autorelease];

	success = false;
	forwardingsCount = 0;

	TEST(@"Forwarding a message and adding an instance method",
	    R([testObject test]) && success && R([testObject test]) &&
	    forwardingsCount == 1);

#ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
	target = [[[ForwardingTarget alloc] init] autorelease];
	TEST(@"-[forwardingTargetForSelector:]",
	    [testObject forwardingTargetTest: 0xDEADBEEF
					    : -1
					    : 1.25
					    : 2.75] == 0x12345678)
	TEST(@"-[forwardingTargetForSelector:] variable arguments",
	    [[testObject forwardingTargetVarArgTest: FORMAT, ARGS]
	    isEqual: RESULT])
	/*
	 * Don't try fpret on Win64 if we don't have stret forwarding, as
	 * long double is handled as a struct there.
	 */
# if !defined(OF_WINDOWS) || !defined(OF_X86_64) || \
	defined(OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET)
	TEST(@"-[forwardingTargetForSelector:] fp return",
	    [testObject forwardingTargetFPRetTest] == 12345678.00006103515625)
# endif
# ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
	TEST(@"-[forwardingTargetForSelector:] struct return",
	    !memcmp([testObject forwardingTargetStRetTest].buffer,
	    "abcdefghijklmnopqrstuvwxyz", 27))
# endif
	EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] nil target",
	    OFNotImplementedException, [testObject forwardingTargetNilTest])
	EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] self target",
	    OFNotImplementedException, [testObject forwardingTargetSelfTest])
# ifdef OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
	EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] nil target + "
	    @"stret", OFNotImplementedException,
	    [testObject forwardingTargetNilStRetTest])
	EXPECT_EXCEPTION(@"-[forwardingTargetForSelector:] self target + "
	    @"stret", OFNotImplementedException,
	    [testObject forwardingTargetSelfStRetTest])
# endif
#endif

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/ImportTest.m from [8e80f2e0e7] to [50763ee365].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *

Modified tests/Makefile from [6a6c363390] to [bb692e21b0].

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
include ../extra.mk

SUBDIRS = ${TESTPLUGIN}



CLEAN = EBOOT.PBP		\
	boot.dol		\
	${PROG_NOINST}.arm9	\
	${PROG_NOINST}.nds
DISTCLEAN = Info.plist

PROG_NOINST = tests${PROG_SUFFIX}
STATIC_LIB_NOINST = ${TESTS_STATIC_LIB}
SRCS = ForwardingTests.m		\
       OFASN1DERParsingTests.m		\
       OFASN1DERRepresentationTests.m	\
       OFArrayTests.m			\
       ${OF_BLOCK_TESTS_M}		\
       OFCharacterSetTests.m		\
       OFDataTests.m			\
       OFDateTests.m			\
       OFDictionaryTests.m		\
       OFInvocationTests.m		\
       OFJSONTests.m			\
       OFListTests.m			\
       OFLocaleTests.m			\
       OFMethodSignatureTests.m		\

       OFNumberTests.m			\
       OFObjectTests.m			\

       OFPropertyListTests.m		\

       OFSetTests.m			\
       OFStreamTests.m			\
       OFStringTests.m			\
       OFSystemInfoTests.m		\
       OFURLTests.m			\
       OFValueTests.m			\
       OFXMLElementBuilderTests.m	\
       OFXMLNodeTests.m			\
       OFXMLParserTests.m		\
       PBKDF2Tests.m			\
       RuntimeTests.m			\
       ${RUNTIME_ARC_TESTS_M}		\
       ScryptTests.m			\
       TestsAppDelegate.m		\
       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_THREADS}		\
       ${USE_SRCS_WINDOWS}
SRCS_FILES = OFHMACTests.m		\
	     OFINIFileTests.m		\
	     OFMD5HashTests.m		\
	     OFRIPEMD160HashTests.m	\
	     OFSerializationTests.m	\
	     OFSHA1HashTests.m		\
	     OFSHA224HashTests.m	\
	     OFSHA256HashTests.m	\
	     OFSHA384HashTests.m	\
	     OFSHA512HashTests.m
SRCS_IPX = OFIPXSocketTests.m		\
	   OFSPXSocketTests.m		\
	   OFSPXStreamSocketTests.m
SRCS_PLUGINS = OFPluginTests.m
SRCS_SCTP = OFSCTPSocketTests.m
SRCS_SOCKETS = OFDNSResolverTests.m		\
	       ${OF_HTTP_CLIENT_TESTS_M}	\
	       OFHTTPCookieTests.m		\
	       OFHTTPCookieManagerTests.m	\
	       OFKernelEventObserverTests.m	\
	       OFTCPSocketTests.m		\
	       OFUDPSocketTests.m		\
	       SocketTests.m			\
	       ${USE_SRCS_IPX}			\
	       ${USE_SRCS_SCTP}





SRCS_THREADS = OFThreadTests.m
SRCS_WINDOWS = OFWindowsRegistryKeyTests.m

IOS_USER ?= mobile
IOS_TMP ?= /tmp/objfw-test

include ../buildsys.mk

post-all: ${RUN_TESTS}

.PHONY: run run-on-ios run-on-android
run:
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}

	rm -f objfwrt.dll libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../src/libobjfw.so; then \
		${LN_S} ../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../src/objfw.dll; then \
		${LN_S} ../src/objfw.dll objfw.dll; \

	fi
	if test -f ../src/libobjfw.dylib; then \
		${LN_S} ../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../src/runtime/libobjfwrt.so; then \
		${LN_S} ../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../src/runtime/objfwrt.dll; then \
		${LN_S} ../src/runtime/objfwrt.dll objfwrt.dll; \

	fi
	if test -f ../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_FRAMEWORK_PATH=../src:../src/runtime$${DYLD_FRAMEWORK_PATH+:}$$DYLD_FRAMEWORK_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	ASAN_OPTIONS=allocator_may_return_null=1 \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} objfw.dll; \

	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} objfwrt.dll; \

	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

run-on-ios: all
	if [ -z "${IOS_HOST}" ]; then \
		echo "Please set IOS_HOST to the hostname of your iOS host!"; \
		exit 1; \


|
>
>










<
<











>


>

>









<


<
















<
<
<

<





|
|
|

|
>
>
>
>
>








<
<




|


>
|









|
|
>













|
|
>
















|
>


|
>







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
include ../extra.mk

SUBDIRS = ${TESTPLUGIN}	\
	  ${OBJC_SYNC}	\
	  terminal

CLEAN = EBOOT.PBP		\
	boot.dol		\
	${PROG_NOINST}.arm9	\
	${PROG_NOINST}.nds
DISTCLEAN = Info.plist

PROG_NOINST = tests${PROG_SUFFIX}
STATIC_LIB_NOINST = ${TESTS_STATIC_LIB}
SRCS = ForwardingTests.m		\


       OFArrayTests.m			\
       ${OF_BLOCK_TESTS_M}		\
       OFCharacterSetTests.m		\
       OFDataTests.m			\
       OFDateTests.m			\
       OFDictionaryTests.m		\
       OFInvocationTests.m		\
       OFJSONTests.m			\
       OFListTests.m			\
       OFLocaleTests.m			\
       OFMethodSignatureTests.m		\
       OFNotificationCenterTests.m	\
       OFNumberTests.m			\
       OFObjectTests.m			\
       OFPBKDF2Tests.m			\
       OFPropertyListTests.m		\
       OFScryptTests.m			\
       OFSetTests.m			\
       OFStreamTests.m			\
       OFStringTests.m			\
       OFSystemInfoTests.m		\
       OFURLTests.m			\
       OFValueTests.m			\
       OFXMLElementBuilderTests.m	\
       OFXMLNodeTests.m			\
       OFXMLParserTests.m		\

       RuntimeTests.m			\
       ${RUNTIME_ARC_TESTS_M}		\

       TestsAppDelegate.m		\
       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_THREADS}		\
       ${USE_SRCS_WINDOWS}
SRCS_FILES = OFHMACTests.m		\
	     OFINIFileTests.m		\
	     OFMD5HashTests.m		\
	     OFRIPEMD160HashTests.m	\
	     OFSerializationTests.m	\
	     OFSHA1HashTests.m		\
	     OFSHA224HashTests.m	\
	     OFSHA256HashTests.m	\
	     OFSHA384HashTests.m	\
	     OFSHA512HashTests.m



SRCS_PLUGINS = OFPluginTests.m

SRCS_SOCKETS = OFDNSResolverTests.m		\
	       ${OF_HTTP_CLIENT_TESTS_M}	\
	       OFHTTPCookieTests.m		\
	       OFHTTPCookieManagerTests.m	\
	       OFKernelEventObserverTests.m	\
	       OFSocketTests.m			\
	       OFTCPSocketTests.m		\
	       OFUDPSocketTests.m		\
	       ${USE_SRCS_IPX}			\
	       ${USE_SRCS_UNIX_SOCKETS}
SRCS_IPX = OFIPXSocketTests.m		\
	   OFSPXSocketTests.m		\
	   OFSPXStreamSocketTests.m
SRCS_UNIX_SOCKETS = OFUNIXDatagramSocketTests.m	\
		    OFUNIXStreamSocketTests.m
SRCS_THREADS = OFThreadTests.m
SRCS_WINDOWS = OFWindowsRegistryKeyTests.m

IOS_USER ?= mobile
IOS_TMP ?= /tmp/objfw-test

include ../buildsys.mk



.PHONY: run run-on-ios run-on-android
run:
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw${OBJFW_LIB_MAJOR}.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../src/libobjfw.so; then \
		${LN_S} ../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../src/objfw${OBJFW_LIB_MAJOR}.dll; then \
		${LN_S} ../src/objfw${OBJFW_LIB_MAJOR}.dll \
			objfw${OBJFW_LIB_MAJOR}.dll; \
	fi
	if test -f ../src/libobjfw.dylib; then \
		${LN_S} ../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../src/runtime/libobjfwrt.so; then \
		${LN_S} ../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll; then \
		${LN_S} ../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll \
			objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	fi
	if test -f ../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_FRAMEWORK_PATH=../src:../src/runtime$${DYLD_FRAMEWORK_PATH+:}$$DYLD_FRAMEWORK_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	ASAN_OPTIONS=allocator_may_return_null=1 \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	rm -f objfw${OBJFW_LIB_MAJOR}.dll; \
	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

run-on-ios: all
	if [ -z "${IOS_HOST}" ]; then \
		echo "Please set IOS_HOST to the hostname of your iOS host!"; \
		exit 1; \

Deleted tests/OFASN1DERParsingTests.m version [dd4c3da7d2].

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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFData+ASN1DERParsing";

@implementation TestsAppDelegate (OFASN1DERParsingTests)
- (void)ASN1DERParsingTests
{
	void *pool = objc_autoreleasePoolPush();
	OFASN1BitString *bitString;
	OFArray *array;
	OFSet *set;
	OFEnumerator *enumerator;

	/* Boolean */
	TEST(@"Parsing of boolean",
	    ![[[OFData dataWithItems: "\x01\x01\x00"
			       count: 3] objectByParsingASN1DER]
	    booleanValue] &&
	    [[[OFData dataWithItems: "\x01\x01\xFF"
			      count: 3] objectByParsingASN1DER] booleanValue])

	EXPECT_EXCEPTION(@"Detection of invalid boolean #1",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x01\x01\x01"
			     count: 3] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid boolean #2",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x01\x02\x00\x00"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid boolean #3",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x01\x00"
			     count: 2] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated boolean",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x01\x01"
			     count: 2] objectByParsingASN1DER])

	/* Integer */
	TEST(@"Parsing of integer",
	    [[[OFData dataWithItems: "\x02\x00"
			      count: 2] objectByParsingASN1DER]
	    longLongValue] == 0 &&
	    [[[OFData dataWithItems: "\x02\x01\x01"
			      count: 3] objectByParsingASN1DER]
	    longLongValue] == 1 &&
	    [[[OFData dataWithItems: "\x02\x02\x01\x04"
			      count: 4] objectByParsingASN1DER]
	    longLongValue] == 260 &&
	    [[[OFData dataWithItems: "\x02\x01\xFF"
			      count: 3] objectByParsingASN1DER]
	    longLongValue] == -1 &&
	    [[[OFData dataWithItems: "\x02\x03\xFF\x00\x00"
			      count: 5] objectByParsingASN1DER]
	    longLongValue] == -65536 &&
	    (unsigned long long)[[[OFData dataWithItems: "\x02\x09\x00\xFF\xFF"
							 "\xFF\xFF\xFF\xFF\xFF"
							 "\xFF"
						  count: 11]
	    objectByParsingASN1DER] longLongValue] == ULLONG_MAX)

	EXPECT_EXCEPTION(@"Detection of invalid integer #1",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x02\x02\x00\x00"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid integer #2",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x02\x02\x00\x7F"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid integer #3",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x02\x02\xFF\x80"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of out of range integer",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x02\x09\x01"
				    "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
			     count: 11] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated integer",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x02\x02\x00"
			     count: 3] objectByParsingASN1DER])

	/* Bit string */
	TEST(@"Parsing of bit string",
	    (bitString = [[OFData dataWithItems: "\x03\x01\x00"
					  count: 3] objectByParsingASN1DER]) &&
	    [bitString.bitStringValue isEqual: [OFData dataWithItems: ""
							       count: 0]] &&
	    bitString.bitStringLength == 0 &&
	    (bitString = [[OFData dataWithItems: "\x03\x0D\x01Hello World\x80"
					  count: 15] objectByParsingASN1DER]) &&
	    [bitString.bitStringValue
	    isEqual: [OFData dataWithItems: "Hello World\x80"
				     count: 12]] &&
	    bitString.bitStringLength == 95 &&
	    (bitString = [[OFData dataWithItems: "\x03\x81\x80\x00xxxxxxxxxxxxx"
						 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
						 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
						 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
						 "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
					 count: 131] objectByParsingASN1DER]) &&
	    [bitString.bitStringValue
	    isEqual: [OFData dataWithItems: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
					    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
					    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
					    "xxxxxxxxxxxxxxxxxxxxxxxxx"
				     count: 127]] &&
	    bitString.bitStringLength == 127 * 8)

	EXPECT_EXCEPTION(@"Detection of invalid bit string #1",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x03\x00"
			     count: 2] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid bit string #2",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x03\x01\x01"
			     count: 3] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of out of range bit string",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x03\x89"
				    "\x01\x01\x01\x01\x01\x01\x01\x01\x01"
			     count: 11] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated bit string",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x03\x01"
			     count: 2] objectByParsingASN1DER])

	/* Octet string */
	TEST(@"Parsing of octet string",
	    [[[[OFData dataWithItems: "\x04\x0CHello World!"
			       count: 14] objectByParsingASN1DER]
	    octetStringValue] isEqual: [OFData dataWithItems: "Hello World!"
						       count: 12]] &&
	    [[[[OFData dataWithItems: "\x04\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxx"
			       count: 131] objectByParsingASN1DER]
	    octetStringValue] isEqual:
	    [OFData dataWithItems: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				   "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				   "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
			    count: 128]])

	EXPECT_EXCEPTION(@"Detection of out of range octet string",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x04\x89"
				    "\x01\x01\x01\x01\x01\x01\x01\x01\x01"
			     count: 11] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated octet string",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x04\x01"
			     count: 2] objectByParsingASN1DER])

	/* Null */
	TEST(@"Parsing of null",
	    [[[OFData dataWithItems: "\x05\x00"
			      count: 2] objectByParsingASN1DER]
	    isEqual: [OFNull null]])

	EXPECT_EXCEPTION(@"Detection of invalid null",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x05\x01\x00"
			     count: 3] objectByParsingASN1DER])

	/* Object Identifier */
	TEST(@"Parsing of Object Identifier",
	    (array = [[[OFData dataWithItems: "\x06\x01\x27"
				       count: 3] objectByParsingASN1DER]
	    subidentifiers]) && array.count == 2 &&
	    [[array objectAtIndex: 0] unsignedLongLongValue] == 0 &&
	    [[array objectAtIndex: 1] unsignedLongLongValue] == 39 &&
	    (array = [[[OFData dataWithItems: "\x06\x01\x4F"
				       count: 3] objectByParsingASN1DER]
	    subidentifiers]) && array.count == 2 &&
	    [[array objectAtIndex: 0] unsignedLongLongValue] == 1 &&
	    [[array objectAtIndex: 1] unsignedLongLongValue] == 39 &&
	    (array = [[[OFData dataWithItems: "\x06\x02\x88\x37"
				       count: 4] objectByParsingASN1DER]
	    subidentifiers]) && array.count == 2 &&
	    [[array objectAtIndex: 0] unsignedLongLongValue] == 2 &&
	    [[array objectAtIndex: 1] unsignedLongLongValue] == 999 &&
	    (array = [[[OFData dataWithItems: "\x06\x09\x2A\x86\x48\x86\xF7\x0D"
					      "\x01\x01\x0B"
				       count: 11] objectByParsingASN1DER]
	    subidentifiers]) && array.count == 7 &&
	    [[array objectAtIndex: 0] unsignedLongLongValue] == 1 &&
	    [[array objectAtIndex: 1] unsignedLongLongValue] == 2 &&
	    [[array objectAtIndex: 2] unsignedLongLongValue] == 840 &&
	    [[array objectAtIndex: 3] unsignedLongLongValue] == 113549 &&
	    [[array objectAtIndex: 4] unsignedLongLongValue] == 1 &&
	    [[array objectAtIndex: 5] unsignedLongLongValue] == 1 &&
	    [[array objectAtIndex: 6] unsignedLongLongValue] == 11)

	EXPECT_EXCEPTION(@"Detection of invalid Object Identifier #1",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x06\x01\x81"
			     count: 3] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid Object Identifier #2",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x06\x02\x80\x01"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of out of range Object Identifier",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x06\x0A\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
				    "\xFF\x7F"
			     count: 12] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated Object Identifier",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x06\x02\x00"
			     count: 3] objectByParsingASN1DER])

	/* Enumerated */
	TEST(@"Parsing of enumerated",
	    [[[OFData dataWithItems: "\x0A\x00"
			     count: 2] objectByParsingASN1DER] longLongValue] ==
	    0 &&
	    [[[OFData dataWithItems: "\x0A\x01\x01"
			     count: 3] objectByParsingASN1DER] longLongValue] ==
	    1 &&
	    [[[OFData dataWithItems: "\x0A\x02\x01\x04"
			     count: 4] objectByParsingASN1DER] longLongValue] ==
	    260 &&
	    [[[OFData dataWithItems: "\x0A\x01\xFF"
			     count: 3] objectByParsingASN1DER] longLongValue] ==
	    -1 &&
	    [[[OFData dataWithItems: "\x0A\x03\xFF\x00\x00"
			     count: 5] objectByParsingASN1DER] longLongValue] ==
	    -65536 &&
	    (unsigned long long)[[[OFData dataWithItems: "\x0A\x09\x00\xFF\xFF"
							 "\xFF\xFF\xFF\xFF\xFF"
							 "\xFF"
						  count: 11]
	    objectByParsingASN1DER] longLongValue] == ULLONG_MAX)

	EXPECT_EXCEPTION(@"Detection of invalid enumerated #1",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x0A\x02\x00\x00"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid enumerated #2",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x0A\x02\x00\x7F"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid enumerated #3",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x0A\x02\xFF\x80"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of out of range enumerated",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x0A\x09\x01"
				    "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
			     count: 11] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated enumerated",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x0A\x02\x00"
			     count: 3] objectByParsingASN1DER])

	/* UTF-8 string */
	TEST(@"Parsing of UTF-8 string",
	    [[[[OFData dataWithItems: "\x0C\x0EHällo Wörld!"
			       count: 16] objectByParsingASN1DER]
	    UTF8StringValue] isEqual: @"Hällo Wörld!"] &&
	    [[[[OFData dataWithItems: "\x0C\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxx"
			       count: 131] objectByParsingASN1DER]
	    UTF8StringValue] isEqual: @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      @"xxxxxxxxxxx"])

	EXPECT_EXCEPTION(@"Detection of out of range UTF-8 string",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x0C\x89"
				    "\x01\x01\x01\x01\x01\x01\x01\x01\x01"
			     count: 11] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated UTF-8 string",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x0C\x01"
			     count: 2] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated length",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x0C\x83\x01\x01"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid / inefficient length #1",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x0C\x81\x7F"
			     count: 3] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of invalid / inefficient length #2",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x0C\x82\x00\x80xxxxxxxxxxxxxxxxxxxxxxxxxx"
				    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				    "xxxxxxxxxxxxxxxxxx"
			     count: 132] objectByParsingASN1DER])

	/* Sequence */
	TEST(@"Parsing of sequence",
	    (array = [[OFData dataWithItems: "\x30\x00"
				      count: 2] objectByParsingASN1DER]) &&
	    [array isKindOfClass: [OFArray class]] && array.count == 0 &&
	    (array = [[OFData dataWithItems: "\x30\x09\x02\x01\x7B\x0C\x04Test"
				      count: 11] objectByParsingASN1DER]) &&
	    [array isKindOfClass: [OFArray class]] && array.count == 2 &&
	    [[array objectAtIndex: 0] longLongValue] == 123 &&
	    [[[array objectAtIndex: 1] stringValue] isEqual: @"Test"])

	EXPECT_EXCEPTION(@"Detection of truncated sequence #1",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x30\x01"
			     count: 2] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated sequence #2",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x30\x04\x02\x01\x01\x00\x00"
			     count: 7] objectByParsingASN1DER])

	/* Set */
	TEST(@"Parsing of set",
	    (set = [[OFData dataWithItems: "\x31\x00"
				    count: 2] objectByParsingASN1DER]) &&
	    [set isKindOfClass: [OFSet class]] && set.count == 0 &&
	    (set = [[OFData dataWithItems: "\x31\x09\x02\x01\x7B\x0C\x04Test"
				    count: 11] objectByParsingASN1DER]) &&
	    [set isKindOfClass: [OFSet class]] && set.count == 2 &&
	    (enumerator = [set objectEnumerator]) &&
	    [[enumerator nextObject] longLongValue] == 123 &&
	    [[[enumerator nextObject] stringValue] isEqual: @"Test"])

	EXPECT_EXCEPTION(@"Detection of invalid set",
	    OFInvalidFormatException,
	    [[OFData dataWithItems: "\x31\x06\x02\x01\x02\x02\x01\x01"
			     count: 8] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated set #1",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x31\x01"
			     count: 2] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated set #2",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x31\x04\x02\x01\x01\x00\x00"
			     count: 7] objectByParsingASN1DER])

	/* NumericString */
	TEST(@"Parsing of NumericString",
	    [[[[OFData dataWithItems: "\x12\x0B" "12345 67890"
			       count: 13] objectByParsingASN1DER]
	    numericStringValue] isEqual: @"12345 67890"] &&
	    [[[[OFData dataWithItems: "\x12\x81\x80" "0000000000000000000000000"
				      "0000000000000000000000000000000000000000"
				      "0000000000000000000000000000000000000000"
				      "00000000000000000000000"
			       count: 131] objectByParsingASN1DER]
	    numericStringValue] isEqual: @"000000000000000000000000000000000000"
					 @"000000000000000000000000000000000000"
					 @"000000000000000000000000000000000000"
					 @"00000000000000000000"])

	EXPECT_EXCEPTION(@"Detection of invalid NumericString",
	    OFInvalidEncodingException,
	    [[OFData dataWithItems: "\x12\x02."
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of out of range NumericString",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x12\x89"
				    "\x01\x01\x01\x01\x01\x01\x01\x01\x01"
			     count: 11] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated NumericString",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x12\x01"
			     count: 2] objectByParsingASN1DER])

	/* PrintableString */
	TEST(@"Parsing of PrintableString",
	    [[[[OFData dataWithItems: "\x13\x0CHello World."
			       count: 14] objectByParsingASN1DER]
	    printableStringValue] isEqual: @"Hello World."] &&
	    [[[[OFData dataWithItems: "\x13\x81\x80 '()+,-./:=?abcdefghijklmnop"
				      "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ '()"
				      "+,-./:=?abcdefghijklmnopqrstuvwxyzABCDEF"
				      "GHIJKLMNOPQRSTUVWXYZ"
			       count: 131] objectByParsingASN1DER]
	    printableStringValue] isEqual: @" '()+,-./:=?abcdefghijklmnopqrstuv"
					   @"wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ '()"
					   @"+,-./:=?abcdefghijklmnopqrstuvwxyz"
					   @"ABCDEFGHIJKLMNOPQRSTUVWXYZ"])

	EXPECT_EXCEPTION(@"Detection of invalid PrintableString",
	    OFInvalidEncodingException,
	    [[OFData dataWithItems: "\x13\x02;"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of out of range PrintableString",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x13\x89"
				    "\x01\x01\x01\x01\x01\x01\x01\x01\x01"
			     count: 11] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated PrintableString",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x13\x01"
			     count: 2] objectByParsingASN1DER])

	/* IA5String */
	TEST(@"Parsing of IA5String",
	    [[[[OFData dataWithItems: "\x16\x0CHello World!"
			       count: 14] objectByParsingASN1DER]
	    IA5StringValue] isEqual: @"Hello World!"] &&
	    [[[[OFData dataWithItems: "\x16\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				      "xxxxxxxxxxxxxxxxxxxx"
			       count: 131] objectByParsingASN1DER]
	    IA5StringValue] isEqual: @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				     @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				     @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
				     @"xxxxxxxx"])

	EXPECT_EXCEPTION(@"Detection of invalid IA5String",
	    OFInvalidEncodingException,
	    [[OFData dataWithItems: "\x16\x02ä"
			     count: 4] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of out of range IA5String",
	    OFOutOfRangeException,
	    [[OFData dataWithItems: "\x16\x89"
				    "\x01\x01\x01\x01\x01\x01\x01\x01\x01"
			     count: 11] objectByParsingASN1DER])

	EXPECT_EXCEPTION(@"Detection of truncated IA5String",
	    OFTruncatedDataException,
	    [[OFData dataWithItems: "\x16\x01"
			     count: 2] objectByParsingASN1DER])

	objc_autoreleasePoolPop(pool);
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted tests/OFASN1DERRepresentationTests.m version [6efb5a6e9d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module;

@implementation TestsAppDelegate (OFASN1DERRepresentationTests)
- (void)ASN1DERRepresentationTests
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data;

	module = @"OFASN1BitString";
	TEST(@"-[ASN1DERRepresentation]",
	    (data = [OFData dataWithItems: "\xFF\x00\xF8"
				    count: 3]) &&
	    [[[OFASN1BitString bitStringWithBitStringValue: data
					   bitStringLength: 21]
	    ASN1DERRepresentation] isEqual:
	    [OFData dataWithItems: "\x03\x04\x03\xFF\x00\xF8"
			    count: 6]] &&
	    (data = [OFData dataWithItems: "abcdefäöü"
				    count: 12]) &&
	    [[[OFASN1BitString bitStringWithBitStringValue: data
					   bitStringLength: 12 * 8]
	    ASN1DERRepresentation] isEqual:
	    [OFData dataWithItems: "\x03\x0D\x00" "abcdefäöü"
			    count: 15]] &&
	    (data = [OFData dataWithItems: ""
				    count: 0]) &&
	    [[[OFASN1BitString bitStringWithBitStringValue: data
					   bitStringLength: 0]
	    ASN1DERRepresentation] isEqual:
	    [OFData dataWithItems: "\x03\x01\x00"
			    count: 3]])

	module = @"OFASN1Boolean";
	TEST(@"-[ASN1DERRepresentation]",
	    [[[OFASN1Boolean booleanWithBooleanValue: false]
	    ASN1DERRepresentation] isEqual:
	    [OFData dataWithItems: "\x01\x01\x00"
			    count: 3]] &&
	    [[[OFASN1Boolean booleanWithBooleanValue: true]
	    ASN1DERRepresentation] isEqual:
	    [OFData dataWithItems: "\x01\x01\xFF"
			    count: 3]])

	module = @"OFNull";
	TEST(@"-[OFASN1DERRepresentation]",
	    [[[OFNull null] ASN1DERRepresentation] isEqual:
	    [OFData dataWithItems: "\x05\x00"
			    count: 2]])

	objc_autoreleasePoolPop(pool);
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































Modified tests/OFArrayTests.m from [650205073d] to [b0ef1c947a].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = nil;
static OFString *c_ary[] = {
	@"Foo",
	@"Bar",
	@"Baz"
};

@interface SimpleArray: OFArray
{

<
<
|

















|
|







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *module;
static OFString *const cArray[] = {
	@"Foo",
	@"Bar",
	@"Baz"
};

@interface SimpleArray: OFArray
{
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
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)object
		     arguments: (va_list)arguments
{
	self = [super init];

	@try {
		_array = [[OFMutableArray alloc] initWithObject: object
						      arguments: arguments];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObjects: (id const *)objects
			  count: (size_t)count
{
	self = [super init];

	@try {
		_array = [[OFMutableArray alloc] initWithObjects: objects
							   count: count];
	} @catch (id e) {







|
<














|
<







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
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)object arguments: (va_list)arguments

{
	self = [super init];

	@try {
		_array = [[OFMutableArray alloc] initWithObject: object
						      arguments: arguments];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObjects: (id const *)objects count: (size_t)count

{
	self = [super init];

	@try {
		_array = [[OFMutableArray alloc] initWithObjects: objects
							   count: count];
	} @catch (id e) {
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
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
@implementation SimpleMutableArray
+ (void)initialize
{
	if (self == [SimpleMutableArray class])
		[self inheritMethodsFromClass: [SimpleArray class]];
}

- (void)insertObject: (id)object
	     atIndex: (size_t)idx
{
	[_array insertObject: object
		     atIndex: idx];
}

- (void)replaceObjectAtIndex: (size_t)idx
		  withObject: (id)object
{
	[_array replaceObjectAtIndex: idx
			  withObject: object];
}

- (void)removeObjectAtIndex: (size_t)idx
{
	[_array removeObjectAtIndex: idx];
}
@end

@implementation TestsAppDelegate (OFArrayTests)
- (void)arrayTestsWithClass: (Class)arrayClass
	       mutableClass: (Class)mutableArrayClass
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *a[3];
	OFMutableArray *m[2];
	OFEnumerator *enumerator;
	id obj;
	bool ok;
	size_t i;

	TEST(@"+[array]", (m[0] = [mutableArrayClass array]))

	TEST(@"+[arrayWithObjects:]",

	    (a[0] = [arrayClass arrayWithObjects: @"Foo", @"Bar", @"Baz", nil]))

	TEST(@"+[arrayWithObjects:count:]",
	    (a[1] = [arrayClass arrayWithObjects: c_ary
					   count: 3]) &&
	    [a[1] isEqual: a[0]])

	TEST(@"-[description]",
	    [a[0].description isEqual: @"(\n\tFoo,\n\tBar,\n\tBaz\n)"])

	TEST(@"-[addObject:]", R([m[0] addObject: c_ary[0]]) &&

	    R([m[0] addObject: c_ary[2]]))

	TEST(@"-[insertObject:atIndex:]", R([m[0] insertObject: c_ary[1]
						       atIndex: 1]))

	TEST(@"-[count]", m[0].count == 3 && a[0].count == 3 && a[1].count == 3)


	TEST(@"-[isEqual:]", [m[0] isEqual: a[0]] && [a[0] isEqual: a[1]])


	TEST(@"-[objectAtIndex:]",
	    [[m[0] objectAtIndex: 0] isEqual: c_ary[0]] &&
	    [[m[0] objectAtIndex: 1] isEqual: c_ary[1]] &&
	    [[m[0] objectAtIndex: 2] isEqual: c_ary[2]] &&
	    [[a[0] objectAtIndex: 0] isEqual: c_ary[0]] &&
	    [[a[0] objectAtIndex: 1] isEqual: c_ary[1]] &&
	    [[a[0] objectAtIndex: 2] isEqual: c_ary[2]] &&
	    [[a[1] objectAtIndex: 0] isEqual: c_ary[0]] &&
	    [[a[1] objectAtIndex: 1] isEqual: c_ary[1]] &&
	    [[a[1] objectAtIndex: 2] isEqual: c_ary[2]])

	TEST(@"-[containsObject:]",
	    [a[0] containsObject: c_ary[1]] &&
	    ![a[0] containsObject: @"nonexistent"])

	TEST(@"-[containsObjectIdenticalTo:]",
	    [a[0] containsObjectIdenticalTo: c_ary[1]] &&
	    ![a[0] containsObjectIdenticalTo:
	    [OFString stringWithString: c_ary[1]]])

	TEST(@"-[indexOfObject:]", [a[0] indexOfObject: c_ary[1]] == 1)

	TEST(@"-[indexOfObjectIdenticalTo:]",
	    [a[1] indexOfObjectIdenticalTo: c_ary[1]] == 1)

	TEST(@"-[objectsInRange:]",
	    [[a[0] objectsInRange: of_range(1, 2)] isEqual:
	    [arrayClass arrayWithObjects: c_ary[1], c_ary[2], nil]])

	TEST(@"-[replaceObject:withObject:]",
	    R([m[0] replaceObject: c_ary[1]
		       withObject: c_ary[0]]) &&
	    [[m[0] objectAtIndex: 0] isEqual: c_ary[0]] &&
	    [[m[0] objectAtIndex: 1] isEqual: c_ary[0]] &&
	    [[m[0] objectAtIndex: 2] isEqual: c_ary[2]])

	TEST(@"-[replaceObject:identicalTo:]",
	    R([m[0] replaceObjectIdenticalTo: c_ary[0]
				  withObject: c_ary[1]]) &&
	    [[m[0] objectAtIndex: 0] isEqual: c_ary[1]] &&
	    [[m[0] objectAtIndex: 1] isEqual: c_ary[0]] &&
	    [[m[0] objectAtIndex: 2] isEqual: c_ary[2]])

	TEST(@"-[replaceObjectAtIndex:withObject:]",
	    R([m[0] replaceObjectAtIndex: 0
			      withObject: c_ary[0]]) &&
	    [[m[0] objectAtIndex: 0] isEqual: c_ary[0]] &&
	    [[m[0] objectAtIndex: 1] isEqual: c_ary[0]] &&
	    [[m[0] objectAtIndex: 2] isEqual: c_ary[2]])

	TEST(@"-[removeObject:]",
	    R([m[0] removeObject: c_ary[0]]) && m[0].count == 2)




	TEST(@"-[removeObjectIdenticalTo:]",
	    R([m[0] removeObjectIdenticalTo: c_ary[2]]) && m[0].count == 1)


	m[1] = [[a[0] mutableCopy] autorelease];
	TEST(@"-[removeObjectAtIndex:]", R([m[1] removeObjectAtIndex: 1]) &&


	    m[1].count == 2 && [[m[1] objectAtIndex: 1] isEqual: c_ary[2]])

	m[1] = [[a[0] mutableCopy] autorelease];
	TEST(@"-[removeObjectsInRange:]",
	    R([m[1] removeObjectsInRange: of_range(0, 2)]) &&

	    m[1].count == 1 && [[m[1] objectAtIndex: 0] isEqual: c_ary[2]])

	m[1] = [[a[0] mutableCopy] autorelease];
	[m[1] addObject: @"qux"];
	[m[1] addObject: @"last"];
	TEST(@"-[reverse]",
	    R([m[1] reverse]) && [m[1] isEqual: [arrayClass arrayWithObjects:
	    @"last", @"qux", @"Baz", @"Bar", @"Foo", nil]])




	m[1] = [[a[0] mutableCopy] autorelease];
	[m[1] addObject: @"qux"];
	[m[1] addObject: @"last"];
	TEST(@"-[reversedArray]",

	    [[m[1] reversedArray] isEqual: [arrayClass arrayWithObjects:
	    @"last", @"qux", @"Baz", @"Bar", @"Foo", nil]])

	m[1] = [[a[0] mutableCopy] autorelease];
	[m[1] addObject: @"0"];
	[m[1] addObject: @"z"];
	TEST(@"-[sortedArray]",
	    [[m[1] sortedArray] isEqual: [arrayClass arrayWithObjects:
	    @"0", @"Bar", @"Baz", @"Foo", @"z", nil]] &&
	    [[m[1] sortedArrayUsingSelector: @selector(compare:)
				    options: OF_ARRAY_SORT_DESCENDING]
	    isEqual: [arrayClass arrayWithObjects:
	    @"z", @"Foo", @"Baz", @"Bar", @"0", nil]])

	EXPECT_EXCEPTION(@"Detect out of range in -[objectAtIndex:]",
	    OFOutOfRangeException, [a[0] objectAtIndex: a[0].count])

	EXPECT_EXCEPTION(@"Detect out of range in -[removeObjectsInRange:]",
	    OFOutOfRangeException, [m[0] removeObjectsInRange:
		of_range(0, m[0].count + 1)])

	TEST(@"-[componentsJoinedByString:]",
	    (a[1] = [arrayClass arrayWithObjects: @"", @"a", @"b", @"c",
	    nil]) &&
	    [[a[1] componentsJoinedByString: @" "] isEqual: @" a b c"] &&
	    (a[1] = [arrayClass arrayWithObject: @"foo"]) &&
	    [[a[1] componentsJoinedByString: @" "] isEqual: @"foo"])

	TEST(@"-[componentsJoinedByString:options]",
	    (a[1] = [arrayClass arrayWithObjects: @"", @"foo", @"", @"", @"bar",

	    @"", nil]) && [[a[1] componentsJoinedByString: @" "
						  options: OF_ARRAY_SKIP_EMPTY]
	    isEqual: @"foo bar"])

	m[0] = [[a[0] mutableCopy] autorelease];
	ok = true;
	i = 0;

	TEST(@"-[objectEnumerator]", (enumerator = [m[0] objectEnumerator]))


	while ((obj = [enumerator nextObject]) != nil) {
		if (![obj isEqual: c_ary[i]])
			ok = false;
		[m[0] replaceObjectAtIndex: i
				withObject: @""];
		i++;
	}

	if (m[0].count != i)
		ok = false;

	TEST(@"OFEnumerator's -[nextObject]", ok)

	[m[0] removeObjectAtIndex: 0];

	EXPECT_EXCEPTION(@"Detection of mutation during enumeration",
	    OFEnumerationMutationException, [enumerator nextObject])

	m[0] = [[a[0] mutableCopy] autorelease];
	ok = true;
	i = 0;

	for (OFString *s in m[0]) {
		if (![s isEqual: c_ary[i]])
			ok = false;
		[m[0] replaceObjectAtIndex: i
				withObject: @""];
		i++;
	}

	if (m[0].count != i)
		ok = false;

	TEST(@"Fast Enumeration", ok)

	[m[0] replaceObjectAtIndex: 0
			withObject: c_ary[0]];
	[m[0] replaceObjectAtIndex: 1
			withObject: c_ary[1]];
	[m[0] replaceObjectAtIndex: 2
			withObject: c_ary[2]];

	ok = false;
	i = 0;
	@try {
		for (OFString *s in m[0]) {
			(void)s;

			if (i == 0)
				[m[0] addObject: @""];

			i++;
		}
	} @catch (OFEnumerationMutationException *e) {
		ok = true;
	}

	TEST(@"Detection of mutation during Fast Enumeration", ok)

	[m[0] removeLastObject];

#ifdef OF_HAVE_BLOCKS
	{
		__block bool blockOk = true;
		__block size_t count = 0;
		OFArray *cmp = a[0];
		OFMutableArray *a2;

		m[0] = [[a[0] mutableCopy] autorelease];
		[m[0] enumerateObjectsUsingBlock:
		    ^ (id object, size_t idx, bool *stop) {
			    count++;

			    if (![object isEqual: [cmp objectAtIndex: idx]])
				    blockOk = false;
		}];

		if (count != cmp.count)
			blockOk = false;

		TEST(@"Enumeration using blocks", blockOk)

		blockOk = false;
		a2 = m[0];
		@try {
			[a2 enumerateObjectsUsingBlock:
			    ^ (id object, size_t idx, bool *stop) {
				[a2 removeObjectAtIndex: idx];
			}];
		} @catch (OFEnumerationMutationException *e) {
			blockOk = true;
		} @catch (OFOutOfRangeException *e) {
			/*
			 * Out of bounds access due to enumeration not being
			 * detected.
			 */
		}

		TEST(@"Detection of mutation during enumeration using blocks",
		    blockOk)
	}

	TEST(@"-[replaceObjectsUsingBlock:]",
	    R([m[0] replaceObjectsUsingBlock: ^ id (id object, size_t idx) {

		switch (idx) {
		case 0:
			return @"foo";
		case 1:
			return @"bar";
		}

		return nil;
	    }]) && [m[0].description isEqual: @"(\n\tfoo,\n\tbar\n)"])

	TEST(@"-[mappedArrayUsingBlock:]",
	    [[m[0] mappedArrayUsingBlock: ^ id (id object, size_t idx) {

		switch (idx) {
		case 0:
			return @"foobar";
		case 1:
			return @"qux";
		}

		return nil;
	    }].description isEqual: @"(\n\tfoobar,\n\tqux\n)"])

	TEST(@"-[filteredArrayUsingBlock:]",
	    [[m[0] filteredArrayUsingBlock: ^ bool (id object, size_t idx) {

		return [object isEqual: @"foo"];
	    }].description isEqual: @"(\n\tfoo\n)"])

	TEST(@"-[foldUsingBlock:]",
	    [[arrayClass arrayWithObjects: [OFMutableString string], @"foo",
	    @"bar", @"baz", nil] foldUsingBlock: ^ id (id left, id right) {
		[left appendString: right];
		return left;
	    }])
#endif

	TEST(@"-[valueForKey:]",
	    [[[arrayClass arrayWithObjects: @"foo", @"bar", @"quxqux", nil]
	    valueForKey: @"length"] isEqual:
	    [arrayClass arrayWithObjects: [OFNumber numberWithInt: 3],
	    [OFNumber numberWithInt: 3], [OFNumber numberWithInt: 6], nil]] &&
	    [[[arrayClass arrayWithObjects: @"1", @"2", nil]
	    valueForKey: @"@count"] isEqual: [OFNumber numberWithInt: 2]])

	m[0] = [mutableArrayClass arrayWithObjects:
	    [OFMutableURL URLWithString: @"http://foo.bar/"],
	    [OFMutableURL URLWithString: @"http://bar.qux/"],
	    [OFMutableURL URLWithString: @"http://qux.quxqux/"], nil];
	TEST(@"-[setValue:forKey:]",
	    R([m[0] setValue: [OFNumber numberWithShort: 1234]
		      forKey: @"port"]) &&
	    [m[0] isEqual: [arrayClass arrayWithObjects:
	    [OFURL URLWithString: @"http://foo.bar:1234/"],
	    [OFURL URLWithString: @"http://bar.qux:1234/"],
	    [OFURL URLWithString: @"http://qux.quxqux:1234/"], nil]])

	objc_autoreleasePoolPop(pool);
}








|
<

|
<


|
<

|
<













|
|

|



|


>
|


|
<
|


|

|
>
|

|
|

|
>

|
>


|
|
|
|
|
|
|
|
|


|
|


|
|
|

|


|


|
|


|
<
|
|
|


|
|
|
|
|


|
<
|
|
|


|
>
>
>


|
>

|
|
>
>
|

|

|
>
|

|
|
|

<
<
>
>
>

|
|
|

>
|


|
|
|

|

|
|




|


|
|


|

|
|
|


|
>
|
|


|



|
>

|
|

|
<



|




|




|



|
|

|
<



|




|
<
|
<
|
<




|
|


|









|



|

|
|

|
|
|
|
>
|
|


|
|

|

|
|

|
|
|


|








|



|
>
|
|
|
|
|
|

|
|


|
>
|
|
|
|
|
|

|



|
>
|





|
|











|




|
|
|







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
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
@implementation SimpleMutableArray
+ (void)initialize
{
	if (self == [SimpleMutableArray class])
		[self inheritMethodsFromClass: [SimpleArray class]];
}

- (void)insertObject: (id)object atIndex: (size_t)idx

{
	[_array insertObject: object atIndex: idx];

}

- (void)replaceObjectAtIndex: (size_t)idx withObject: (id)object

{
	[_array replaceObjectAtIndex: idx withObject: object];

}

- (void)removeObjectAtIndex: (size_t)idx
{
	[_array removeObjectAtIndex: idx];
}
@end

@implementation TestsAppDelegate (OFArrayTests)
- (void)arrayTestsWithClass: (Class)arrayClass
	       mutableClass: (Class)mutableArrayClass
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *array1, *array2;
	OFMutableArray *mutableArray1, *mutableArray2;
	OFEnumerator *enumerator;
	id object;
	bool ok;
	size_t i;

	TEST(@"+[array]", (mutableArray1 = [mutableArrayClass array]))

	TEST(@"+[arrayWithObjects:]",
	    (array1 =
	    [arrayClass arrayWithObjects: @"Foo", @"Bar", @"Baz", nil]))

	TEST(@"+[arrayWithObjects:count:]",
	    (array2 = [arrayClass arrayWithObjects: cArray count: 3]) &&

	    [array2 isEqual: array1])

	TEST(@"-[description]",
	    [array1.description isEqual: @"(\n\tFoo,\n\tBar,\n\tBaz\n)"])

	TEST(@"-[addObject:]",
	    R([mutableArray1 addObject: cArray[0]]) &&
	    R([mutableArray1 addObject: cArray[2]]))

	TEST(@"-[insertObject:atIndex:]",
	    R([mutableArray1 insertObject: cArray[1] atIndex: 1]))

	TEST(@"-[count]",
	    mutableArray1.count == 3 && array1.count == 3 && array2.count == 3)

	TEST(@"-[isEqual:]",
	    [mutableArray1 isEqual: array1] && [array1 isEqual: array2])

	TEST(@"-[objectAtIndex:]",
	    [[mutableArray1 objectAtIndex: 0] isEqual: cArray[0]] &&
	    [[mutableArray1 objectAtIndex: 1] isEqual: cArray[1]] &&
	    [[mutableArray1 objectAtIndex: 2] isEqual: cArray[2]] &&
	    [[array1 objectAtIndex: 0] isEqual: cArray[0]] &&
	    [[array1 objectAtIndex: 1] isEqual: cArray[1]] &&
	    [[array1 objectAtIndex: 2] isEqual: cArray[2]] &&
	    [[array2 objectAtIndex: 0] isEqual: cArray[0]] &&
	    [[array2 objectAtIndex: 1] isEqual: cArray[1]] &&
	    [[array2 objectAtIndex: 2] isEqual: cArray[2]])

	TEST(@"-[containsObject:]",
	    [array1 containsObject: cArray[1]] &&
	    ![array1 containsObject: @"nonexistent"])

	TEST(@"-[containsObjectIdenticalTo:]",
	    [array1 containsObjectIdenticalTo: cArray[1]] &&
	    ![array1 containsObjectIdenticalTo:
	    [OFString stringWithString: cArray[1]]])

	TEST(@"-[indexOfObject:]", [array1 indexOfObject: cArray[1]] == 1)

	TEST(@"-[indexOfObjectIdenticalTo:]",
	    [array2 indexOfObjectIdenticalTo: cArray[1]] == 1)

	TEST(@"-[objectsInRange:]",
	    [[array1 objectsInRange: OFRangeMake(1, 2)] isEqual:
	    [arrayClass arrayWithObjects: cArray[1], cArray[2], nil]])

	TEST(@"-[replaceObject:withObject:]",
	    R([mutableArray1 replaceObject: cArray[1] withObject: cArray[0]]) &&

	    [[mutableArray1 objectAtIndex: 0] isEqual: cArray[0]] &&
	    [[mutableArray1 objectAtIndex: 1] isEqual: cArray[0]] &&
	    [[mutableArray1 objectAtIndex: 2] isEqual: cArray[2]])

	TEST(@"-[replaceObject:identicalTo:]",
	    R([mutableArray1 replaceObjectIdenticalTo: cArray[0]
					   withObject: cArray[1]]) &&
	    [[mutableArray1 objectAtIndex: 0] isEqual: cArray[1]] &&
	    [[mutableArray1 objectAtIndex: 1] isEqual: cArray[0]] &&
	    [[mutableArray1 objectAtIndex: 2] isEqual: cArray[2]])

	TEST(@"-[replaceObjectAtIndex:withObject:]",
	    R([mutableArray1 replaceObjectAtIndex: 0 withObject: cArray[0]]) &&

	    [[mutableArray1 objectAtIndex: 0] isEqual: cArray[0]] &&
	    [[mutableArray1 objectAtIndex: 1] isEqual: cArray[0]] &&
	    [[mutableArray1 objectAtIndex: 2] isEqual: cArray[2]])

	TEST(@"-[removeObject:]",
	    R([mutableArray1 removeObject: cArray[0]]) &&
	    mutableArray1.count == 1)

	[mutableArray1 addObject: cArray[0]];

	TEST(@"-[removeObjectIdenticalTo:]",
	    R([mutableArray1 removeObjectIdenticalTo: cArray[2]]) &&
	    mutableArray1.count == 1)

	mutableArray2 = [[array1 mutableCopy] autorelease];
	TEST(@"-[removeObjectAtIndex:]",
	    R([mutableArray2 removeObjectAtIndex: 1]) &&
	    mutableArray2.count == 2 &&
	    [[mutableArray2 objectAtIndex: 1] isEqual: cArray[2]])

	mutableArray2 = [[array1 mutableCopy] autorelease];
	TEST(@"-[removeObjectsInRange:]",
	    R([mutableArray2 removeObjectsInRange: OFRangeMake(0, 2)]) &&
	    mutableArray2.count == 1 &&
	    [[mutableArray2 objectAtIndex: 0] isEqual: cArray[2]])

	mutableArray2 = [[array1 mutableCopy] autorelease];
	[mutableArray2 addObject: @"qux"];
	[mutableArray2 addObject: @"last"];
	TEST(@"-[reverse]",


	    R([mutableArray2 reverse]) &&
	    [mutableArray2 isEqual: [arrayClass arrayWithObjects:
	    @"last", @"qux", @"Baz", @"Bar", @"Foo", nil]])

	mutableArray2 = [[array1 mutableCopy] autorelease];
	[mutableArray2 addObject: @"qux"];
	[mutableArray2 addObject: @"last"];
	TEST(@"-[reversedArray]",
	    [[mutableArray2 reversedArray] isEqual:
	    [arrayClass arrayWithObjects:
	    @"last", @"qux", @"Baz", @"Bar", @"Foo", nil]])

	mutableArray2 = [[array1 mutableCopy] autorelease];
	[mutableArray2 addObject: @"0"];
	[mutableArray2 addObject: @"z"];
	TEST(@"-[sortedArray]",
	    [[mutableArray2 sortedArray] isEqual: [arrayClass arrayWithObjects:
	    @"0", @"Bar", @"Baz", @"Foo", @"z", nil]] &&
	    [[mutableArray2 sortedArrayUsingSelector: @selector(compare:)
					     options: OFArraySortDescending]
	    isEqual: [arrayClass arrayWithObjects:
	    @"z", @"Foo", @"Baz", @"Bar", @"0", nil]])

	EXPECT_EXCEPTION(@"Detect out of range in -[objectAtIndex:]",
	    OFOutOfRangeException, [array1 objectAtIndex: array1.count])

	EXPECT_EXCEPTION(@"Detect out of range in -[removeObjectsInRange:]",
	    OFOutOfRangeException, [mutableArray1 removeObjectsInRange:
		OFRangeMake(0, mutableArray1.count + 1)])

	TEST(@"-[componentsJoinedByString:]",
	    (array2 = [arrayClass arrayWithObjects: @"", @"a", @"b", @"c",
	    nil]) &&
	    [[array2 componentsJoinedByString: @" "] isEqual: @" a b c"] &&
	    (array2 = [arrayClass arrayWithObject: @"foo"]) &&
	    [[array2 componentsJoinedByString: @" "] isEqual: @"foo"])

	TEST(@"-[componentsJoinedByString:options]",
	    (array2 = [arrayClass arrayWithObjects: @"", @"foo", @"", @"",
	    @"bar", @"", nil]) &&
	    [[array2 componentsJoinedByString: @" "
				      options: OFArraySkipEmptyComponents]
	    isEqual: @"foo bar"])

	mutableArray1 = [[array1 mutableCopy] autorelease];
	ok = true;
	i = 0;

	TEST(@"-[objectEnumerator]",
	    (enumerator = [mutableArray1 objectEnumerator]))

	while ((object = [enumerator nextObject]) != nil) {
		if (![object isEqual: cArray[i]])
			ok = false;
		[mutableArray1 replaceObjectAtIndex: i withObject: @""];

		i++;
	}

	if (mutableArray1.count != i)
		ok = false;

	TEST(@"OFEnumerator's -[nextObject]", ok)

	[mutableArray1 removeObjectAtIndex: 0];

	EXPECT_EXCEPTION(@"Detection of mutation during enumeration",
	    OFEnumerationMutationException, [enumerator nextObject])

	mutableArray1 = [[array1 mutableCopy] autorelease];
	ok = true;
	i = 0;

	for (OFString *string in mutableArray1) {
		if (![string isEqual: cArray[i]])
			ok = false;
		[mutableArray1 replaceObjectAtIndex: i withObject: @""];

		i++;
	}

	if (mutableArray1.count != i)
		ok = false;

	TEST(@"Fast Enumeration", ok)

	[mutableArray1 replaceObjectAtIndex: 0 withObject: cArray[0]];

	[mutableArray1 replaceObjectAtIndex: 1 withObject: cArray[1]];

	[mutableArray1 replaceObjectAtIndex: 2 withObject: cArray[2]];


	ok = false;
	i = 0;
	@try {
		for (OFString *string in mutableArray1) {
			(void)string;

			if (i == 0)
				[mutableArray1 addObject: @""];

			i++;
		}
	} @catch (OFEnumerationMutationException *e) {
		ok = true;
	}

	TEST(@"Detection of mutation during Fast Enumeration", ok)

	[mutableArray1 removeLastObject];

#ifdef OF_HAVE_BLOCKS
	{
		__block bool blockOK = true;
		__block size_t count = 0;
		OFArray *compareArray = array1;
		OFMutableArray *mutableArray3;

		mutableArray1 = [[array1 mutableCopy] autorelease];
		[mutableArray1 enumerateObjectsUsingBlock:
		    ^ (id object_, size_t idx, bool *stop) {
			count++;
			if (![object_ isEqual:
			    [compareArray objectAtIndex: idx]])
				blockOK = false;
		}];

		if (count != compareArray.count)
			blockOK = false;

		TEST(@"Enumeration using blocks", blockOK)

		blockOK = false;
		mutableArray3 = mutableArray1;
		@try {
			[mutableArray3 enumerateObjectsUsingBlock:
			    ^ (id object_, size_t idx, bool *stop) {
				[mutableArray3 removeObjectAtIndex: idx];
			}];
		} @catch (OFEnumerationMutationException *e) {
			blockOK = true;
		} @catch (OFOutOfRangeException *e) {
			/*
			 * Out of bounds access due to enumeration not being
			 * detected.
			 */
		}

		TEST(@"Detection of mutation during enumeration using blocks",
		    blockOK)
	}

	TEST(@"-[replaceObjectsUsingBlock:]",
	    R([mutableArray1 replaceObjectsUsingBlock:
		^ id (id object_, size_t idx) {
		    switch (idx) {
		    case 0:
			    return @"foo";
		    case 1:
			    return @"bar";
		    }

		    return nil;
	    }]) && [mutableArray1.description isEqual: @"(\n\tfoo,\n\tbar\n)"])

	TEST(@"-[mappedArrayUsingBlock:]",
	    [[mutableArray1 mappedArrayUsingBlock:
		^ id (id object_, size_t idx) {
		    switch (idx) {
		    case 0:
			    return @"foobar";
		    case 1:
			    return @"qux";
		    }

		    return nil;
	    }].description isEqual: @"(\n\tfoobar,\n\tqux\n)"])

	TEST(@"-[filteredArrayUsingBlock:]",
	    [[mutableArray1 filteredArrayUsingBlock:
		^ bool (id object_, size_t idx) {
		    return [object_ isEqual: @"foo"];
	    }].description isEqual: @"(\n\tfoo\n)"])

	TEST(@"-[foldUsingBlock:]",
	    [[arrayClass arrayWithObjects: [OFMutableString string], @"foo",
	    @"bar", @"baz", nil] foldUsingBlock: ^ id (id left, id right) {
		    [left appendString: right];
		    return left;
	    }])
#endif

	TEST(@"-[valueForKey:]",
	    [[[arrayClass arrayWithObjects: @"foo", @"bar", @"quxqux", nil]
	    valueForKey: @"length"] isEqual:
	    [arrayClass arrayWithObjects: [OFNumber numberWithInt: 3],
	    [OFNumber numberWithInt: 3], [OFNumber numberWithInt: 6], nil]] &&
	    [[[arrayClass arrayWithObjects: @"1", @"2", nil]
	    valueForKey: @"@count"] isEqual: [OFNumber numberWithInt: 2]])

	mutableArray1 = [mutableArrayClass arrayWithObjects:
	    [OFMutableURL URLWithString: @"http://foo.bar/"],
	    [OFMutableURL URLWithString: @"http://bar.qux/"],
	    [OFMutableURL URLWithString: @"http://qux.quxqux/"], nil];
	TEST(@"-[setValue:forKey:]",
	    R([mutableArray1 setValue: [OFNumber numberWithShort: 1234]
			       forKey: @"port"]) &&
	    [mutableArray1 isEqual: [arrayClass arrayWithObjects:
	    [OFURL URLWithString: @"http://foo.bar:1234/"],
	    [OFURL URLWithString: @"http://bar.qux:1234/"],
	    [OFURL URLWithString: @"http://qux.quxqux:1234/"], nil]])

	objc_autoreleasePoolPop(pool);
}

Modified tests/OFBlockTests.m from [0c40afc8f7] to [d2cc5b8a58].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFBlock";

#if defined(OF_OBJFW_RUNTIME)
extern struct objc_class _NSConcreteStackBlock;
extern struct objc_class _NSConcreteGlobalBlock;
extern struct objc_class _NSConcreteMallocBlock;
#elif defined(OF_APPLE_RUNTIME)
extern void *_NSConcreteStackBlock;
extern void *_NSConcreteGlobalBlock;
extern void *_NSConcreteMallocBlock;
#endif

static void (^g)(void) = ^ {};

static int
(^returnStackBlock(void))(void)
{
	__block int i = 42;

	return [Block_copy(^ int { return ++i; }) autorelease];
}

static double
forwardTest(void)
{
	__block double d;
	void (^b)(void) = Block_copy(^ {
		d = 5;
	});

	b();
	Block_release(b);

	return d;
}

@implementation TestsAppDelegate (OFBlockTests)
- (void)blockTests
{
	void *pool = objc_autoreleasePoolPush();
	__block int x;
	void (^s)(void) = ^ { x = 0; };

	void (^m)(void);

	int (^v)(void);


	TEST(@"Class of stack block",
	    (Class)&_NSConcreteStackBlock == objc_getClass("OFStackBlock") &&
	    [s isKindOfClass: [OFBlock class]])

#if !defined(OF_WINDOWS) || !defined(__clang__) || !defined(OF_NO_SHARED)
	/*
	 * Causes a linker error on Windows with Clang when compiling as a
	 * static library. This is a bug in Clang.
	 */
	TEST(@"Class of global block",
	    (Class)&_NSConcreteGlobalBlock == objc_getClass("OFGlobalBlock") &&
	    [g isKindOfClass: [OFBlock class]])
#endif

	TEST(@"Class of a malloc block",
	    (Class)&_NSConcreteMallocBlock == objc_getClass("OFMallocBlock"))

	TEST(@"Copying a stack block",
	    (m = [[s copy] autorelease]) &&
	    [m class] == objc_getClass("OFMallocBlock") &&
	    [m isKindOfClass: [OFBlock class]])

	TEST(@"Copying a stack block and referencing its variable",
	    forwardTest() == 5)

	TEST(@"Copying a stack block and using its copied variable",
	    (v = returnStackBlock()) && v() == 43 && v() == 44 && v() == 45)


	TEST(@"Copying a global block", (id)g == [[g copy] autorelease])


#ifndef __clang_analyzer__
	TEST(@"Copying a malloc block",

	    (id)m == [m copy] && [m retainCount] == 2)
#endif

	TEST(@"Autorelease a stack block", R([s autorelease]))

	TEST(@"Autorelease a global block", R([g autorelease]))

#ifndef __clang_analyzer__
	TEST(@"Autorelease a malloc block", R([m autorelease]))
#endif

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|











|













|



|
|









|
>
|
>
|
>



|








|






|
|
|





|
>

|
>



>
|


|

|


|





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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFBlock";

#if defined(OF_OBJFW_RUNTIME)
extern struct objc_class _NSConcreteStackBlock;
extern struct objc_class _NSConcreteGlobalBlock;
extern struct objc_class _NSConcreteMallocBlock;
#elif defined(OF_APPLE_RUNTIME)
extern void *_NSConcreteStackBlock;
extern void *_NSConcreteGlobalBlock;
extern void *_NSConcreteMallocBlock;
#endif

static void (^globalBlock)(void) = ^ {};

static int
(^returnStackBlock(void))(void)
{
	__block int i = 42;

	return [Block_copy(^ int { return ++i; }) autorelease];
}

static double
forwardTest(void)
{
	__block double d;
	void (^block)(void) = Block_copy(^ {
		d = 5;
	});

	block();
	Block_release(block);

	return d;
}

@implementation TestsAppDelegate (OFBlockTests)
- (void)blockTests
{
	void *pool = objc_autoreleasePoolPush();
	__block int x;
	void (^stackBlock)(void) = ^ {
		x = 0;
		(void)x;
	};
	void (^mallocBlock)(void);
	int (^voidBlock)(void);

	TEST(@"Class of stack block",
	    (Class)&_NSConcreteStackBlock == objc_getClass("OFStackBlock") &&
	    [stackBlock isKindOfClass: [OFBlock class]])

#if !defined(OF_WINDOWS) || !defined(__clang__) || !defined(OF_NO_SHARED)
	/*
	 * Causes a linker error on Windows with Clang when compiling as a
	 * static library. This is a bug in Clang.
	 */
	TEST(@"Class of global block",
	    (Class)&_NSConcreteGlobalBlock == objc_getClass("OFGlobalBlock") &&
	    [globalBlock isKindOfClass: [OFBlock class]])
#endif

	TEST(@"Class of a malloc block",
	    (Class)&_NSConcreteMallocBlock == objc_getClass("OFMallocBlock"))

	TEST(@"Copying a stack block",
	    (mallocBlock = [[stackBlock copy] autorelease]) &&
	    [mallocBlock class] == objc_getClass("OFMallocBlock") &&
	    [mallocBlock isKindOfClass: [OFBlock class]])

	TEST(@"Copying a stack block and referencing its variable",
	    forwardTest() == 5)

	TEST(@"Copying a stack block and using its copied variable",
	    (voidBlock = returnStackBlock()) && voidBlock() == 43 &&
	    voidBlock() == 44 && voidBlock() == 45)

	TEST(@"Copying a global block",
	    (id)globalBlock == [[globalBlock copy] autorelease])

#ifndef __clang_analyzer__
	TEST(@"Copying a malloc block",
	    (id)mallocBlock == [mallocBlock copy] &&
	    [mallocBlock retainCount] == 2)
#endif

	TEST(@"Autorelease a stack block", R([stackBlock autorelease]))

	TEST(@"Autorelease a global block", R([globalBlock autorelease]))

#ifndef __clang_analyzer__
	TEST(@"Autorelease a malloc block", R([mallocBlock autorelease]))
#endif

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFCharacterSetTests.m from [b475a2bd21] to [9f65e98d38].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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

#import "TestsAppDelegate.h"

#import "OFCharacterSet.h"
#import "OFBitSetCharacterSet.h"
#import "OFRangeCharacterSet.h"

static OFString *module = nil;

@interface SimpleCharacterSet: OFCharacterSet
@end

@implementation SimpleCharacterSet
- (bool)characterIsMember: (of_unichar_t)character
{
	return (character % 2 == 0);
}
@end

@implementation TestsAppDelegate (OFCharacterSetTests)
- (void)characterSetTests
{
	void *pool = objc_autoreleasePoolPush();
	OFCharacterSet *cs, *ics;
	bool ok;

	module = @"OFCharacterSet";

	cs = [[[SimpleCharacterSet alloc] init] autorelease];

	ok = true;
	for (of_unichar_t c = 0; c < 65536; c++) {
		if (c % 2 == 0) {
			if (![cs characterIsMember: c])
				ok = false;
		} else if ([cs characterIsMember: c])
			ok = false;
	}
	TEST(@"-[characterIsMember:]", ok);

	module = @"OFBitSetCharacterSet";

	TEST(@"+[characterSetWithCharactersInString:]",
	    (cs = [OFCharacterSet characterSetWithCharactersInString:
	    @"0123456789"]) &&
	    [cs isKindOfClass: [OFBitSetCharacterSet class]])

	ok = true;
	for (of_unichar_t c = 0; c < 65536; c++) {
		if (c >= '0' && c <= '9') {
			if (![cs characterIsMember: c])
				ok = false;
		} else if ([cs characterIsMember: c])
			ok = false;
	}
	TEST(@"-[characterIsMember:]", ok);

	module = @"OFRangeCharacterSet";

	TEST(@"+[characterSetWithRange:]",

	    (cs = [OFCharacterSet characterSetWithRange: of_range('0', 10)]) &&
	    [cs isKindOfClass: [OFRangeCharacterSet class]])

	ok = true;
	for (of_unichar_t c = 0; c < 65536; c++) {
		if (c >= '0' && c <= '9') {
			if (![cs characterIsMember: c])
				ok = false;
		} else if ([cs characterIsMember: c])
			ok = false;
	}
	TEST(@"-[characterIsMember:]", ok);

	ok = true;
	ics = cs.invertedSet;
	for (of_unichar_t c = 0; c < 65536; c++) {
		if (c >= '0' && c <= '9') {
			if ([ics characterIsMember: c])
				ok = false;
		} else if (![ics characterIsMember: c])
			ok = false;
	}
	TEST(@"-[invertedSet]", ok);

	TEST(@"Inverting -[invertedSet] returns original set",
	    ics.invertedSet == cs)

	objc_autoreleasePoolPop(pool);
}
@end







|





|









|




|


|

|

|







|

|


|

|

|







>
|
|


|

|

|





|
|

|

|





|




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

#import "TestsAppDelegate.h"

#import "OFCharacterSet.h"
#import "OFBitSetCharacterSet.h"
#import "OFRangeCharacterSet.h"

static OFString *module;

@interface SimpleCharacterSet: OFCharacterSet
@end

@implementation SimpleCharacterSet
- (bool)characterIsMember: (OFUnichar)character
{
	return (character % 2 == 0);
}
@end

@implementation TestsAppDelegate (OFCharacterSetTests)
- (void)characterSetTests
{
	void *pool = objc_autoreleasePoolPush();
	OFCharacterSet *characterSet, *invertedCharacterSet;
	bool ok;

	module = @"OFCharacterSet";

	characterSet = [[[SimpleCharacterSet alloc] init] autorelease];

	ok = true;
	for (OFUnichar c = 0; c < 65536; c++) {
		if (c % 2 == 0) {
			if (![characterSet characterIsMember: c])
				ok = false;
		} else if ([characterSet characterIsMember: c])
			ok = false;
	}
	TEST(@"-[characterIsMember:]", ok);

	module = @"OFBitSetCharacterSet";

	TEST(@"+[characterSetWithCharactersInString:]",
	    (characterSet = [OFCharacterSet characterSetWithCharactersInString:
	    @"0123456789"]) &&
	    [characterSet isKindOfClass: [OFBitSetCharacterSet class]])

	ok = true;
	for (OFUnichar c = 0; c < 65536; c++) {
		if (c >= '0' && c <= '9') {
			if (![characterSet characterIsMember: c])
				ok = false;
		} else if ([characterSet characterIsMember: c])
			ok = false;
	}
	TEST(@"-[characterIsMember:]", ok);

	module = @"OFRangeCharacterSet";

	TEST(@"+[characterSetWithRange:]",
	    (characterSet = [OFCharacterSet
	    characterSetWithRange: OFRangeMake('0', 10)]) &&
	    [characterSet isKindOfClass: [OFRangeCharacterSet class]])

	ok = true;
	for (OFUnichar c = 0; c < 65536; c++) {
		if (c >= '0' && c <= '9') {
			if (![characterSet characterIsMember: c])
				ok = false;
		} else if ([characterSet characterIsMember: c])
			ok = false;
	}
	TEST(@"-[characterIsMember:]", ok);

	ok = true;
	invertedCharacterSet = characterSet.invertedSet;
	for (OFUnichar c = 0; c < 65536; c++) {
		if (c >= '0' && c <= '9') {
			if ([invertedCharacterSet characterIsMember: c])
				ok = false;
		} else if (![invertedCharacterSet characterIsMember: c])
			ok = false;
	}
	TEST(@"-[invertedSet]", ok);

	TEST(@"Inverting -[invertedSet] returns original set",
	    invertedCharacterSet.invertedSet == characterSet)

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFDNSResolverTests.m from [e970066231] to [b7997ffc05].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@implementation TestsAppDelegate (OFDNSResolverTests)
- (void)DNSResolverTests
{
	void *pool = objc_autoreleasePoolPush();
	OFDNSResolver *resolver = [OFDNSResolver resolver];
	OFMutableString *staticHosts = [OFMutableString string];

	[of_stdout setForegroundColor: [OFColor lime]];

	for (OFString *host in resolver.staticHosts) {
		OFString *IPs;

		if (staticHosts.length > 0)
			[staticHosts appendString: @"; "];

		IPs = [[resolver.staticHosts objectForKey: host]
		    componentsJoinedByString: @", "];

		[staticHosts appendFormat: @"%@=(%@)", host, IPs];
	}
	[of_stdout writeFormat: @"[OFDNSResolver] Static hosts: %@\n",
	    staticHosts];

	[of_stdout writeFormat: @"[OFDNSResolver] Name servers: %@\n",
	    [resolver.nameServers componentsJoinedByString: @", "]];

	[of_stdout writeFormat: @"[OFDNSResolver] Local domain: %@\n",
	    resolver.localDomain];

	[of_stdout writeFormat: @"[OFDNSResolver] Search domains: %@\n",
	    [resolver.searchDomains componentsJoinedByString: @", "]];

	[of_stdout writeFormat: @"[OFDNSResolver] Timeout: %lf\n",
	    resolver.timeout];

	[of_stdout writeFormat: @"[OFDNSResolver] Max attempts: %u\n",
	    resolver.maxAttempts];

	[of_stdout writeFormat:
	    @"[OFDNSResolver] Min number of dots in absolute name: %u\n",
	    resolver.minNumberOfDotsInAbsoluteName];

	[of_stdout writeFormat: @"[OFDNSResolver] Uses TCP: %u\n",
	    resolver.usesTCP];

	[of_stdout writeFormat:
	    @"[OFDNSResolver] Config reload interval: %lf\n",
	    resolver.configReloadInterval];

	objc_autoreleasePoolPop(pool);
}
@end







|












|


|


|


|


|


|


|



|


|






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
@implementation TestsAppDelegate (OFDNSResolverTests)
- (void)DNSResolverTests
{
	void *pool = objc_autoreleasePoolPush();
	OFDNSResolver *resolver = [OFDNSResolver resolver];
	OFMutableString *staticHosts = [OFMutableString string];

	[OFStdOut setForegroundColor: [OFColor lime]];

	for (OFString *host in resolver.staticHosts) {
		OFString *IPs;

		if (staticHosts.length > 0)
			[staticHosts appendString: @"; "];

		IPs = [[resolver.staticHosts objectForKey: host]
		    componentsJoinedByString: @", "];

		[staticHosts appendFormat: @"%@=(%@)", host, IPs];
	}
	[OFStdOut writeFormat: @"[OFDNSResolver] Static hosts: %@\n",
	    staticHosts];

	[OFStdOut writeFormat: @"[OFDNSResolver] Name servers: %@\n",
	    [resolver.nameServers componentsJoinedByString: @", "]];

	[OFStdOut writeFormat: @"[OFDNSResolver] Local domain: %@\n",
	    resolver.localDomain];

	[OFStdOut writeFormat: @"[OFDNSResolver] Search domains: %@\n",
	    [resolver.searchDomains componentsJoinedByString: @", "]];

	[OFStdOut writeFormat: @"[OFDNSResolver] Timeout: %lf\n",
	    resolver.timeout];

	[OFStdOut writeFormat: @"[OFDNSResolver] Max attempts: %u\n",
	    resolver.maxAttempts];

	[OFStdOut writeFormat:
	    @"[OFDNSResolver] Min number of dots in absolute name: %u\n",
	    resolver.minNumberOfDotsInAbsoluteName];

	[OFStdOut writeFormat: @"[OFDNSResolver] Uses TCP: %u\n",
	    resolver.usesTCP];

	[OFStdOut writeFormat:
	    @"[OFDNSResolver] Config reload interval: %lf\n",
	    resolver.configReloadInterval];

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFDataTests.m from [ec82a4e0aa] to [cc1d29fd59].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFData";
const char *str = "Hello!";

@implementation TestsAppDelegate (OFDataTests)
- (void)dataTests
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *mutable;
	OFData *immutable;
	void *raw[2];
	of_range_t range;

	TEST(@"+[dataWithItemSize:]",
	    (mutable = [OFMutableData dataWithItemSize: 4096]))

	OFObject *tmp = [[[OFObject alloc] init] autorelease];
	raw[0] = [tmp allocMemoryWithSize: 4096];
	raw[1] = [tmp allocMemoryWithSize: 4096];
	memset(raw[0], 0xFF, 4096);
	memset(raw[1], 0x42, 4096);

	TEST(@"-[addItem:]", R([mutable addItem: raw[0]]) &&
	    R([mutable addItem: raw[1]]))

	TEST(@"-[itemAtIndex:]",
	    memcmp([mutable itemAtIndex: 0], raw[0], 4096) == 0 &&
	    memcmp([mutable itemAtIndex: 1], raw[1], 4096) == 0)

	TEST(@"-[lastItem]", memcmp(mutable.lastItem, raw[1], 4096) == 0)

	TEST(@"-[count]", mutable.count == 2)

	TEST(@"-[isEqual:]",
	    (immutable = [OFData dataWithItems: mutable.items
				      itemSize: mutable.itemSize
					 count: mutable.count]) &&

	    [immutable isEqual: mutable] &&
	    R([mutable removeLastItem]) && ![mutable isEqual: immutable])

	TEST(@"-[mutableCopy]",
	    (mutable = [[immutable mutableCopy] autorelease]) &&
	    [mutable isEqual: immutable])

	TEST(@"-[compare]", [mutable compare: immutable] == 0 &&
	    R([mutable removeLastItem]) &&
	    [immutable compare: mutable] == OF_ORDERED_DESCENDING &&
	    [mutable compare: immutable] == OF_ORDERED_ASCENDING &&
	    [[OFData dataWithItems: "aa"
			     count: 2] compare:
	    [OFData dataWithItems: "z"
			    count: 1]] == OF_ORDERED_ASCENDING)

	TEST(@"-[hash]", immutable.hash == 0x634A529F)

	mutable = [OFMutableData dataWithItems: "abcdef"
					 count: 6];

	TEST(@"-[removeLastItem]", R([mutable removeLastItem]) &&

	    mutable.count == 5 && memcmp(mutable.items, "abcde", 5) == 0)

	TEST(@"-[removeItemsInRange:]",
	    R([mutable removeItemsInRange: of_range(1, 2)]) &&
	    mutable.count == 3 && memcmp(mutable.items, "ade", 3) == 0)

	TEST(@"-[insertItems:atIndex:count:]",
	    R([mutable insertItems: "bc"
			   atIndex: 1
			     count: 2]) && mutable.count == 5 &&
	    memcmp(mutable.items, "abcde", 5) == 0)

	immutable = [OFData dataWithItems: "aaabaccdacaabb"
				 itemSize: 2

				    count: 7];
	TEST(@"-[rangeOfString:options:range:]",
	    R(range = [immutable rangeOfData: [OFData dataWithItems: "aa"
							   itemSize: 2
							      count: 1]
				     options: 0
				       range: of_range(0, 7)]) &&

	    range.location == 0 && range.length == 1 &&

	    R(range = [immutable rangeOfData: [OFData dataWithItems: "aa"

							   itemSize: 2






							      count: 1]

				     options: OF_DATA_SEARCH_BACKWARDS
				       range: of_range(0, 7)]) &&

	    range.location == 5 && range.length == 1 &&

	    R(range = [immutable rangeOfData: [OFData dataWithItems: "ac"
							   itemSize: 2
							      count: 1]
				     options: 0
				       range: of_range(0, 7)]) &&
	    range.location == 2 && range.length == 1 &&
	    R(range = [immutable rangeOfData: [OFData dataWithItems: "aabb"
							   itemSize: 2
							      count: 2]
				     options: 0
				       range: of_range(0, 7)]) &&

	    range.location == 5 && range.length == 2 &&


	    R(range = [immutable rangeOfData: [OFData dataWithItems: "aa"
							   itemSize: 2
							      count: 1]

				     options: 0
				       range: of_range(1, 6)]) &&
	    range.location == 5 && range.length == 1 &&

	    R(range = [immutable rangeOfData: [OFData dataWithItems: "aa"
							   itemSize: 2
							      count: 1]

				     options: OF_DATA_SEARCH_BACKWARDS
				       range: of_range(0, 5)]) &&

	    range.location == 0 && range.length == 1)

	EXPECT_EXCEPTION(
	    @"-[rangeOfString:options:range:] failing on different itemSize",
	    OFInvalidArgumentException,
	    [immutable rangeOfData: [OFData dataWithItems: "aaa"
						 itemSize: 3
						    count: 1]

			   options: 0
			     range: of_range(0, 1)])

	EXPECT_EXCEPTION(
	    @"-[rangeOfData:options:range:] failing on out of range",
	    OFOutOfRangeException,
	    [immutable rangeOfData: [OFData dataWithItems: ""
						 itemSize: 2
						    count: 0]
			   options: 0
			     range: of_range(8, 1)])

	TEST(@"-[subdataWithRange:]",
	    [[immutable subdataWithRange: of_range(2, 4)]
	    isEqual: [OFData dataWithItems: "accdacaa"
				  itemSize: 2
				     count: 4]] &&
	    [[mutable subdataWithRange: of_range(2, 3)]
	    isEqual: [OFData dataWithItems: "cde"
				     count: 3]])

	EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #1",
	    OFOutOfRangeException, [immutable subdataWithRange: of_range(7, 1)])


	EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #2",
	    OFOutOfRangeException, [mutable subdataWithRange: of_range(6, 1)])




	TEST(@"-[MD5Hash]", [mutable.MD5Hash isEqual: @"abcde".MD5Hash])

	TEST(@"-[RIPEMD160Hash]", [mutable.RIPEMD160Hash
	    isEqual: @"abcde".RIPEMD160Hash])


	TEST(@"-[SHA1Hash]", [mutable.SHA1Hash isEqual: @"abcde".SHA1Hash])



	TEST(@"-[SHA224Hash]", [mutable.SHA224Hash
	    isEqual: @"abcde".SHA224Hash])



	TEST(@"-[SHA256Hash]", [mutable.SHA256Hash
	    isEqual: @"abcde".SHA256Hash])



	TEST(@"-[SHA384Hash]", [mutable.SHA384Hash
	    isEqual: @"abcde".SHA384Hash])



	TEST(@"-[SHA512Hash]", [mutable.SHA512Hash
	    isEqual: @"abcde".SHA512Hash])




	TEST(@"-[stringByBase64Encoding]",
	    [mutable.stringByBase64Encoding isEqual: @"YWJjZGU="])

	TEST(@"+[dataWithBase64EncodedString:]",
	    memcmp([[OFData dataWithBase64EncodedString: @"YWJjZGU="]
	    items], "abcde", 5) == 0)

	TEST(@"Building strings",
	    (mutable = [OFMutableData dataWithItems: str
					       count: 6]) &&
	    R([mutable addItem: ""]) &&
	    strcmp(mutable.items, str) == 0)

	EXPECT_EXCEPTION(@"Detect out of range in -[itemAtIndex:]",
	    OFOutOfRangeException, [mutable itemAtIndex: mutable.count])

	EXPECT_EXCEPTION(@"Detect out of range in -[addItems:count:]",
	    OFOutOfRangeException, [mutable addItems: raw[0]
					       count: SIZE_MAX])

	EXPECT_EXCEPTION(@"Detect out of range in -[removeItemsInRange:]",
	    OFOutOfRangeException,
	    [mutable removeItemsInRange: of_range(mutable.count, 1)])




	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|
<





|
|

|


|

<
|
|



|
|


|
|

|

|


|
<
|
>
|
|


|
|

|
|
|
|
|
<
|
<

|

|
<

|
>
|


|
|


|
<
|
|

|
|
>
|
<
<
|
<
|
|
>
|
>
|
>
|
>
>
>
>
>
>
|
>
|
|
>
|
>
|
<
|
<
<
<
<
|
<
|
|
>
|
>
>
|
<
|
>
|
|
|
>
|
<
|
>
|
|
>



|

|
<
|
>
|
|




|
<
<
|
|


|
|
<
<
|
|
<


|
>


|
>

>
>
|

|
|
>

|
>
>

|
|
>
>

|
|
>
>

|
|
>
>

|
|
>
>
>


|


|
|


|
<
|
|


|


|
|



|
>
>
>




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFData";


@implementation TestsAppDelegate (OFDataTests)
- (void)dataTests
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *mutableData;
	OFData *data;
	void *raw[2];
	OFRange range;

	TEST(@"+[dataWithItemSize:]",
	    (mutableData = [OFMutableData dataWithItemSize: 4096]))


	raw[0] = OFAllocMemory(1, 4096);
	raw[1] = OFAllocMemory(1, 4096);
	memset(raw[0], 0xFF, 4096);
	memset(raw[1], 0x42, 4096);

	TEST(@"-[addItem:]", R([mutableData addItem: raw[0]]) &&
	    R([mutableData addItem: raw[1]]))

	TEST(@"-[itemAtIndex:]",
	    memcmp([mutableData itemAtIndex: 0], raw[0], 4096) == 0 &&
	    memcmp([mutableData itemAtIndex: 1], raw[1], 4096) == 0)

	TEST(@"-[lastItem]", memcmp(mutableData.lastItem, raw[1], 4096) == 0)

	TEST(@"-[count]", mutableData.count == 2)

	TEST(@"-[isEqual:]",
	    (data = [OFData dataWithItems: mutableData.items

				    count: mutableData.count
				 itemSize: mutableData.itemSize]) &&
	    [data isEqual: mutableData] &&
	    R([mutableData removeLastItem]) && ![mutableData isEqual: data])

	TEST(@"-[mutableCopy]",
	    (mutableData = [[data mutableCopy] autorelease]) &&
	    [mutableData isEqual: data])

	TEST(@"-[compare]", [mutableData compare: data] == 0 &&
	    R([mutableData removeLastItem]) &&
	    [data compare: mutableData] == OFOrderedDescending &&
	    [mutableData compare: data] == OFOrderedAscending &&
	    [[OFData dataWithItems: "aa" count: 2] compare:

	    [OFData dataWithItems: "z" count: 1]] == OFOrderedAscending)


	TEST(@"-[hash]", data.hash == 0x634A529F)

	mutableData = [OFMutableData dataWithItems: "abcdef" count: 6];


	TEST(@"-[removeLastItem]",
	    R([mutableData removeLastItem]) && mutableData.count == 5 &&
	    memcmp(mutableData.items, "abcde", 5) == 0)

	TEST(@"-[removeItemsInRange:]",
	    R([mutableData removeItemsInRange: OFRangeMake(1, 2)]) &&
	    mutableData.count == 3 && memcmp(mutableData.items, "ade", 3) == 0)

	TEST(@"-[insertItems:atIndex:count:]",
	    R([mutableData insertItems: "bc" atIndex: 1 count: 2]) &&

	    mutableData.count == 5 &&
	    memcmp(mutableData.items, "abcde", 5) == 0)

	data = [OFData dataWithItems: "aaabaccdacaabb" count: 7 itemSize: 2];

	range = [data rangeOfData: [OFData dataWithItems: "aa"
						   count: 1


						itemSize: 2]

			  options: 0
			    range: OFRangeMake(0, 7)];
	TEST(@"-[rangeOfData:options:range:] #1",
	    range.location == 0 && range.length == 1)

	range = [data rangeOfData: [OFData dataWithItems: "aa"
						   count: 1
						itemSize: 2]
			  options: OFDataSearchBackwards
			    range: OFRangeMake(0, 7)];
	TEST(@"-[rangeOfData:options:range:] #2",
	    range.location == 5 && range.length == 1)

	range = [data rangeOfData: [OFData dataWithItems: "ac"
						   count: 1
						itemSize: 2]
			  options: 0
			    range: OFRangeMake(0, 7)];
	TEST(@"-[rangeOfData:options:range:] #3",
	    range.location == 2 && range.length == 1)

	range = [data rangeOfData: [OFData dataWithItems: "aabb"

						   count: 2




						itemSize: 2]

			  options: 0
			    range: OFRangeMake(0, 7)];
	TEST(@"-[rangeOfData:options:range:] #4",
	    range.location == 5 && range.length == 2)

	TEST(@"-[rangeOfData:options:range:] #5",
	    R(range = [data rangeOfData: [OFData dataWithItems: "aa"

							 count: 1
						      itemSize: 2]
				options: 0
				  range: OFRangeMake(1, 6)]) &&
	    range.location == 5 && range.length == 1)

	range = [data rangeOfData: [OFData dataWithItems: "aa"

						   count: 1
						itemSize: 2]
			  options: OFDataSearchBackwards
			    range: OFRangeMake(0, 5)];
	TEST(@"-[rangeOfData:options:range:] #6",
	    range.location == 0 && range.length == 1)

	EXPECT_EXCEPTION(
	    @"-[rangeOfData:options:range:] failing on different itemSize",
	    OFInvalidArgumentException,
	    [data rangeOfData: [OFData dataWithItems: "aaa"

					       count: 1
					    itemSize: 3]
		      options: 0
			range: OFRangeMake(0, 1)])

	EXPECT_EXCEPTION(
	    @"-[rangeOfData:options:range:] failing on out of range",
	    OFOutOfRangeException,
	    [data rangeOfData: [OFData dataWithItems: "" count: 0 itemSize: 2]


		      options: 0
			range: OFRangeMake(8, 1)])

	TEST(@"-[subdataWithRange:]",
	    [[data subdataWithRange: OFRangeMake(2, 4)]
	    isEqual: [OFData dataWithItems: "accdacaa" count: 4 itemSize: 2]] &&


	    [[mutableData subdataWithRange: OFRangeMake(2, 3)]
	    isEqual: [OFData dataWithItems: "cde" count: 3]])


	EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #1",
	    OFOutOfRangeException,
	    [data subdataWithRange: OFRangeMake(7, 1)])

	EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #2",
	    OFOutOfRangeException,
	    [mutableData subdataWithRange: OFRangeMake(6, 1)])

	TEST(@"-[stringByMD5Hashing]",
	    [mutableData.stringByMD5Hashing
	    isEqual: @"ab56b4d92b40713acc5af89985d4b786"])

	TEST(@"-[stringByRIPEMD160Hashing]",
	    [mutableData.stringByRIPEMD160Hashing
	    isEqual: @"973398b6e6c6cfa6b5e6a5173f195ce3274bf828"])

	TEST(@"-[stringBySHA1Hashing]",
	    [mutableData.stringBySHA1Hashing
	    isEqual: @"03de6c570bfe24bfc328ccd7ca46b76eadaf4334"])

	TEST(@"-[stringBySHA224Hashing]",
	    [mutableData.stringBySHA224Hashing
	    isEqual: @"bdd03d560993e675516ba5a50638b6531ac2ac3d5847c61916cfced6"
	    ])

	TEST(@"-[stringBySHA256Hashing]",
	    [mutableData.stringBySHA256Hashing
	    isEqual: @"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0"
		     @"c44ca42c"])

	TEST(@"-[stringBySHA384Hashing]",
	    [mutableData.stringBySHA384Hashing
	    isEqual: @"4c525cbeac729eaf4b4665815bc5db0c84fe6300068a727cf74e2813"
		     @"521565abc0ec57a37ee4d8be89d097c0d2ad52f0"])

	TEST(@"-[stringBySHA512Hashing]",
	    [mutableData.stringBySHA512Hashing
	    isEqual: @"878ae65a92e86cac011a570d4c30a7eaec442b85ce8eca0c2952b5e3"
		     @"cc0628c2e79d889ad4d5c7c626986d452dd86374b6ffaa7cd8b67665"
		     @"bef2289a5c70b0a1"])

	TEST(@"-[stringByBase64Encoding]",
	    [mutableData.stringByBase64Encoding isEqual: @"YWJjZGU="])

	TEST(@"+[dataWithBase64EncodedString:]",
	    memcmp([[OFData dataWithBase64EncodedString: @"YWJjZGU="] items],
	    "abcde", 5) == 0)

	TEST(@"Building strings",
	    (mutableData = [OFMutableData dataWithItems: "Hello!" count: 6]) &&

	    R([mutableData addItem: ""]) &&
	    strcmp(mutableData.items, "Hello!") == 0)

	EXPECT_EXCEPTION(@"Detect out of range in -[itemAtIndex:]",
	    OFOutOfRangeException, [mutableData itemAtIndex: mutableData.count])

	EXPECT_EXCEPTION(@"Detect out of range in -[addItems:count:]",
	    OFOutOfRangeException,
	    [mutableData addItems: raw[0] count: SIZE_MAX])

	EXPECT_EXCEPTION(@"Detect out of range in -[removeItemsInRange:]",
	    OFOutOfRangeException,
	    [mutableData removeItemsInRange: OFRangeMake(mutableData.count, 1)])

	OFFreeMemory(raw[0]);
	OFFreeMemory(raw[1]);

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFDateTests.m from [3f9f2f2e15] to [f110295df2].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <time.h>

#import "TestsAppDelegate.h"


static OFString *module = @"OFDate";

@implementation TestsAppDelegate (OFDateTests)
- (void)dateTests
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *d1, *d2;

	struct tm tm;
	int16_t tz;
	const char *dstr = "Wed, 09 Jun 2021 +0200x";
	TEST(@"of_strptime()",
	    of_strptime(dstr, "%a, %d %b %Y %z", &tm, &tz) == dstr + 22 &&
	    tm.tm_wday == 3 && tm.tm_mday == 9 && tm.tm_mon == 5 &&
	    tm.tm_year == 2021 - 1900 && tz == 2 * 60)

	TEST(@"+[dateWithTimeIntervalSince1970:]",
	    (d1 = [OFDate dateWithTimeIntervalSince1970: 0]))

	TEST(@"-[dateByAddingTimeInterval:]",
	    (d2 = [d1 dateByAddingTimeInterval: 3600 * 25 + 5.000002]))

	TEST(@"-[description]",
	    [d1.description isEqual: @"1970-01-01T00:00:00Z"] &&
	    [d2.description isEqual: @"1970-01-02T01:00:05Z"])

	TEST(@"+[dateWithDateString:format:]",
	    [[[OFDate dateWithDateString: @"2000-06-20T12:34:56+0200"
				  format: @"%Y-%m-%dT%H:%M:%S%z"] description]
	    isEqual: @"2000-06-20T10:34:56Z"]);

	EXPECT_EXCEPTION(@"Detection of unparsed in "

<
<
|


















>

|





|




|
|




|


|


|
|







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
/*


 * 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 <time.h>

#import "TestsAppDelegate.h"
#import "OFStrPTime.h"

static OFString *const module = @"OFDate";

@implementation TestsAppDelegate (OFDateTests)
- (void)dateTests
{
	void *pool = objc_autoreleasePoolPush();
	OFDate *date1, *date2;

	struct tm tm;
	int16_t tz;
	const char *dstr = "Wed, 09 Jun 2021 +0200x";
	TEST(@"OFStrPTime()",
	    OFStrPTime(dstr, "%a, %d %b %Y %z", &tm, &tz) == dstr + 22 &&
	    tm.tm_wday == 3 && tm.tm_mday == 9 && tm.tm_mon == 5 &&
	    tm.tm_year == 2021 - 1900 && tz == 2 * 60)

	TEST(@"+[dateWithTimeIntervalSince1970:]",
	    (date1 = [OFDate dateWithTimeIntervalSince1970: 0]))

	TEST(@"-[dateByAddingTimeInterval:]",
	    (date2 = [date1 dateByAddingTimeInterval: 3600 * 25 + 5.000002]))

	TEST(@"-[description]",
	    [date1.description isEqual: @"1970-01-01T00:00:00Z"] &&
	    [date2.description isEqual: @"1970-01-02T01:00:05Z"])

	TEST(@"+[dateWithDateString:format:]",
	    [[[OFDate dateWithDateString: @"2000-06-20T12:34:56+0200"
				  format: @"%Y-%m-%dT%H:%M:%S%z"] description]
	    isEqual: @"2000-06-20T10:34:56Z"]);

	EXPECT_EXCEPTION(@"Detection of unparsed in "
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

	EXPECT_EXCEPTION(@"Detection of unparsed in "
	    @"+[dateWithLocalDateString:format:] #2", OFInvalidFormatException,
	    [OFDate dateWithLocalDateString: @"2000-06-20T12:34:56+0200x"
				     format: @"%Y-%m-%dT%H:%M:%S%z"])

	TEST(@"-[isEqual:]",
	    [d1 isEqual: [OFDate dateWithTimeIntervalSince1970: 0]] &&
	    ![d1 isEqual: [OFDate dateWithTimeIntervalSince1970: 0.0000001]])

	TEST(@"-[compare:]", [d1 compare: d2] == OF_ORDERED_ASCENDING)

	TEST(@"-[second]", d1.second == 0 && d2.second == 5)

	TEST(@"-[microsecond]", d1.microsecond == 0 && d2.microsecond == 2)


	TEST(@"-[minute]", d1.minute == 0 && d2.minute == 0)

	TEST(@"-[hour]", d1.hour == 0 && d2.hour == 1)

	TEST(@"-[dayOfMonth]", d1.dayOfMonth == 1 && d2.dayOfMonth == 2)

	TEST(@"-[monthOfYear]", d1.monthOfYear == 1 && d2.monthOfYear == 1)


	TEST(@"-[year]", d1.year == 1970 && d2.year == 1970)

	TEST(@"-[dayOfWeek]", d1.dayOfWeek == 4 && d2.dayOfWeek == 5)

	TEST(@"-[dayOfYear]", d1.dayOfYear == 1 && d2.dayOfYear == 2)

	TEST(@"-[earlierDate:]", [[d1 earlierDate: d2] isEqual: d1])

	TEST(@"-[laterDate:]", [[d1 laterDate: d2] isEqual: d2])

	objc_autoreleasePoolPop(pool);
}
@end







|
|

|

|

|
>

|

|

|

|
>

|

|

|

|

|




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

	EXPECT_EXCEPTION(@"Detection of unparsed in "
	    @"+[dateWithLocalDateString:format:] #2", OFInvalidFormatException,
	    [OFDate dateWithLocalDateString: @"2000-06-20T12:34:56+0200x"
				     format: @"%Y-%m-%dT%H:%M:%S%z"])

	TEST(@"-[isEqual:]",
	    [date1 isEqual: [OFDate dateWithTimeIntervalSince1970: 0]] &&
	    ![date1 isEqual: [OFDate dateWithTimeIntervalSince1970: 0.0000001]])

	TEST(@"-[compare:]", [date1 compare: date2] == OFOrderedAscending)

	TEST(@"-[second]", date1.second == 0 && date2.second == 5)

	TEST(@"-[microsecond]",
	    date1.microsecond == 0 && date2.microsecond == 2)

	TEST(@"-[minute]", date1.minute == 0 && date2.minute == 0)

	TEST(@"-[hour]", date1.hour == 0 && date2.hour == 1)

	TEST(@"-[dayOfMonth]", date1.dayOfMonth == 1 && date2.dayOfMonth == 2)

	TEST(@"-[monthOfYear]",
	    date1.monthOfYear == 1 && date2.monthOfYear == 1)

	TEST(@"-[year]", date1.year == 1970 && date2.year == 1970)

	TEST(@"-[dayOfWeek]", date1.dayOfWeek == 4 && date2.dayOfWeek == 5)

	TEST(@"-[dayOfYear]", date1.dayOfYear == 1 && date2.dayOfYear == 2)

	TEST(@"-[earlierDate:]", [[date1 earlierDate: date2] isEqual: date1])

	TEST(@"-[laterDate:]", [[date1 laterDate: date2] isEqual: date2])

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFDictionaryTests.m from [a25483183f] to [7127a0734c].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = nil;
static OFString *keys[] = {
	@"key1",
	@"key2"
};
static OFString *values[] = {
	@"value1",
	@"value2"

<
<
|

















|







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *module;
static OFString *keys[] = {
	@"key1",
	@"key2"
};
static OFString *values[] = {
	@"value1",
	@"value2"
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithKey: (id)key
		  arguments: (va_list)arguments
{
	self = [super init];

	@try {
		_dictionary = [[OFMutableDictionary alloc]
		    initWithKey: key
		      arguments: arguments];







|
<







51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithKey: (id)key arguments: (va_list)arguments

{
	self = [super init];

	@try {
		_dictionary = [[OFMutableDictionary alloc]
		    initWithKey: key
		      arguments: arguments];
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
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
- (id)objectForKey: (id)key
{
	return [_dictionary objectForKey: key];
}

- (size_t)count
{
	return [_dictionary count];
}

- (OFEnumerator *)keyEnumerator
{
	return [_dictionary keyEnumerator];
}
@end

@implementation SimpleMutableDictionary
+ (void)initialize
{
	if (self == [SimpleMutableDictionary class])
		[self inheritMethodsFromClass: [SimpleDictionary class]];
}

- (void)setObject: (id)object
	   forKey: (id)key
{
	bool existed = ([_dictionary objectForKey: key] == nil);

	[_dictionary setObject: object
			forKey: key];

	if (existed)
		_mutations++;
}

- (void)removeObjectForKey: (id)key
{
	bool existed = ([_dictionary objectForKey: key] == nil);

	[_dictionary removeObjectForKey: key];

	if (existed)
		_mutations++;
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	int ret = [super countByEnumeratingWithState: state
					     objects: objects
					       count: count];

	state->mutationsPtr = &_mutations;

	return ret;
}
@end

@implementation TestsAppDelegate (OFDictionaryTests)
- (void)dictionaryTestsWithClass: (Class)dictionaryClass
		    mutableClass: (Class)mutableDictionaryClass
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary *mutDict = [mutableDictionaryClass dictionary];
	OFDictionary *dict;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFArray *keysArray, *valuesArray;

	[mutDict setObject: values[0]
		    forKey: keys[0]];
	[mutDict setValue: values[1]
		   forKey: keys[1]];

	TEST(@"-[objectForKey:]",
	    [[mutDict objectForKey: keys[0]] isEqual: values[0]] &&
	    [[mutDict objectForKey: keys[1]] isEqual: values[1]] &&
	    [mutDict objectForKey: @"key3"] == nil)

	TEST(@"-[valueForKey:]",
	    [[mutDict valueForKey: keys[0]] isEqual: values[0]] &&
	    [[mutDict valueForKey: @"@count"] isEqual:
	    [OFNumber numberWithInt: 2]])

	EXPECT_EXCEPTION(@"Catching -[setValue:forKey:] on immutable "
	    @"dictionary", OFUndefinedKeyException,
	    [[dictionaryClass dictionary] setValue: @"x"
					    forKey: @"x"])

	TEST(@"-[containsObject:]",
	    [mutDict containsObject: values[0]] &&
	    ![mutDict containsObject: @"nonexistent"])

	TEST(@"-[containsObjectIdenticalTo:]",
	    [mutDict containsObjectIdenticalTo: values[0]] &&
	    ![mutDict containsObjectIdenticalTo:
	    [OFString stringWithString: values[0]]])

	TEST(@"-[description]",
	    [[mutDict description] isEqual:
	    @"{\n\tkey1 = value1;\n\tkey2 = value2;\n}"])

	TEST(@"-[allKeys]",
	    [[mutDict allKeys] isEqual: [OFArray arrayWithObjects: keys[0],
	    keys[1], nil]])

	TEST(@"-[allObjects]",
	    [[mutDict allObjects] isEqual: [OFArray arrayWithObjects: values[0],
	    values[1], nil]])

	TEST(@"-[keyEnumerator]", (keyEnumerator = [mutDict keyEnumerator]))
	TEST(@"-[objectEnumerator]",
	    (objectEnumerator = [mutDict objectEnumerator]))

	TEST(@"OFEnumerator's -[nextObject]",
	    [[keyEnumerator nextObject] isEqual: keys[0]] &&
	    [[objectEnumerator nextObject] isEqual: values[0]] &&
	    [[keyEnumerator nextObject] isEqual: keys[1]] &&
	    [[objectEnumerator nextObject] isEqual: values[1]] &&
	    [keyEnumerator nextObject] == nil &&
	    [objectEnumerator nextObject] == nil)

	[mutDict removeObjectForKey: keys[0]];

	EXPECT_EXCEPTION(@"Detection of mutation during enumeration",
	    OFEnumerationMutationException, [keyEnumerator nextObject]);

	[mutDict setObject: values[0]
		    forKey: keys[0]];

	size_t i = 0;
	bool ok = true;

	for (OFString *key in mutDict) {
		if (i > 1 || ![key isEqual: keys[i]]) {
			ok = false;
			break;
		}

		[mutDict setObject: [mutDict objectForKey: key]
			    forKey: key];
		i++;
	}

	TEST(@"Fast Enumeration", ok)

	ok = false;
	@try {
		for (OFString *key in mutDict) {
			(void)key;

			[mutDict setObject: @""
				    forKey: @""];
		}
	} @catch (OFEnumerationMutationException *e) {
		ok = true;
	}

	TEST(@"Detection of mutation during Fast Enumeration", ok)

	[mutDict removeObjectForKey: @""];

	TEST(@"-[stringByURLEncoding]",
	    [[[OFDictionary dictionaryWithKeysAndObjects: @"foo", @"bar",
							  @"q&x", @"q=x", nil]
	    stringByURLEncoding] isEqual: @"q%26x=q%3Dx&foo=bar"])

#ifdef OF_HAVE_BLOCKS
	{
		__block size_t j = 0;
		__block bool blockOk = true;

		[mutDict enumerateKeysAndObjectsUsingBlock:
		    ^ (id key, id object, bool *stop) {
			if (j > 1 || ![key isEqual: keys[j]]) {
				blockOk = false;
				*stop = true;
				return;
			}

			[mutDict setObject: [mutDict objectForKey: key]
				    forKey: key];
			j++;
		}];

		TEST(@"Enumeration using blocks", blockOk)

		blockOk = false;
		@try {
			[mutDict enumerateKeysAndObjectsUsingBlock:
			    ^ (id key, id object, bool *stop) {
				[mutDict setObject: @""
					    forKey: @""];
			}];
		} @catch (OFEnumerationMutationException *e) {
			blockOk = true;
		}

		TEST(@"Detection of mutation during enumeration using blocks",
		    blockOk)

		[mutDict removeObjectForKey: @""];
	}

	TEST(@"-[replaceObjectsUsingBlock:]",
	    R([mutDict replaceObjectsUsingBlock: ^ id (id key, id object) {
		if ([key isEqual: keys[0]])
			return @"value_1";
		if ([key isEqual: keys[1]])
			return @"value_2";

		return nil;
	    }]) && [[mutDict objectForKey: keys[0]] isEqual: @"value_1"] &&
	    [[mutDict objectForKey: keys[1]] isEqual: @"value_2"])

	TEST(@"-[mappedDictionaryUsingBlock:]",
	    [[[mutDict mappedDictionaryUsingBlock: ^ id (id key, id object) {

		if ([key isEqual: keys[0]])
			return @"val1";
		if ([key isEqual: keys[1]])
			return @"val2";

		return nil;
	    }] description] isEqual: @"{\n\tkey1 = val1;\n\tkey2 = val2;\n}"])

	TEST(@"-[filteredDictionaryUsingBlock:]",
	    [[[mutDict filteredDictionaryUsingBlock:
	    ^ bool (id key, id object) {
		return [key isEqual: keys[0]];
	    }] description] isEqual: @"{\n\tkey1 = value_1;\n}"])
#endif

	TEST(@"-[count]", mutDict.count == 2)

	TEST(@"+[dictionaryWithKeysAndObjects:]",
	    (dict = [dictionaryClass dictionaryWithKeysAndObjects:
	    @"foo", @"bar", @"baz", @"qux", nil]) &&
	    [[dict objectForKey: @"foo"] isEqual: @"bar"] &&
	    [[dict objectForKey: @"baz"] isEqual: @"qux"])








|















|
<



|
<















|


















|




|
<
|
<


|
|
|


|
|




|
<


|
|


|
|



|



|
|


|
|

|

|









|




|
<




|





|
|







|

<
|
<







|











|







|
|







|

|
<








|



|
|
|
|
|

|
|
|


|
>
|
|
|
|





|
|
|



|







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
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
- (id)objectForKey: (id)key
{
	return [_dictionary objectForKey: key];
}

- (size_t)count
{
	return _dictionary.count;
}

- (OFEnumerator *)keyEnumerator
{
	return [_dictionary keyEnumerator];
}
@end

@implementation SimpleMutableDictionary
+ (void)initialize
{
	if (self == [SimpleMutableDictionary class])
		[self inheritMethodsFromClass: [SimpleDictionary class]];
}

- (void)setObject: (id)object forKey: (id)key

{
	bool existed = ([_dictionary objectForKey: key] == nil);

	[_dictionary setObject: object forKey: key];


	if (existed)
		_mutations++;
}

- (void)removeObjectForKey: (id)key
{
	bool existed = ([_dictionary objectForKey: key] == nil);

	[_dictionary removeObjectForKey: key];

	if (existed)
		_mutations++;
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	int ret = [super countByEnumeratingWithState: state
					     objects: objects
					       count: count];

	state->mutationsPtr = &_mutations;

	return ret;
}
@end

@implementation TestsAppDelegate (OFDictionaryTests)
- (void)dictionaryTestsWithClass: (Class)dictionaryClass
		    mutableClass: (Class)mutableDictionaryClass
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary *mutableDict = [mutableDictionaryClass dictionary];
	OFDictionary *dict;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFArray *keysArray, *valuesArray;

	[mutableDict setObject: values[0] forKey: keys[0]];

	[mutableDict setValue: values[1] forKey: keys[1]];


	TEST(@"-[objectForKey:]",
	    [[mutableDict objectForKey: keys[0]] isEqual: values[0]] &&
	    [[mutableDict objectForKey: keys[1]] isEqual: values[1]] &&
	    [mutableDict objectForKey: @"key3"] == nil)

	TEST(@"-[valueForKey:]",
	    [[mutableDict valueForKey: keys[0]] isEqual: values[0]] &&
	    [[mutableDict valueForKey: @"@count"] isEqual:
	    [OFNumber numberWithInt: 2]])

	EXPECT_EXCEPTION(@"Catching -[setValue:forKey:] on immutable "
	    @"dictionary", OFUndefinedKeyException,
	    [[dictionaryClass dictionary] setValue: @"x" forKey: @"x"])


	TEST(@"-[containsObject:]",
	    [mutableDict containsObject: values[0]] &&
	    ![mutableDict containsObject: @"nonexistent"])

	TEST(@"-[containsObjectIdenticalTo:]",
	    [mutableDict containsObjectIdenticalTo: values[0]] &&
	    ![mutableDict containsObjectIdenticalTo:
	    [OFString stringWithString: values[0]]])

	TEST(@"-[description]",
	    [[mutableDict description] isEqual:
	    @"{\n\tkey1 = value1;\n\tkey2 = value2;\n}"])

	TEST(@"-[allKeys]",
	    [[mutableDict allKeys] isEqual:
	    [OFArray arrayWithObjects: keys[0], keys[1], nil]])

	TEST(@"-[allObjects]",
	    [[mutableDict allObjects] isEqual:
	    [OFArray arrayWithObjects: values[0], values[1], nil]])

	TEST(@"-[keyEnumerator]", (keyEnumerator = [mutableDict keyEnumerator]))
	TEST(@"-[objectEnumerator]",
	    (objectEnumerator = [mutableDict objectEnumerator]))

	TEST(@"OFEnumerator's -[nextObject]",
	    [[keyEnumerator nextObject] isEqual: keys[0]] &&
	    [[objectEnumerator nextObject] isEqual: values[0]] &&
	    [[keyEnumerator nextObject] isEqual: keys[1]] &&
	    [[objectEnumerator nextObject] isEqual: values[1]] &&
	    [keyEnumerator nextObject] == nil &&
	    [objectEnumerator nextObject] == nil)

	[mutableDict removeObjectForKey: keys[0]];

	EXPECT_EXCEPTION(@"Detection of mutation during enumeration",
	    OFEnumerationMutationException, [keyEnumerator nextObject]);

	[mutableDict setObject: values[0] forKey: keys[0]];


	size_t i = 0;
	bool ok = true;

	for (OFString *key in mutableDict) {
		if (i > 1 || ![key isEqual: keys[i]]) {
			ok = false;
			break;
		}

		[mutableDict setObject: [mutableDict objectForKey: key]
				forKey: key];
		i++;
	}

	TEST(@"Fast Enumeration", ok)

	ok = false;
	@try {
		for (OFString *key in mutableDict) {
			(void)key;

			[mutableDict setObject: @"" forKey: @""];

		}
	} @catch (OFEnumerationMutationException *e) {
		ok = true;
	}

	TEST(@"Detection of mutation during Fast Enumeration", ok)

	[mutableDict removeObjectForKey: @""];

	TEST(@"-[stringByURLEncoding]",
	    [[[OFDictionary dictionaryWithKeysAndObjects: @"foo", @"bar",
							  @"q&x", @"q=x", nil]
	    stringByURLEncoding] isEqual: @"q%26x=q%3Dx&foo=bar"])

#ifdef OF_HAVE_BLOCKS
	{
		__block size_t j = 0;
		__block bool blockOk = true;

		[mutableDict enumerateKeysAndObjectsUsingBlock:
		    ^ (id key, id object, bool *stop) {
			if (j > 1 || ![key isEqual: keys[j]]) {
				blockOk = false;
				*stop = true;
				return;
			}

			[mutableDict setObject: [mutableDict objectForKey: key]
					forKey: key];
			j++;
		}];

		TEST(@"Enumeration using blocks", blockOk)

		blockOk = false;
		@try {
			[mutableDict enumerateKeysAndObjectsUsingBlock:
			    ^ (id key, id object, bool *stop) {
				[mutableDict setObject: @"" forKey: @""];

			}];
		} @catch (OFEnumerationMutationException *e) {
			blockOk = true;
		}

		TEST(@"Detection of mutation during enumeration using blocks",
		    blockOk)

		[mutableDict removeObjectForKey: @""];
	}

	TEST(@"-[replaceObjectsUsingBlock:]",
	    R([mutableDict replaceObjectsUsingBlock: ^ id (id key, id object) {
		    if ([key isEqual: keys[0]])
			    return @"value_1";
		    if ([key isEqual: keys[1]])
			    return @"value_2";

		    return nil;
	    }]) && [[mutableDict objectForKey: keys[0]] isEqual: @"value_1"] &&
	    [[mutableDict objectForKey: keys[1]] isEqual: @"value_2"])

	TEST(@"-[mappedDictionaryUsingBlock:]",
	    [[[mutableDict mappedDictionaryUsingBlock:
		^ id (id key, id object) {
		    if ([key isEqual: keys[0]])
			    return @"val1";
		    if ([key isEqual: keys[1]])
			    return @"val2";

		return nil;
	    }] description] isEqual: @"{\n\tkey1 = val1;\n\tkey2 = val2;\n}"])

	TEST(@"-[filteredDictionaryUsingBlock:]",
	    [[[mutableDict filteredDictionaryUsingBlock:
		^ bool (id key, id object) {
		    return [key isEqual: keys[0]];
	    }] description] isEqual: @"{\n\tkey1 = value_1;\n}"])
#endif

	TEST(@"-[count]", mutableDict.count == 2)

	TEST(@"+[dictionaryWithKeysAndObjects:]",
	    (dict = [dictionaryClass dictionaryWithKeysAndObjects:
	    @"foo", @"bar", @"baz", @"qux", nil]) &&
	    [[dict objectForKey: @"foo"] isEqual: @"bar"] &&
	    [[dict objectForKey: @"baz"] isEqual: @"qux"])

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

	TEST(@"-[copy]",
	    (dict = [[dict copy] autorelease]) &&
	    [[dict objectForKey: keys[0]] isEqual: values[0]] &&
	    [[dict objectForKey: keys[1]] isEqual: values[1]])

	TEST(@"-[mutableCopy]",
	    (mutDict = [[dict mutableCopy] autorelease]) &&
	    mutDict.count == dict.count &&
	    [[mutDict objectForKey: keys[0]] isEqual: values[0]] &&
	    [[mutDict objectForKey: keys[1]] isEqual: values[1]] &&
	    R([mutDict setObject: @"value3"
			  forKey: @"key3"]) &&
	    [[mutDict objectForKey: @"key3"] isEqual: @"value3"] &&
	    [[mutDict objectForKey: keys[0]] isEqual: values[0]] &&
	    R([mutDict setObject: @"foo"
			  forKey: keys[0]]) &&
	    [[mutDict objectForKey: keys[0]] isEqual: @"foo"])

	TEST(@"-[removeObjectForKey:]",
	    R([mutDict removeObjectForKey: keys[0]]) &&
	    [mutDict objectForKey: keys[0]] == nil)

	[mutDict setObject: @"foo"
		    forKey: keys[0]];
	TEST(@"-[isEqual:]", ![mutDict isEqual: dict] &&
	    R([mutDict removeObjectForKey: @"key3"]) &&
	    ![mutDict isEqual: dict] &&
	    R([mutDict setObject: values[0]
			  forKey: keys[0]]) &&
	    [mutDict isEqual: dict])

	objc_autoreleasePoolPop(pool);
}

- (void)dictionaryTests
{
	module = @"OFDictionary";







|
|
|
|
|
<
|
|
|
<
|


|
|

|
<
|
|
|
|
<
|







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

	TEST(@"-[copy]",
	    (dict = [[dict copy] autorelease]) &&
	    [[dict objectForKey: keys[0]] isEqual: values[0]] &&
	    [[dict objectForKey: keys[1]] isEqual: values[1]])

	TEST(@"-[mutableCopy]",
	    (mutableDict = [[dict mutableCopy] autorelease]) &&
	    mutableDict.count == dict.count &&
	    [[mutableDict objectForKey: keys[0]] isEqual: values[0]] &&
	    [[mutableDict objectForKey: keys[1]] isEqual: values[1]] &&
	    R([mutableDict setObject: @"value3" forKey: @"key3"]) &&

	    [[mutableDict objectForKey: @"key3"] isEqual: @"value3"] &&
	    [[mutableDict objectForKey: keys[0]] isEqual: values[0]] &&
	    R([mutableDict setObject: @"foo" forKey: keys[0]]) &&

	    [[mutableDict objectForKey: keys[0]] isEqual: @"foo"])

	TEST(@"-[removeObjectForKey:]",
	    R([mutableDict removeObjectForKey: keys[0]]) &&
	    [mutableDict objectForKey: keys[0]] == nil)

	[mutableDict setObject: @"foo" forKey: keys[0]];

	TEST(@"-[isEqual:]", ![mutableDict isEqual: dict] &&
	    R([mutableDict removeObjectForKey: @"key3"]) &&
	    ![mutableDict isEqual: dict] &&
	    R([mutableDict setObject: values[0] forKey: keys[0]]) &&

	    [mutableDict isEqual: dict])

	objc_autoreleasePoolPop(pool);
}

- (void)dictionaryTests
{
	module = @"OFDictionary";

Modified tests/OFHMACTests.m from [ee4f6f3a73] to [7c5227bed5].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFHMAC";
static const uint8_t key[] =
    "yM9h8K6IWnJRvxC/0F8XRWG7RnACDBz8wqK2tbXrYVLoKC3vPLeJikyJSM47tVHc"
    "DlXHww9zULAC2sJUlm2Kg1z4oz2aXY3Y1PQSB4VkC/m0DQ7hCI6cAg4TWnKdzWTy"
    "cvYGX+Y6HWeDY79/PGSd8fNItme6I8w4HDBqU7BP2sum3jbePJqoiSnhcyJZQTeZ"
    "jw0ZXoyrfHgOYD2M+NsTDaGpLblFtQ7n5CczjKtafG40PkEwx1dcrd46U9i3GyTK";
static const size_t key_length = sizeof(key);
static const uint8_t digest_md5[] =
    "\xCC\x1F\xEF\x09\x29\xA3\x25\x1A\x06\xA9\x83\x99\xF9\xBC\x8F\x42";
static const uint8_t digest_sha1[] =
    "\x94\xB9\x0A\x6F\xFB\xA7\x13\x6A\x75\x55"
    "\xD5\x7F\x5D\xB7\xF4\xCA\xEB\x4A\xDE\xBF";
static const uint8_t digest_rmd160[] =
    "\x2C\xE1\xED\x41\xC6\xF3\x51\xA8\x04\xD2"
    "\xC3\x9B\x08\x33\x3B\xD5\xC9\x00\x39\x50";
static const uint8_t digest_sha256[] =
    "\xFB\x8C\xDA\x88\xB3\x81\x32\x16\xD7\xD8\x62\xD4\xA6\x26\x9D\x77"
    "\x01\x99\x62\x65\x29\x02\x41\xE6\xEF\xA1\x02\x31\xA8\x9D\x77\x5D";
static const uint8_t digest_sha384[] =
    "\x2F\x4A\x47\xAE\x13\x8E\x96\x52\xF1\x8F\x05\xFD\x65\xCD\x9A\x97"
    "\x93\x2F\xC9\x02\xD6\xC6\xAB\x2E\x15\x76\xC0\xA7\xA0\x05\xF4\xEF"
    "\x14\x52\x33\x4B\x9C\x5F\xD8\x07\x4E\x98\xAE\x97\x46\x29\x24\xB4";
static const uint8_t digest_sha512[] =
    "\xF5\x8C\x3F\x9C\xA2\x2F\x0A\xF3\x26\xD8\xC0\x7E\x20\x63\x88\x61"
    "\xC9\xE1\x1F\xD7\xC7\xE5\x59\x33\xD5\x2F\xAF\x56\x1C\x94\xC8\xA4"
    "\x61\xB3\xF9\x1A\xE3\x09\x43\xA6\x5B\x85\xB1\x50\x5B\xCB\x1A\x2E"
    "\xB7\xE8\x87\xC1\x73\x19\x63\xF6\xA2\x91\x8D\x7E\x2E\xCC\xEC\x99";

@implementation TestsAppDelegate (OFHMACTests)
- (void)HMACTests
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *f = [OFFile fileWithPath: @"testfile.bin"
				    mode: @"r"];
	OFHMAC *HMAC_MD5, *HMAC_SHA1, *HMAC_RMD160;
	OFHMAC *HMAC_SHA256, *HMAC_SHA384, *HMAC_SHA512;

	TEST(@"+[HMACWithHashClass:] with MD5",
	    (HMAC_MD5 = [OFHMAC HMACWithHashClass: [OFMD5Hash class]
			    allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-1",
	    (HMAC_SHA1 = [OFHMAC HMACWithHashClass: [OFSHA1Hash class]
			     allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with RIPEMD-160",
	    (HMAC_RMD160 = [OFHMAC HMACWithHashClass: [OFRIPEMD160Hash class]
			       allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-256",
	    (HMAC_SHA256 = [OFHMAC HMACWithHashClass: [OFSHA256Hash class]
			       allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-384",
	    (HMAC_SHA384 = [OFHMAC HMACWithHashClass: [OFSHA384Hash class]
			       allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-512",
	    (HMAC_SHA512 = [OFHMAC HMACWithHashClass: [OFSHA512Hash class]
			       allowsSwappableMemory: true]))

	EXPECT_EXCEPTION(@"Detection of missing key",
	    OFInvalidArgumentException, [HMAC_MD5 updateWithBuffer: ""
							    length: 0])

	TEST(@"-[setKey:length:] with MD5",
	    R([HMAC_MD5 setKey: key
			length: key_length]))
	TEST(@"-[setKey:length:] with SHA-1",
	    R([HMAC_SHA1 setKey: key
			 length: key_length]))
	TEST(@"-[setKey:length:] with RIPEMD-160",
	    R([HMAC_RMD160 setKey: key
			   length: key_length]))
	TEST(@"-[setKey:length:] with SHA-256",
	    R([HMAC_SHA256 setKey: key
			   length: key_length]))
	TEST(@"-[setKey:length:] with SHA-384",
	    R([HMAC_SHA384 setKey: key
			   length: key_length]))
	TEST(@"-[setKey:length:] with SHA-512",
	    R([HMAC_SHA512 setKey: key
			   length: key_length]))

	while (!f.atEndOfStream) {
		char buf[64];
		size_t len = [f readIntoBuffer: buf
					length: 64];
		[HMAC_MD5 updateWithBuffer: buf
				    length: len];
		[HMAC_SHA1 updateWithBuffer: buf
				     length: len];
		[HMAC_RMD160 updateWithBuffer: buf
				       length: len];
		[HMAC_SHA256 updateWithBuffer: buf
				       length: len];
		[HMAC_SHA384 updateWithBuffer: buf
				       length: len];
		[HMAC_SHA512 updateWithBuffer: buf
				       length: len];
	}
	[f close];








	TEST(@"-[digest] with MD5",
	    memcmp(HMAC_MD5.digest, digest_md5, HMAC_MD5.digestSize) == 0)
	TEST(@"-[digest] with SHA-1",
	    memcmp(HMAC_SHA1.digest, digest_sha1, HMAC_SHA1.digestSize) == 0)
	TEST(@"-[digest] with RIPEMD-160",
	    memcmp(HMAC_RMD160.digest, digest_rmd160,
	    HMAC_RMD160.digestSize) == 0)
	TEST(@"-[digest] with SHA-256",
	    memcmp(HMAC_SHA256.digest, digest_sha256,
	    HMAC_SHA256.digestSize) == 0)
	TEST(@"-[digest] with SHA-384",
	    memcmp(HMAC_SHA384.digest, digest_sha384,
	    HMAC_SHA384.digestSize) == 0)
	TEST(@"-[digest] with SHA-512",
	    memcmp(HMAC_SHA512.digest, digest_sha512,
	    HMAC_SHA512.digestSize) == 0)

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|





|
|

|


|


|


|



|









|
<
|
|


|
|

|
|

|
|

|
|

|
|

|
|


|
|


|
<

|
<

|
<

|
<

|
<

|
<

|
|
|
<
|
<
|
<
|
<
|
<
|
<
|
<

|
>
>
>
>
>
>
>


|

|

|
|

|
<

|
<

|
<




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFHMAC";
static const uint8_t key[] =
    "yM9h8K6IWnJRvxC/0F8XRWG7RnACDBz8wqK2tbXrYVLoKC3vPLeJikyJSM47tVHc"
    "DlXHww9zULAC2sJUlm2Kg1z4oz2aXY3Y1PQSB4VkC/m0DQ7hCI6cAg4TWnKdzWTy"
    "cvYGX+Y6HWeDY79/PGSd8fNItme6I8w4HDBqU7BP2sum3jbePJqoiSnhcyJZQTeZ"
    "jw0ZXoyrfHgOYD2M+NsTDaGpLblFtQ7n5CczjKtafG40PkEwx1dcrd46U9i3GyTK";
static const size_t keyLength = sizeof(key);
static const uint8_t MD5Digest[] =
    "\xCC\x1F\xEF\x09\x29\xA3\x25\x1A\x06\xA9\x83\x99\xF9\xBC\x8F\x42";
static const uint8_t SHA1Digest[] =
    "\x94\xB9\x0A\x6F\xFB\xA7\x13\x6A\x75\x55"
    "\xD5\x7F\x5D\xB7\xF4\xCA\xEB\x4A\xDE\xBF";
static const uint8_t RIPEMD160Digest[] =
    "\x2C\xE1\xED\x41\xC6\xF3\x51\xA8\x04\xD2"
    "\xC3\x9B\x08\x33\x3B\xD5\xC9\x00\x39\x50";
static const uint8_t SHA256Digest[] =
    "\xFB\x8C\xDA\x88\xB3\x81\x32\x16\xD7\xD8\x62\xD4\xA6\x26\x9D\x77"
    "\x01\x99\x62\x65\x29\x02\x41\xE6\xEF\xA1\x02\x31\xA8\x9D\x77\x5D";
static const uint8_t SHA384Digest[] =
    "\x2F\x4A\x47\xAE\x13\x8E\x96\x52\xF1\x8F\x05\xFD\x65\xCD\x9A\x97"
    "\x93\x2F\xC9\x02\xD6\xC6\xAB\x2E\x15\x76\xC0\xA7\xA0\x05\xF4\xEF"
    "\x14\x52\x33\x4B\x9C\x5F\xD8\x07\x4E\x98\xAE\x97\x46\x29\x24\xB4";
static const uint8_t SHA512Digest[] =
    "\xF5\x8C\x3F\x9C\xA2\x2F\x0A\xF3\x26\xD8\xC0\x7E\x20\x63\x88\x61"
    "\xC9\xE1\x1F\xD7\xC7\xE5\x59\x33\xD5\x2F\xAF\x56\x1C\x94\xC8\xA4"
    "\x61\xB3\xF9\x1A\xE3\x09\x43\xA6\x5B\x85\xB1\x50\x5B\xCB\x1A\x2E"
    "\xB7\xE8\x87\xC1\x73\x19\x63\xF6\xA2\x91\x8D\x7E\x2E\xCC\xEC\x99";

@implementation TestsAppDelegate (OFHMACTests)
- (void)HMACTests
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"];

	OFHMAC *HMACMD5, *HMACSHA1, *HMACRMD160;
	OFHMAC *HMACSHA256, *HMACSHA384, *HMACSHA512;

	TEST(@"+[HMACWithHashClass:] with MD5",
	    (HMACMD5 = [OFHMAC HMACWithHashClass: [OFMD5Hash class]
			   allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-1",
	    (HMACSHA1 = [OFHMAC HMACWithHashClass: [OFSHA1Hash class]
			    allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with RIPEMD-160",
	    (HMACRMD160 = [OFHMAC HMACWithHashClass: [OFRIPEMD160Hash class]
			      allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-256",
	    (HMACSHA256 = [OFHMAC HMACWithHashClass: [OFSHA256Hash class]
			      allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-384",
	    (HMACSHA384 = [OFHMAC HMACWithHashClass: [OFSHA384Hash class]
			      allowsSwappableMemory: true]))
	TEST(@"+[HMACWithHashClass:] with SHA-512",
	    (HMACSHA512 = [OFHMAC HMACWithHashClass: [OFSHA512Hash class]
			      allowsSwappableMemory: true]))

	EXPECT_EXCEPTION(@"Detection of missing key",
	    OFInvalidArgumentException,
	    [HMACMD5 updateWithBuffer: "" length: 0])

	TEST(@"-[setKey:length:] with MD5",
	    R([HMACMD5 setKey: key length: keyLength]))

	TEST(@"-[setKey:length:] with SHA-1",
	    R([HMACSHA1 setKey: key length: keyLength]))

	TEST(@"-[setKey:length:] with RIPEMD-160",
	    R([HMACRMD160 setKey: key length: keyLength]))

	TEST(@"-[setKey:length:] with SHA-256",
	    R([HMACSHA256 setKey: key length: keyLength]))

	TEST(@"-[setKey:length:] with SHA-384",
	    R([HMACSHA384 setKey: key length: keyLength]))

	TEST(@"-[setKey:length:] with SHA-512",
	    R([HMACSHA512 setKey: key length: keyLength]))


	while (!file.atEndOfStream) {
		char buffer[64];
		size_t length = [file readIntoBuffer: buffer length: 64];

		[HMACMD5 updateWithBuffer: buffer length: length];

		[HMACSHA1 updateWithBuffer: buffer length: length];

		[HMACRMD160 updateWithBuffer: buffer length: length];

		[HMACSHA256 updateWithBuffer: buffer length: length];

		[HMACSHA384 updateWithBuffer: buffer length: length];

		[HMACSHA512 updateWithBuffer: buffer length: length];

	}
	[file close];

	TEST(@"-[calculate] with MD5", R([HMACMD5 calculate]))
	TEST(@"-[calculate] with SHA-1", R([HMACSHA1 calculate]))
	TEST(@"-[calculate] with RIPEMD-160", R([HMACRMD160 calculate]))
	TEST(@"-[calculate] with SHA-256", R([HMACSHA256 calculate]))
	TEST(@"-[calculate] with SHA-384", R([HMACSHA384 calculate]))
	TEST(@"-[calculate] with SHA-512", R([HMACSHA512 calculate]))

	TEST(@"-[digest] with MD5",
	    memcmp(HMACMD5.digest, MD5Digest, HMACMD5.digestSize) == 0)
	TEST(@"-[digest] with SHA-1",
	    memcmp(HMACSHA1.digest, SHA1Digest, HMACSHA1.digestSize) == 0)
	TEST(@"-[digest] with RIPEMD-160",
	    memcmp(HMACRMD160.digest, RIPEMD160Digest,
	    HMACRMD160.digestSize) == 0)
	TEST(@"-[digest] with SHA-256",
	    memcmp(HMACSHA256.digest, SHA256Digest, HMACSHA256.digestSize) == 0)

	TEST(@"-[digest] with SHA-384",
	    memcmp(HMACSHA384.digest, SHA384Digest, HMACSHA384.digestSize) == 0)

	TEST(@"-[digest] with SHA-512",
	    memcmp(HMACSHA512.digest, SHA512Digest, HMACSHA512.digestSize) == 0)


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFHTTPClientTests.m from [086ef33868] to [0615ead311].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <inttypes.h>
#include <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFHTTPClient";
static OFCondition *cond;
static OFHTTPResponse *response = nil;

@interface TestsAppDelegate (HTTPClientTests) <OFHTTPClientDelegate>
@end

@interface HTTPClientTestsServer: OFThread
{
@public
	uint16_t _port;
}
@end

@implementation HTTPClientTestsServer
- (id)main
{
	OFTCPSocket *listener, *client;
	char buffer[5];

	[cond lock];

	listener = [OFTCPSocket socket];
	_port = [listener bindToHost: @"127.0.0.1"
				port: 0];
	[listener listen];

	[cond signal];
	[cond unlock];

	client = [listener accept];

	if (![[client readLine] isEqual: @"GET /foo HTTP/1.1"])
		OF_ENSURE(0);

	if (![[client readLine] hasPrefix: @"User-Agent:"])
		OF_ENSURE(0);

	if (![[client readLine] isEqual: @"Content-Length: 5"])
		OF_ENSURE(0);

	if (![[client readLine] isEqual:
	    @"Content-Type: application/x-www-form-urlencoded; charset=UTF-8"])
		OF_ENSURE(0);

	if (![[client readLine] isEqual:
	    [OFString stringWithFormat: @"Host: 127.0.0.1:%" @PRIu16, _port]])
		OF_ENSURE(0);

	if (![[client readLine] isEqual: @""])
		OF_ENSURE(0);

	[client readIntoBuffer: buffer
		   exactLength: 5];
	if (memcmp(buffer, "Hello", 5) != 0)
		OF_ENSURE(0);

	[client writeString: @"HTTP/1.0 200 OK\r\n"
			     @"cONTeNT-lENgTH: 7\r\n"
			     @"\r\n"
			     @"foo\n"
			     @"bar"];
	[client close];

<
<
|




















|
|


















|


|
<


|
|



|
<
<
|
<
<
|
<
<
|
|
<



|

|
<

|
<
|
<







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
/*


 * 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 <inttypes.h>
#include <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFHTTPClient";
static OFCondition *condition;
static OFHTTPResponse *response = nil;

@interface TestsAppDelegate (HTTPClientTests) <OFHTTPClientDelegate>
@end

@interface HTTPClientTestsServer: OFThread
{
@public
	uint16_t _port;
}
@end

@implementation HTTPClientTestsServer
- (id)main
{
	OFTCPSocket *listener, *client;
	char buffer[5];

	[condition lock];

	listener = [OFTCPSocket socket];
	_port = [listener bindToHost: @"127.0.0.1" port: 0];

	[listener listen];

	[condition signal];
	[condition unlock];

	client = [listener accept];

	OFEnsure([[client readLine] isEqual: @"GET /foo HTTP/1.1"]);


	OFEnsure([[client readLine] hasPrefix: @"User-Agent:"]);


	OFEnsure([[client readLine] isEqual: @"Content-Length: 5"]);


	OFEnsure([[client readLine] isEqual:
	    @"Content-Type: application/x-www-form-urlencoded; charset=UTF-8"]);


	if (![[client readLine] isEqual:
	    [OFString stringWithFormat: @"Host: 127.0.0.1:%" @PRIu16, _port]])
		OFEnsure(0);

	OFEnsure([[client readLine] isEqual: @""]);


	[client readIntoBuffer: buffer exactLength: 5];

	OFEnsure(memcmp(buffer, "Hello", 5) == 0);


	[client writeString: @"HTTP/1.0 200 OK\r\n"
			     @"cONTeNT-lENgTH: 7\r\n"
			     @"\r\n"
			     @"foo\n"
			     @"bar"];
	[client close];
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
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response_
	  exception: (id)exception
{
	OF_ENSURE(exception == nil);

	response = [response_ retain];

	[[OFRunLoop mainRunLoop] stop];
}

- (void)HTTPClientTests
{
	void *pool = objc_autoreleasePoolPush();
	HTTPClientTestsServer *server;
	OFURL *URL;
	OFHTTPClient *client;
	OFHTTPRequest *request;
	OFData *data;

	cond = [OFCondition condition];
	[cond lock];

	server = [[[HTTPClientTestsServer alloc] init] autorelease];
	server.supportsSockets = true;
	[server start];

	[cond wait];
	[cond unlock];

	URL = [OFURL URLWithString:
	    [OFString stringWithFormat: @"http://127.0.0.1:%" @PRIu16 "/foo",
					server->_port]];

	TEST(@"-[asyncPerformRequest:]",
	    (client = [OFHTTPClient client]) && (client.delegate = self) &&







|















|
|





|
|







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
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response_
	  exception: (id)exception
{
	OFEnsure(exception == nil);

	response = [response_ retain];

	[[OFRunLoop mainRunLoop] stop];
}

- (void)HTTPClientTests
{
	void *pool = objc_autoreleasePoolPush();
	HTTPClientTestsServer *server;
	OFURL *URL;
	OFHTTPClient *client;
	OFHTTPRequest *request;
	OFData *data;

	condition = [OFCondition condition];
	[condition lock];

	server = [[[HTTPClientTestsServer alloc] init] autorelease];
	server.supportsSockets = true;
	[server start];

	[condition wait];
	[condition unlock];

	URL = [OFURL URLWithString:
	    [OFString stringWithFormat: @"http://127.0.0.1:%" @PRIu16 "/foo",
					server->_port]];

	TEST(@"-[asyncPerformRequest:]",
	    (client = [OFHTTPClient client]) && (client.delegate = self) &&

Modified tests/OFHTTPCookieManagerTests.m from [0364d9ffbe] to [8647894d6b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFHTTPCookieManager";

@implementation TestsAppDelegate (OFHTTPCookieManagerTests)
- (void)HTTPCookieManagerTests
{
	void *pool = objc_autoreleasePoolPush();
	OFHTTPCookieManager *manager = [OFHTTPCookieManager manager];
	OFURL *URL[4];
	OFHTTPCookie *cookie[5];

	URL[0] = [OFURL URLWithString: @"http://nil.im/foo"];
	URL[1] = [OFURL URLWithString: @"https://nil.im/foo/bar"];
	URL[2] = [OFURL URLWithString: @"https://test.nil.im/foo/bar"];
	URL[3] = [OFURL URLWithString: @"http://webkeks.org/foo/bar"];

	cookie[0] = [OFHTTPCookie cookieWithName: @"test"
					   value: @"1"
					  domain: @"nil.im"];
	TEST(@"-[addCookie:forURL:] #1", R([manager addCookie: cookie[0]
						       forURL: URL[0]]))

	TEST(@"-[cookiesForURL:] #1",
	    [[manager cookiesForURL: URL[0]] isEqual:
	    [OFArray arrayWithObject: cookie[0]]])

	cookie[1] = [OFHTTPCookie cookieWithName: @"test"
					   value: @"2"
					  domain: @"webkeks.org"];
	TEST(@"-[addCookie:forURL:] #2", R([manager addCookie: cookie[1]
						       forURL: URL[0]]))

	TEST(@"-[cookiesForURL:] #2",
	    [[manager cookiesForURL: URL[0]] isEqual:
	    [OFArray arrayWithObject: cookie[0]]] &&
	    [[manager cookiesForURL: URL[3]] isEqual: [OFArray array]])

	cookie[2] = [OFHTTPCookie cookieWithName: @"test"
					   value: @"3"
					  domain: @"nil.im"];
	cookie[2].secure = true;
	TEST(@"-[addCookie:forURL:] #3", R([manager addCookie: cookie[2]
						       forURL: URL[1]]))

	TEST(@"-[cookiesForURL:] #3",
	    [[manager cookiesForURL: URL[1]] isEqual:
	    [OFArray arrayWithObject: cookie[2]]] &&
	    [[manager cookiesForURL: URL[0]] isEqual: [OFArray array]])

	cookie[2].expires = [OFDate dateWithTimeIntervalSinceNow: -1];
	cookie[3] = [OFHTTPCookie cookieWithName: @"test"
					   value: @"4"
					  domain: @"nil.im"];
	cookie[3].domain = @".nil.im";
	TEST(@"-[addCookie:forURL:] #4", R([manager addCookie: cookie[3]
						       forURL: URL[1]]))

	TEST(@"-[cookiesForURL:] #4",
	    [[manager cookiesForURL: URL[1]] isEqual:
	    [OFArray arrayWithObject: cookie[3]]] &&
	    [[manager cookiesForURL: URL[2]] isEqual:
	    [OFArray arrayWithObject: cookie[3]]])

	cookie[4] = [OFHTTPCookie cookieWithName: @"bar"
					   value: @"5"
					  domain: @"test.nil.im"];
	TEST(@"-[addCookie:forURL:] #5", R([manager addCookie: cookie[4]
						       forURL: URL[0]]))

	TEST(@"-[cookiesForURL:] #5",
	    [[manager cookiesForURL: URL[0]] isEqual:
	    [OFArray arrayWithObject: cookie[3]]] &&
	    [[manager cookiesForURL: URL[2]] isEqual:
	    [OFArray arrayWithObjects: cookie[3], cookie[4], nil]])

	TEST(@"-[purgeExpiredCookies]",
	    [manager.cookies isEqual:
	    [OFArray arrayWithObjects: cookie[2], cookie[3], cookie[4], nil]] &&
	    R([manager purgeExpiredCookies]) &&
	    [manager.cookies isEqual:
	    [OFArray arrayWithObjects: cookie[3], cookie[4], nil]])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|






|
|

|
|
|
|

|
|
|
|
|


|
|

|
|
|
|
|


|
|
|

|
|
|
|
|
|


|
|
|

|
|
|
|
|
|
|


|
|
|
|

|
|
|
|
|


|
|
|
|



|


|




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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFHTTPCookieManager";

@implementation TestsAppDelegate (OFHTTPCookieManagerTests)
- (void)HTTPCookieManagerTests
{
	void *pool = objc_autoreleasePoolPush();
	OFHTTPCookieManager *manager = [OFHTTPCookieManager manager];
	OFURL *URL1, *URL2, *URL3, *URL4;
	OFHTTPCookie *cookie1, *cookie2, *cookie3, *cookie4, *cookie5;

	URL1 = [OFURL URLWithString: @"http://nil.im/foo"];
	URL2 = [OFURL URLWithString: @"https://nil.im/foo/bar"];
	URL3 = [OFURL URLWithString: @"https://test.nil.im/foo/bar"];
	URL4 = [OFURL URLWithString: @"http://webkeks.org/foo/bar"];

	cookie1 = [OFHTTPCookie cookieWithName: @"test"
					 value: @"1"
					domain: @"nil.im"];
	TEST(@"-[addCookie:forURL:] #1",
	    R([manager addCookie: cookie1 forURL: URL1]))

	TEST(@"-[cookiesForURL:] #1",
	    [[manager cookiesForURL: URL1] isEqual:
	    [OFArray arrayWithObject: cookie1]])

	cookie2 = [OFHTTPCookie cookieWithName: @"test"
					 value: @"2"
					domain: @"webkeks.org"];
	TEST(@"-[addCookie:forURL:] #2",
	    R([manager addCookie: cookie2 forURL: URL1]))

	TEST(@"-[cookiesForURL:] #2",
	    [[manager cookiesForURL: URL1] isEqual:
	    [OFArray arrayWithObject: cookie1]] &&
	    [[manager cookiesForURL: URL4] isEqual: [OFArray array]])

	cookie3 = [OFHTTPCookie cookieWithName: @"test"
					 value: @"3"
					domain: @"nil.im"];
	cookie3.secure = true;
	TEST(@"-[addCookie:forURL:] #3",
	    R([manager addCookie: cookie3 forURL: URL2]))

	TEST(@"-[cookiesForURL:] #3",
	    [[manager cookiesForURL: URL2] isEqual:
	    [OFArray arrayWithObject: cookie3]] &&
	    [[manager cookiesForURL: URL1] isEqual: [OFArray array]])

	cookie3.expires = [OFDate dateWithTimeIntervalSinceNow: -1];
	cookie4 = [OFHTTPCookie cookieWithName: @"test"
					 value: @"4"
					domain: @"nil.im"];
	cookie4.domain = @".nil.im";
	TEST(@"-[addCookie:forURL:] #4",
	    R([manager addCookie: cookie4 forURL: URL2]))

	TEST(@"-[cookiesForURL:] #4",
	    [[manager cookiesForURL: URL2] isEqual:
	    [OFArray arrayWithObject: cookie4]] &&
	    [[manager cookiesForURL: URL3] isEqual:
	    [OFArray arrayWithObject: cookie4]])

	cookie5 = [OFHTTPCookie cookieWithName: @"bar"
					 value: @"5"
					domain: @"test.nil.im"];
	TEST(@"-[addCookie:forURL:] #5",
	    R([manager addCookie: cookie5 forURL: URL1]))

	TEST(@"-[cookiesForURL:] #5",
	    [[manager cookiesForURL: URL1] isEqual:
	    [OFArray arrayWithObject: cookie4]] &&
	    [[manager cookiesForURL: URL3] isEqual:
	    [OFArray arrayWithObjects: cookie4, cookie5, nil]])

	TEST(@"-[purgeExpiredCookies]",
	    [manager.cookies isEqual:
	    [OFArray arrayWithObjects: cookie3, cookie4, cookie5, nil]] &&
	    R([manager purgeExpiredCookies]) &&
	    [manager.cookies isEqual:
	    [OFArray arrayWithObjects: cookie4, cookie5, nil]])

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFHTTPCookieTests.m from [5858023a0b] to [e2e55e8f73].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFHTTPCookie";

@implementation TestsAppDelegate (OFHTTPCookieTests)
- (void)HTTPCookieTests
{
	void *pool = objc_autoreleasePoolPush();
	OFURL *URL = [OFURL URLWithString: @"http://nil.im"];
	OFHTTPCookie *cookie[2];
	OFArray OF_GENERIC(OFHTTPCookie *) *cookies;

	cookie[0] = [OFHTTPCookie cookieWithName: @"foo"
					   value: @"bar"
					  domain: @"nil.im"];
	TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #1",
	    [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary
	    dictionaryWithObject: @"foo=bar"
	    forKey: @"Set-Cookie"] forURL: URL]
	    isEqual: [OFArray arrayWithObject: cookie[0]]])

	cookie[1] = [OFHTTPCookie cookieWithName: @"qux"
					   value: @"cookie"
					  domain: @"nil.im"];
	TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #2",
	    [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary
	    dictionaryWithObject: @"foo=bar,qux=cookie"
	    forKey: @"Set-Cookie"] forURL: URL]
	    isEqual: [OFArray arrayWithObjects: cookie[0], cookie[1], nil]])

	cookie[0].expires = [OFDate dateWithTimeIntervalSince1970: 1234567890];
	cookie[1].expires = [OFDate dateWithTimeIntervalSince1970: 1234567890];
	cookie[0].path = @"/x";
	cookie[1].domain = @"webkeks.org";
	cookie[1].path = @"/objfw";
	cookie[1].secure = true;
	cookie[1].HTTPOnly = true;
	[cookie[1].extensions addObject: @"foo"];
	[cookie[1].extensions addObject: @"bar"];
	TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #3",
	    [(cookies = [OFHTTPCookie cookiesWithResponseHeaderFields:
	    [OFDictionary dictionaryWithObject:
	    @"foo=bar; Expires=Fri, 13 Feb 2009 23:31:30 GMT; Path=/x,"
	    @"qux=cookie; Expires=Fri, 13 Feb 2009 23:31:30 GMT; "
	    @"Domain=webkeks.org; Path=/objfw; Secure; HTTPOnly; foo; bar"
	    forKey: @"Set-Cookie"] forURL: URL]) isEqual:
	    [OFArray arrayWithObjects: cookie[0], cookie[1], nil]])

	TEST(@"+[requestHeaderFieldsWithCookies:]",
	    [[OFHTTPCookie requestHeaderFieldsWithCookies: cookies] isEqual:
	    [OFDictionary dictionaryWithObject: @"foo=bar; qux=cookie"
					forKey: @"Cookie"]])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|






|


|
|
|




|

|
|
|




|

|
|
|
|
|
|
|
|
|







|









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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFHTTPCookie";

@implementation TestsAppDelegate (OFHTTPCookieTests)
- (void)HTTPCookieTests
{
	void *pool = objc_autoreleasePoolPush();
	OFURL *URL = [OFURL URLWithString: @"http://nil.im"];
	OFHTTPCookie *cookie1, *cookie2;
	OFArray OF_GENERIC(OFHTTPCookie *) *cookies;

	cookie1 = [OFHTTPCookie cookieWithName: @"foo"
					 value: @"bar"
					domain: @"nil.im"];
	TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #1",
	    [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary
	    dictionaryWithObject: @"foo=bar"
	    forKey: @"Set-Cookie"] forURL: URL]
	    isEqual: [OFArray arrayWithObject: cookie1]])

	cookie2 = [OFHTTPCookie cookieWithName: @"qux"
					 value: @"cookie"
					domain: @"nil.im"];
	TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #2",
	    [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary
	    dictionaryWithObject: @"foo=bar,qux=cookie"
	    forKey: @"Set-Cookie"] forURL: URL]
	    isEqual: [OFArray arrayWithObjects: cookie1, cookie2, nil]])

	cookie1.expires = [OFDate dateWithTimeIntervalSince1970: 1234567890];
	cookie2.expires = [OFDate dateWithTimeIntervalSince1970: 1234567890];
	cookie1.path = @"/x";
	cookie2.domain = @"webkeks.org";
	cookie2.path = @"/objfw";
	cookie2.secure = true;
	cookie2.HTTPOnly = true;
	[cookie2.extensions addObject: @"foo"];
	[cookie2.extensions addObject: @"bar"];
	TEST(@"+[cookiesWithResponseHeaderFields:forURL:] #3",
	    [(cookies = [OFHTTPCookie cookiesWithResponseHeaderFields:
	    [OFDictionary dictionaryWithObject:
	    @"foo=bar; Expires=Fri, 13 Feb 2009 23:31:30 GMT; Path=/x,"
	    @"qux=cookie; Expires=Fri, 13 Feb 2009 23:31:30 GMT; "
	    @"Domain=webkeks.org; Path=/objfw; Secure; HTTPOnly; foo; bar"
	    forKey: @"Set-Cookie"] forURL: URL]) isEqual:
	    [OFArray arrayWithObjects: cookie1, cookie2, nil]])

	TEST(@"+[requestHeaderFieldsWithCookies:]",
	    [[OFHTTPCookie requestHeaderFieldsWithCookies: cookies] isEqual:
	    [OFDictionary dictionaryWithObject: @"foo=bar; qux=cookie"
					forKey: @"Cookie"]])

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFINIFileTests.m from [72a60d0481] to [7188d57b9b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFINIFile";

@implementation TestsAppDelegate (OFINIFileTests)
- (void)INIFileTests
{
	void *pool = objc_autoreleasePoolPush();
	OFString *output = @"[tests]\r\n"
	    @"foo=baz\r\n"

<
<
|

















|







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *module;

@implementation TestsAppDelegate (OFINIFileTests)
- (void)INIFileTests
{
	void *pool = objc_autoreleasePoolPush();
	OFString *output = @"[tests]\r\n"
	    @"foo=baz\r\n"
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
	    @"double=0.75\r\n";
	OFINIFile *file;
	OFINICategory *tests, *foobar, *types;
	OFArray *array;
#ifndef OF_NINTENDO_DS
	OFString *writePath;
#endif



	TEST(@"+[fileWithPath:encoding:]",
	    (file = [OFINIFile fileWithPath: @"testfile.ini"
				   encoding: OF_STRING_ENCODING_CODEPAGE_437]))

	tests = [file categoryForName: @"tests"];
	foobar = [file categoryForName: @"foobar"];
	types = [file categoryForName: @"types"];
	TEST(@"-[categoryForName:]",
	    tests != nil && foobar != nil && types != nil)

	module = @"OFINICategory";

	TEST(@"-[stringForKey:]",
	    [[tests stringForKey: @"foo"] isEqual: @"bar"] &&
	    [[foobar stringForKey: @"quxquxqux"] isEqual: @"hello\"wörld"])

	TEST(@"-[setString:forKey:]",
	    R([tests setString: @"baz"
			forKey: @"foo"]) &&
	    R([tests setString: @"new"
			forKey: @"new"]) &&
	    R([foobar setString: @"a\fb"
			 forKey: @"qux3"]))

	TEST(@"-[integerForKey:defaultValue:]",
	    [types integerForKey: @"integer"
		    defaultValue: 2] == 0x20)

	TEST(@"-[setInteger:forKey:]", R([types setInteger: 0x10
						    forKey: @"integer"]))

	TEST(@"-[boolForKey:defaultValue:]",
	    [types boolForKey: @"bool"
		 defaultValue: false] == true)

	TEST(@"-[setBool:forKey:]", R([types setBool: false
					      forKey: @"bool"]))

	TEST(@"-[floatForKey:defaultValue:]",
	    [types floatForKey: @"float"
		  defaultValue: 1] == 0.5f)

	TEST(@"-[setFloat:forKey:]", R([types setFloat: 0.25f
						forKey: @"float"]))

	TEST(@"-[doubleForKey:defaultValue:]",
	    [types doubleForKey: @"double"
		   defaultValue: 3] == 0.25)

	TEST(@"-[setDouble:forKey:]", R([types setDouble: 0.75
						  forKey: @"double"]))

	array = [OFArray arrayWithObjects: @"1", @"2", nil];
	TEST(@"-[arrayForKey:]",
	    [[types arrayForKey: @"array1"] isEqual: array] &&
	    [[types arrayForKey: @"array2"] isEqual: array] &&
	    [[types arrayForKey: @"array3"] isEqual: [OFArray array]])

	array = [OFArray arrayWithObjects: @"foo", @"bar", nil];
	TEST(@"-[setArray:forKey:]", R([types setArray: array
						forKey: @"array1"]))

	TEST(@"-[removeValueForKey:]",
	    R([foobar removeValueForKey: @"quxqux "]) &&
	    R([types removeValueForKey: @"array2"]))

	module = @"OFINIFile";

	/* FIXME: Find a way to write files on Nintendo DS */
#ifndef OF_NINTENDO_DS
# ifndef OF_IOS
	writePath = @"tmpfile.ini";
# else
	writePath = [OFString pathWithComponents: [OFArray arrayWithObjects:
	    [[OFApplication environment] objectForKey: @"HOME"],
	    @"tmp", @"tmpfile.ini", nil]];
# endif
	TEST(@"-[writeToFile:encoding:]",
	    R([file writeToFile: writePath
		       encoding: OF_STRING_ENCODING_CODEPAGE_437]) &&
	    [[OFString
		stringWithContentsOfFile: writePath
				encoding: OF_STRING_ENCODING_CODEPAGE_437]
	    isEqual: output])
	[[OFFileManager defaultManager] removeItemAtPath: writePath];
#else
	(void)output;
#endif

	objc_autoreleasePoolPop(pool);
}
@end







>
>



|














|
<
|
<
|
<

|
<
|

|
|


|
<

|
<


|
<

|
|


|
<

|
|


|
|
|
|


|
|









<
|
<
|
<
<
<


<
|
|
|









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
	    @"double=0.75\r\n";
	OFINIFile *file;
	OFINICategory *tests, *foobar, *types;
	OFArray *array;
#ifndef OF_NINTENDO_DS
	OFString *writePath;
#endif

	module = @"OFINIFile";

	TEST(@"+[fileWithPath:encoding:]",
	    (file = [OFINIFile fileWithPath: @"testfile.ini"
				   encoding: OFStringEncodingCodepage437]))

	tests = [file categoryForName: @"tests"];
	foobar = [file categoryForName: @"foobar"];
	types = [file categoryForName: @"types"];
	TEST(@"-[categoryForName:]",
	    tests != nil && foobar != nil && types != nil)

	module = @"OFINICategory";

	TEST(@"-[stringForKey:]",
	    [[tests stringForKey: @"foo"] isEqual: @"bar"] &&
	    [[foobar stringForKey: @"quxquxqux"] isEqual: @"hello\"wörld"])

	TEST(@"-[setString:forKey:]",
	    R([tests setString: @"baz" forKey: @"foo"]) &&

	    R([tests setString: @"new" forKey: @"new"]) &&

	    R([foobar setString: @"a\fb" forKey: @"qux3"]))


	TEST(@"-[longLongForKey:defaultValue:]",

	    [types longLongForKey: @"integer" defaultValue: 2] == 0x20)

	TEST(@"-[setLongLong:forKey:]",
	    R([types setLongLong: 0x10 forKey: @"integer"]))

	TEST(@"-[boolForKey:defaultValue:]",
	    [types boolForKey: @"bool" defaultValue: false] == true)


	TEST(@"-[setBool:forKey:]", R([types setBool: false forKey: @"bool"]))


	TEST(@"-[floatForKey:defaultValue:]",
	    [types floatForKey: @"float" defaultValue: 1] == 0.5f)


	TEST(@"-[setFloat:forKey:]",
	    R([types setFloat: 0.25f forKey: @"float"]))

	TEST(@"-[doubleForKey:defaultValue:]",
	    [types doubleForKey: @"double" defaultValue: 3] == 0.25)


	TEST(@"-[setDouble:forKey:]",
	    R([types setDouble: 0.75 forKey: @"double"]))

	array = [OFArray arrayWithObjects: @"1", @"2", nil];
	TEST(@"-[stringArrayForKey:]",
	    [[types stringArrayForKey: @"array1"] isEqual: array] &&
	    [[types stringArrayForKey: @"array2"] isEqual: array] &&
	    [[types stringArrayForKey: @"array3"] isEqual: [OFArray array]])

	array = [OFArray arrayWithObjects: @"foo", @"bar", nil];
	TEST(@"-[setStringArray:forKey:]",
	    R([types setStringArray: array forKey: @"array1"]))

	TEST(@"-[removeValueForKey:]",
	    R([foobar removeValueForKey: @"quxqux "]) &&
	    R([types removeValueForKey: @"array2"]))

	module = @"OFINIFile";

	/* FIXME: Find a way to write files on Nintendo DS */
#ifndef OF_NINTENDO_DS

	writePath = [[OFSystemInfo temporaryDirectoryPath]

	    stringByAppendingPathComponent: @"objfw-tests.ini"];



	TEST(@"-[writeToFile:encoding:]",
	    R([file writeToFile: writePath

		       encoding: OFStringEncodingCodepage437]) &&
	    [[OFString stringWithContentsOfFile: writePath
				       encoding: OFStringEncodingCodepage437]
	    isEqual: output])
	[[OFFileManager defaultManager] removeItemAtPath: writePath];
#else
	(void)output;
#endif

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFIPXSocketTests.m from [a065806b7f] to [4dd5afc6b6].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "TestsAppDelegate.h"

static OFString *module = @"OFIPXSocket";

@implementation TestsAppDelegate (OFIPXSocketTests)
- (void)IPXSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFIPXSocket *sock;
	of_socket_address_t address1, address2;
	char buffer[5];

	TEST(@"+[socket]", (sock = [OFIPXSocket socket]))

	@try {
		TEST(@"-[bindToPort:packetType:]",
		    R(address1 = [sock bindToPort: 0
				       packetType: 0]))
	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EAFNOSUPPORT:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFIPXSocket] -[bindToPort:packetType:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFIPXSocket] -[bindToPort:packetType:]: "
			    @"IPX not configured, skipping tests"];
			break;
		default:
			@throw e;
		}

		objc_autoreleasePoolPop(pool);
		return;
	}

	TEST(@"-[sendBuffer:length:receiver:]",
	    R([sock sendBuffer: "Hello"
			length: 5
		      receiver: &address1]))

	TEST(@"-[receiveIntoBuffer:length:sender:]",
	    [sock receiveIntoBuffer: buffer
			     length: 5
			     sender: &address2] == 5 &&
	    memcmp(buffer, "Hello", 5) == 0 &&
	    of_socket_address_equal(&address1, &address2) &&
	    of_socket_address_hash(&address1) ==
	    of_socket_address_hash(&address2))

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|






|






|
<



|
|
|



|
|
|











|
<
<


|
<
<

|
|
<




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
/*


 * 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 "TestsAppDelegate.h"

static OFString *const module = @"OFIPXSocket";

@implementation TestsAppDelegate (OFIPXSocketTests)
- (void)IPXSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFIPXSocket *sock;
	OFSocketAddress address1, address2;
	char buffer[5];

	TEST(@"+[socket]", (sock = [OFIPXSocket socket]))

	@try {
		TEST(@"-[bindToPort:packetType:]",
		    R(address1 = [sock bindToPort: 0 packetType: 0]))

	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EAFNOSUPPORT:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFIPXSocket] -[bindToPort:packetType:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFIPXSocket] -[bindToPort:packetType:]: "
			    @"IPX not configured, skipping tests"];
			break;
		default:
			@throw e;
		}

		objc_autoreleasePoolPop(pool);
		return;
	}

	TEST(@"-[sendBuffer:length:receiver:]",
	    R([sock sendBuffer: "Hello" length: 5 receiver: &address1]))



	TEST(@"-[receiveIntoBuffer:length:sender:]",
	    [sock receiveIntoBuffer: buffer length: 5 sender: &address2] == 5 &&


	    memcmp(buffer, "Hello", 5) == 0 &&
	    OFSocketAddressEqual(&address1, &address2) &&
	    OFSocketAddressHash(&address1) == OFSocketAddressHash(&address2))


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFInvocationTests.m from [2e969e8d08] to [bb91b5d725].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
268
269
270
271

#if defined(HAVE_COMPLEX_H) && !defined(__STDC_NO_COMPLEX__)
# include <complex.h>
#endif

#import "TestsAppDelegate.h"

static OFString *module = @"OFInvocation";

struct test_struct {
	unsigned char c;
	unsigned int i;
};

@implementation TestsAppDelegate (OFInvocationTests)
- (struct test_struct)invocationTestMethod1: (unsigned char)c
					   : (unsigned int)i
					   : (struct test_struct *)ptr
					   : (struct test_struct)st
{
	return st;
}

#ifdef OF_INVOCATION_CAN_INVOKE
- (void)invocationTestMethod2: (id)obj
{
	assert(obj == self);
}

- (int)invocationTestMethod3: (int)i1
			    : (int)i2
			    : (int)i3
			    : (int)i4
			    : (int)i5
			    : (int)i6
			    : (int)i7
			    : (int)i8
			    : (int)i9
			    : (int)i10
			    : (int)i11
			    : (int)i12
			    : (int)i13
			    : (int)i14
			    : (int)i15
			    : (int)i16
{
	return (i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 +
	    i12 + i13 + i14 + i15 + i16) / 16;
}

- (double)invocationTestMethod4: (double)d1
			       : (double)d2
			       : (double)d3
			       : (double)d4
			       : (double)d5
			       : (double)d6
			       : (double)d7
			       : (double)d8
			       : (double)d9
			       : (double)d10
			       : (double)d11
			       : (double)d12
			       : (double)d13
			       : (double)d14
			       : (double)d15
			       : (double)d16
{
	return (d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 +
	    d12 + d13 + d14 + d15 + d16) / 16;
}

- (float)invocationTestMethod5: (double)d1
			      : (float)f2
			      : (float)f3
			      : (float)f4
			      : (float)f5
			      : (float)f6
			      : (float)f7
			      : (float)f8
			      : (float)f9
			      : (double)d10
			      : (float)f11
			      : (float)f12
			      : (float)f13
			      : (float)f14
			      : (float)f15
			      : (float)f16
{
	return (float)((d1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9 + d10 + f11 +
	    f12 + f13 + f14 + f15 + f16) / 16);
}

- (long double)invocationTestMethod6: (long double)d1
				    : (long double)d2
				    : (long double)d3
				    : (long double)d4
				    : (long double)d5
				    : (long double)d6
				    : (long double)d7
				    : (long double)d8
				    : (long double)d9
				    : (long double)d10
				    : (long double)d11
				    : (long double)d12
				    : (long double)d13
				    : (long double)d14
				    : (long double)d15
				    : (long double)d16
{
	return (d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 +
	    d12 + d13 + d14 + d15 + d16) / 16;
}

# if defined(HAVE_COMPLEX_H) && !defined(__STDC_NO_COMPLEX__)
- (complex double)invocationTestMethod7: (complex float)c1
				       : (complex double)c2
				       : (complex float)c3
				       : (complex double)c4
				       : (complex float)c5
				       : (complex double)c6
				       : (complex float)c7
				       : (complex double)c8
				       : (complex float)c9
				       : (complex double)c10
				       : (complex float)c11
				       : (complex double)c12
				       : (complex float)c13
				       : (complex double)c14
				       : (complex float)c15
				       : (complex double)c16
{
	OF_ENSURE(creal(c1) == 1.0 && cimag(c1) == 0.5);
	OF_ENSURE(creal(c2) == 2.0 && cimag(c2) == 1.0);
	OF_ENSURE(creal(c3) == 3.0 && cimag(c3) == 1.5);
	OF_ENSURE(creal(c4) == 4.0 && cimag(c4) == 2.0);
	OF_ENSURE(creal(c5) == 5.0 && cimag(c5) == 2.5);
	OF_ENSURE(creal(c6) == 6.0 && cimag(c6) == 3.0);
	OF_ENSURE(creal(c7) == 7.0 && cimag(c7) == 3.5);
	OF_ENSURE(creal(c8) == 8.0 && cimag(c8) == 4.0);
	OF_ENSURE(creal(c9) == 9.0 && cimag(c9) == 4.5);
	OF_ENSURE(creal(c10) == 10.0 && cimag(c10) == 5.0);
	OF_ENSURE(creal(c11) == 11.0 && cimag(c11) == 5.5);
	OF_ENSURE(creal(c12) == 12.0 && cimag(c12) == 6.0);
	OF_ENSURE(creal(c13) == 13.0 && cimag(c13) == 6.5);
	OF_ENSURE(creal(c14) == 14.0 && cimag(c14) == 7.0);
	OF_ENSURE(creal(c15) == 15.0 && cimag(c15) == 7.5);
	OF_ENSURE(creal(c16) == 16.0 && cimag(c16) == 8.0);

	return (c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 +
	    c12 + c13 + c14 + c15 + c16) / 16;
}

- (complex long double)invocationTestMethod8: (complex double)c1
					    : (complex float)c2
					    : (complex long double)c3
					    : (complex double)c4
					    : (complex float)c5
					    : (complex long double)c6
					    : (complex double)c7
					    : (complex float)c8
					    : (complex long double)c9
					    : (complex double)c10
					    : (complex float)c11
					    : (complex long double)c12
					    : (complex double)c13
					    : (complex float)c14
					    : (complex long double)c15
					    : (complex double)c16
{
	OF_ENSURE(creal(c1) == 1.0 && cimag(c1) == 0.5);
	OF_ENSURE(creal(c2) == 2.0 && cimag(c2) == 1.0);
	OF_ENSURE(creal(c3) == 3.0 && cimag(c3) == 1.5);
	OF_ENSURE(creal(c4) == 4.0 && cimag(c4) == 2.0);
	OF_ENSURE(creal(c5) == 5.0 && cimag(c5) == 2.5);
	OF_ENSURE(creal(c6) == 6.0 && cimag(c6) == 3.0);
	OF_ENSURE(creal(c7) == 7.0 && cimag(c7) == 3.5);
	OF_ENSURE(creal(c8) == 8.0 && cimag(c8) == 4.0);
	OF_ENSURE(creal(c9) == 9.0 && cimag(c9) == 4.5);
	OF_ENSURE(creal(c10) == 10.0 && cimag(c10) == 5.0);
	OF_ENSURE(creal(c11) == 11.0 && cimag(c11) == 5.5);
	OF_ENSURE(creal(c12) == 12.0 && cimag(c12) == 6.0);
	OF_ENSURE(creal(c13) == 13.0 && cimag(c13) == 6.5);
	OF_ENSURE(creal(c14) == 14.0 && cimag(c14) == 7.0);
	OF_ENSURE(creal(c15) == 15.0 && cimag(c15) == 7.5);
	OF_ENSURE(creal(c16) == 16.0 && cimag(c16) == 8.0);

	return (c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 +
	    c12 + c13 + c14 + c15 + c16) / 16;
}
# endif

# ifdef __SIZEOF_INT128__
__extension__
- (__int128)invocationTestMethod9: (int)i1
				 : (__int128)i2
				 : (__int128)i3
				 : (__int128)i4
				 : (int)i5
				 : (__int128)i6
				 : (__int128)i7
				 : (__int128)i8
				 : (__int128)i9
				 : (__int128)i10
				 : (__int128)i11
				 : (__int128)i12
				 : (__int128)i13
				 : (__int128)i14
				 : (__int128)i15
				 : (__int128)i16
{
	__int128 mask = (__int128)0xFFFFFFFFFFFFFFFF << 64;

	OF_ENSURE(i1 == 1);
	OF_ENSURE(i2 == mask + 2);
	OF_ENSURE(i3 == mask + 3);
	OF_ENSURE(i4 == mask + 4);
	OF_ENSURE(i5 == 5);
	OF_ENSURE(i6 == mask + 6);
	OF_ENSURE(i7 == mask + 7);
	OF_ENSURE(i8 == mask + 8);
	OF_ENSURE(i9 == mask + 9);
	OF_ENSURE(i10 == mask + 10);
	OF_ENSURE(i11 == mask + 11);
	OF_ENSURE(i12 == mask + 12);
	OF_ENSURE(i13 == mask + 13);
	OF_ENSURE(i14 == mask + 14);
	OF_ENSURE(i15 == mask + 15);
	OF_ENSURE(i16 == mask + 16);

	return ((i1 + (int)i2 + (int)i3 + (int)i4 + i5 + (int)i6 + (int)i7 +
	    (int)i8 + (int)i9 + (int)i10 + (int)i11 + (int)i12 + (int)i13 +
	    (int)i14 + (int)i15 + (int)i16) / 16) + mask;
}
# endif
#endif

- (void)invocationTests
{
	void *pool = objc_autoreleasePoolPush();
	SEL selector = @selector(invocationTestMethod1::::);
	OFMethodSignature *sig = [self methodSignatureForSelector: selector];
	OFInvocation *invocation;
	struct test_struct st, st2, *stp = &st, *stp2;
	unsigned const char c = 0xAA;
	unsigned char c2;
	const unsigned int i = 0x55555555;
	unsigned int i2;

	memset(&st, '\xFF', sizeof(st));
	st.c = 0x55;







|

|





|
|
|
|




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






|







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

#if defined(HAVE_COMPLEX_H) && !defined(__STDC_NO_COMPLEX__)
# include <complex.h>
#endif

#import "TestsAppDelegate.h"

static OFString *const module = @"OFInvocation";

struct TestStruct {
	unsigned char c;
	unsigned int i;
};

@implementation TestsAppDelegate (OFInvocationTests)
- (struct TestStruct)invocationTestMethod1: (unsigned char)c
					  : (unsigned int)i
					  : (struct TestStruct *)ptr
					  : (struct TestStruct)st
{
	return st;
}






















































































































































































































- (void)invocationTests
{
	void *pool = objc_autoreleasePoolPush();
	SEL selector = @selector(invocationTestMethod1::::);
	OFMethodSignature *sig = [self methodSignatureForSelector: selector];
	OFInvocation *invocation;
	struct TestStruct st, st2, *stp = &st, *stp2;
	unsigned const char c = 0xAA;
	unsigned char c2;
	const unsigned int i = 0x55555555;
	unsigned int i2;

	memset(&st, '\xFF', sizeof(st));
	st.c = 0x55;
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
	TEST(@"-[setReturnValue]", R([invocation setReturnValue: &st]))

	TEST(@"-[getReturnValue]", R([invocation getReturnValue: &st2]) &&
	    memcmp(&st, &st2, sizeof(st)) == 0)

	memset(&st2, '\0', sizeof(st2));

	TEST(@"-[setArgument:atIndex:] #1", R([invocation setArgument: &c
							      atIndex: 2]))

	TEST(@"-[setArgument:atIndex:] #2", R([invocation setArgument: &i
							      atIndex: 3]))

	TEST(@"-[setArgument:atIndex:] #3", R([invocation setArgument: &stp
							      atIndex: 4]))

	TEST(@"-[setArgument:atIndex:] #4", R([invocation setArgument: &st
							      atIndex: 5]))

	TEST(@"-[getArgument:atIndex:] #1", R([invocation getArgument: &c2
							      atIndex: 2]) &&
	    c == c2)

	TEST(@"-[getArgument:atIndex:] #2", R([invocation getArgument: &i2
							      atIndex: 3]) &&
	    i == i2)

	TEST(@"-[getArgument:atIndex:] #3", R([invocation getArgument: &stp2
							      atIndex: 4]) &&
	    stp == stp2)

	TEST(@"-[getArgument:atIndex:] #4", R([invocation getArgument: &st2
							      atIndex: 5]) &&
	    memcmp(&st, &st2, sizeof(st)) == 0)

#ifdef OF_INVOCATION_CAN_INVOKE
	/* -[invoke] #1 */
	selector = @selector(invocationTestMethod2:);
	invocation = [OFInvocation invocationWithMethodSignature:
	    [self methodSignatureForSelector: selector]];

	[invocation setArgument: &self
			atIndex: 0];
	[invocation setArgument: &selector
			atIndex: 1];
	[invocation setArgument: &self
			atIndex: 2];

	TEST(@"-[invoke] #1", R([invocation invoke]))

	/* -[invoke] #2 */
	selector = @selector(invocationTestMethod3::::::::::::::::);
	invocation = [OFInvocation invocationWithMethodSignature:
	    [self methodSignatureForSelector: selector]];

	[invocation setArgument: &self
			atIndex: 0];
	[invocation setArgument: &selector
			atIndex: 1];

	for (int j = 1; j <= 16; j++)
		[invocation setArgument: &j
				atIndex: j + 1];

	int intResult;
	TEST(@"-[invoke] #2", R([invocation invoke]) &&
	    R([invocation getReturnValue: &intResult]) && intResult == 8)

	/* -[invoke] #3 */
	selector = @selector(invocationTestMethod4::::::::::::::::);
	invocation = [OFInvocation invocationWithMethodSignature:
	    [self methodSignatureForSelector: selector]];

	[invocation setArgument: &self
			atIndex: 0];
	[invocation setArgument: &selector
			atIndex: 1];

	for (int j = 1; j <= 16; j++) {
		double d = j;
		[invocation setArgument: &d
				atIndex: j + 1];
	}

	double doubleResult;
	TEST(@"-[invoke] #3", R([invocation invoke]) &&
	    R([invocation getReturnValue: &doubleResult]) &&
	    doubleResult == 8.5)

	/* -[invoke] #4 */
	selector = @selector(invocationTestMethod5::::::::::::::::);
	invocation = [OFInvocation invocationWithMethodSignature:
	    [self methodSignatureForSelector: selector]];

	[invocation setArgument: &self
			atIndex: 0];
	[invocation setArgument: &selector
			atIndex: 1];

	for (int j = 1; j <= 16; j++) {
		float f = j;
		double d = j;

		if (j == 1 || j == 10)
			[invocation setArgument: &d
					atIndex: j + 1];
		else
			[invocation setArgument: &f
					atIndex: j + 1];
	}

	float floatResult;
	TEST(@"-[invoke] #4", R([invocation invoke]) &&
	    R([invocation getReturnValue: &floatResult]) && floatResult == 8.5)

	/* Only when encoding long doubles is supported */
	if (strcmp(@encode(double), @encode(long double)) != 0) {
		/* -[invoke] #5 */
		selector = @selector(invocationTestMethod6::::::::::::::::);
		invocation = [OFInvocation invocationWithMethodSignature:
		    [self methodSignatureForSelector: selector]];

		[invocation setArgument: &self
				atIndex: 0];
		[invocation setArgument: &selector
				atIndex: 1];

		for (int j = 1; j <= 16; j++) {
			long double d = j;
			[invocation setArgument: &d
					atIndex: j + 1];
		}

		long double longDoubleResult;
		TEST(@"-[invoke] #5", R([invocation invoke]) &&
		    R([invocation getReturnValue: &longDoubleResult]) &&
		    longDoubleResult == 8.5)
	}

# if defined(HAVE_COMPLEX_H) && !defined(__STDC_NO_COMPLEX__)
	/* -[invoke] #6 */
	selector = @selector(invocationTestMethod7::::::::::::::::);
	invocation = [OFInvocation invocationWithMethodSignature:
	    [self methodSignatureForSelector: selector]];

	[invocation setArgument: &self
			atIndex: 0];
	[invocation setArgument: &selector
			atIndex: 1];

	for (int j = 1; j <= 16; j++) {
		complex float cf = j + 0.5 * j * I;
		complex double cd = j + 0.5 * j * I;

		if (j & 1)
			[invocation setArgument: &cf
					atIndex: j + 1];
		else
			[invocation setArgument: &cd
					atIndex: j + 1];
	}

	complex double complexDoubleResult;
	TEST(@"-[invoke] #6", R([invocation invoke]) &&
	    R([invocation getReturnValue: &complexDoubleResult]) &&
	    complexDoubleResult == 8.5 + 4.25 * I)

	/* Only when encoding complex long doubles is supported */
	if (strcmp(@encode(complex double),
	    @encode(complex long double)) != 0) {
		/* -[invoke] #7 */
		selector = @selector(invocationTestMethod8::::::::::::::::);
		invocation = [OFInvocation invocationWithMethodSignature:
		    [self methodSignatureForSelector: selector]];

		[invocation setArgument: &self
				atIndex: 0];
		[invocation setArgument: &selector
				atIndex: 1];

		for (int j = 1; j <= 16; j++) {
			complex double cd = j + 0.5 * j * I;
			complex float cf = j + 0.5 * j * I;
			complex long double cld = j + 0.5 * j * I;

			switch (j % 3) {
			case 0:
				[invocation setArgument: &cld
						atIndex: j + 1];
				break;
			case 1:
				[invocation setArgument: &cd
						atIndex: j + 1];
				break;
			case 2:
				[invocation setArgument: &cf
						atIndex: j + 1];
				break;
			}
		}

		complex long double complexLongDoubleResult;
		TEST(@"-[invoke] #7", R([invocation invoke]) &&
		    R([invocation getReturnValue: &complexLongDoubleResult]) &&
		    complexLongDoubleResult == 8.5 + 4.25 * I)
	}
# endif

# ifdef __SIZEOF_INT128__
	/* -[invoke] #8 */
	selector = @selector(invocationTestMethod9::::::::::::::::);
	invocation = [OFInvocation invocationWithMethodSignature:
	    [self methodSignatureForSelector: selector]];

	[invocation setArgument: &self
			atIndex: 0];
	[invocation setArgument: &selector
			atIndex: 1];

	for (int j = 1; j <= 16; j++) {
		__extension__ __int128 i128 = 0xFFFFFFFFFFFFFFFF;
		i128 <<= 64;
		i128 |= j;

		if (j == 1 || j == 5)
			[invocation setArgument: &j
					atIndex: j + 1];
		else
			[invocation setArgument: &i128
					atIndex: j + 1];
	}

	__extension__ __int128 int128Result;
	TEST(@"-[invoke] #8", R([invocation invoke]) &&
	    R([invocation getReturnValue: &int128Result]) &&
	    int128Result == __extension__ ((__int128)0xFFFFFFFFFFFFFFFF << 64) +
	    8)
# endif
#endif

	objc_autoreleasePoolPop(pool);
}
@end







|
<
<
|
<

<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<

<
<
<
<
<
|
<
<
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
|
<
<
<
<

<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
|
<
<
<
<
|
<
<
<
<
<
<
<
<
<
|
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




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
	TEST(@"-[setReturnValue]", R([invocation setReturnValue: &st]))

	TEST(@"-[getReturnValue]", R([invocation getReturnValue: &st2]) &&
	    memcmp(&st, &st2, sizeof(st)) == 0)

	memset(&st2, '\0', sizeof(st2));

	TEST(@"-[setArgument:atIndex:] #1",


	    R([invocation setArgument: &c atIndex: 2]))





	TEST(@"-[setArgument:atIndex:] #2",












































	    R([invocation setArgument: &i atIndex: 3]))


















	TEST(@"-[setArgument:atIndex:] #3",













	    R([invocation setArgument: &stp atIndex: 4]))









	TEST(@"-[setArgument:atIndex:] #4",


	    R([invocation setArgument: &st atIndex: 5]))















	TEST(@"-[getArgument:atIndex:] #1",































	    R([invocation getArgument: &c2 atIndex: 2]) && c == c2)




















	TEST(@"-[getArgument:atIndex:] #2",


	    R([invocation getArgument: &i2 atIndex: 3]) && i == i2)











	TEST(@"-[getArgument:atIndex:] #3",








	    R([invocation getArgument: &stp2 atIndex: 4]) && stp == stp2)














	TEST(@"-[getArgument:atIndex:] #4",
	    R([invocation getArgument: &st2 atIndex: 5]) &&

	    memcmp(&st, &st2, sizeof(st)) == 0)





















	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFJSONTests.m from [dda895677f] to [330199594e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFJSON";

@implementation TestsAppDelegate (JSONTests)
- (void)JSONTests
{
	void *pool = objc_autoreleasePoolPush();
	OFString *s = @"{\"foo\"\t:'b\\na\\r', \"x\":/*foo*/ [.5\r,0xF,null"
	    @"//bar\n,\"foo\",false]}";
	OFDictionary *d = [OFDictionary dictionaryWithKeysAndObjects:
	    @"foo", @"b\na\r",
	    @"x", [OFArray arrayWithObjects:
	    [OFNumber numberWithFloat: .5f],
	    [OFNumber numberWithInt: 0xF],
	    [OFNull null],
	    @"foo",
	    [OFNumber numberWithBool: false],
	    nil],
	    nil];

	TEST(@"-[objectByParsingJSON] #1", [s.objectByParsingJSON isEqual: d])


	TEST(@"-[JSONRepresentation]", [[d JSONRepresentation] isEqual:

	    @"{\"x\":[0.5,15,null,\"foo\",false],\"foo\":\"b\\na\\r\"}"])

	TEST(@"OF_JSON_REPRESENTATION_PRETTY",
	    [[d JSONRepresentationWithOptions: OF_JSON_REPRESENTATION_PRETTY]

	    isEqual: @"{\n\t\"x\": [\n\t\t0.5,\n\t\t15,\n\t\tnull,\n\t\t"
		     @"\"foo\",\n\t\tfalse\n\t],\n\t\"foo\": \"b\\na\\r\"\n}"])

	TEST(@"OF_JSON_REPRESENTATION_JSON5",
	    [[d JSONRepresentationWithOptions: OF_JSON_REPRESENTATION_JSON5]

	    isEqual: @"{x:[0.5,15,null,\"foo\",false],foo:\"b\\\na\\r\"}"])

	EXPECT_EXCEPTION(@"-[objectByParsingJSON] #2", OFInvalidJSONException,
	    [@"{" objectByParsingJSON])
	EXPECT_EXCEPTION(@"-[objectByParsingJSON] #3", OFInvalidJSONException,
	    [@"]" objectByParsingJSON])
	EXPECT_EXCEPTION(@"-[objectByParsingJSON] #4", OFInvalidJSONException,
	    [@"bar" objectByParsingJSON])

<
<
|

















|





|
|
|


|
|
|
|
|
|


|
>

|
>


|
|
>
|
|

|
|
>
|







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFJSON";

@implementation TestsAppDelegate (JSONTests)
- (void)JSONTests
{
	void *pool = objc_autoreleasePoolPush();
	OFString *string = @"{\"foo\"\t:'b\\na\\r', \"x\":/*foo*/ [.5\r,0xF,"
	    @"null//bar\n,\"foo\",false]}";
	OFDictionary *dict = [OFDictionary dictionaryWithKeysAndObjects:
	    @"foo", @"b\na\r",
	    @"x", [OFArray arrayWithObjects:
		[OFNumber numberWithFloat: .5f],
		[OFNumber numberWithInt: 0xF],
		[OFNull null],
		@"foo",
		[OFNumber numberWithBool: false],
		nil],
	    nil];

	TEST(@"-[objectByParsingJSON] #1",
	    [string.objectByParsingJSON isEqual: dict])

	TEST(@"-[JSONRepresentation]",
	    [[dict JSONRepresentation] isEqual:
	    @"{\"x\":[0.5,15,null,\"foo\",false],\"foo\":\"b\\na\\r\"}"])

	TEST(@"OFJSONRepresentationOptionPretty",
	    [[dict JSONRepresentationWithOptions:
	    OFJSONRepresentationOptionPretty] isEqual:
	    @"{\n\t\"x\": [\n\t\t0.5,\n\t\t15,\n\t\tnull,\n\t\t"
	    @"\"foo\",\n\t\tfalse\n\t],\n\t\"foo\": \"b\\na\\r\"\n}"])

	TEST(@"OFJSONRepresentationOptionJSON5",
	    [[dict JSONRepresentationWithOptions:
	    OFJSONRepresentationOptionJSON5] isEqual:
	    @"{x:[0.5,15,null,\"foo\",false],foo:\"b\\\na\\r\"}"])

	EXPECT_EXCEPTION(@"-[objectByParsingJSON] #2", OFInvalidJSONException,
	    [@"{" objectByParsingJSON])
	EXPECT_EXCEPTION(@"-[objectByParsingJSON] #3", OFInvalidJSONException,
	    [@"]" objectByParsingJSON])
	EXPECT_EXCEPTION(@"-[objectByParsingJSON] #4", OFInvalidJSONException,
	    [@"bar" objectByParsingJSON])

Modified tests/OFKernelEventObserverTests.m from [d22c843df2] to [cec268df6c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#endif
#ifdef HAVE_SELECT
# import "OFSelectKernelEventObserver.h"
#endif

#import "TestsAppDelegate.h"

#define EXPECTED_EVENTS 3

static OFString *module;

@interface ObserverTest: OFObject <OFKernelEventObserverDelegate>
{
@public
	TestsAppDelegate *_testsAppDelegate;







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#endif
#ifdef HAVE_SELECT
# import "OFSelectKernelEventObserver.h"
#endif

#import "TestsAppDelegate.h"

static const size_t numExpectedEvents = 3;

static OFString *module;

@interface ObserverTest: OFObject <OFKernelEventObserverDelegate>
{
@public
	TestsAppDelegate *_testsAppDelegate;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

	@try {
		uint16_t port;

		_testsAppDelegate = testsAppDelegate;

		_server = [[OFTCPSocket alloc] init];
		port = [_server bindToHost: @"127.0.0.1"
				      port: 0];
		[_server listen];

		_client = [[OFTCPSocket alloc] init];
		[_client connectToHost: @"127.0.0.1"
				  port: port];

		[_client writeBuffer: "0"
			      length: 1];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







|
<



|
<
<
|
<







54
55
56
57
58
59
60
61

62
63
64
65


66

67
68
69
70
71
72
73

	@try {
		uint16_t port;

		_testsAppDelegate = testsAppDelegate;

		_server = [[OFTCPSocket alloc] init];
		port = [_server bindToHost: @"127.0.0.1" port: 0];

		[_server listen];

		_client = [[OFTCPSocket alloc] init];
		[_client connectToHost: @"127.0.0.1" port: port];


		[_client writeBuffer: "0" length: 1];

	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
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
	OFDate *deadline;
	bool deadlineExceeded = false;

	[_testsAppDelegate outputTesting: @"-[observe] with listening socket"
				inModule: module];

	deadline = [OFDate dateWithTimeIntervalSinceNow: 1];
	while (_events < EXPECTED_EVENTS) {
		if (deadline.timeIntervalSinceNow < 0) {
			deadlineExceeded = true;
			break;
		}

		[_observer observeForTimeInterval: 0.01];
	}

	if (!deadlineExceeded)
		[_testsAppDelegate
		    outputSuccess: @"-[observe] not exceeding deadline"
			 inModule: module];
	else {
		[_testsAppDelegate
		    outputFailure: @"-[observe] not exceeding deadline"
			 inModule: module];
		_fails++;
	}

	if (_events == EXPECTED_EVENTS)
		[_testsAppDelegate
		    outputSuccess: @"-[observe] handling all events"
			 inModule: module];
	else {
		[_testsAppDelegate
		    outputFailure: @"-[observe] handling all events"
			 inModule: module];
		_fails++;
	}
}

- (void)objectIsReadyForReading: (id)object
{
	char buf;

	switch (_events++) {
	case 0:
		if (object == _server)
			[_testsAppDelegate
			    outputSuccess: @"-[observe] with listening socket"
				 inModule: module];







|



















|













|







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
	OFDate *deadline;
	bool deadlineExceeded = false;

	[_testsAppDelegate outputTesting: @"-[observe] with listening socket"
				inModule: module];

	deadline = [OFDate dateWithTimeIntervalSinceNow: 1];
	while (_events < numExpectedEvents) {
		if (deadline.timeIntervalSinceNow < 0) {
			deadlineExceeded = true;
			break;
		}

		[_observer observeForTimeInterval: 0.01];
	}

	if (!deadlineExceeded)
		[_testsAppDelegate
		    outputSuccess: @"-[observe] not exceeding deadline"
			 inModule: module];
	else {
		[_testsAppDelegate
		    outputFailure: @"-[observe] not exceeding deadline"
			 inModule: module];
		_fails++;
	}

	if (_events == numExpectedEvents)
		[_testsAppDelegate
		    outputSuccess: @"-[observe] handling all events"
			 inModule: module];
	else {
		[_testsAppDelegate
		    outputFailure: @"-[observe] handling all events"
			 inModule: module];
		_fails++;
	}
}

- (void)objectIsReadyForReading: (id)object
{
	char buffer;

	switch (_events++) {
	case 0:
		if (object == _server)
			[_testsAppDelegate
			    outputSuccess: @"-[observe] with listening socket"
				 inModule: module];
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
		[_testsAppDelegate
		    outputTesting: @"-[observe] with data ready to read"
			 inModule: module];

		break;
	case 1:
		if (object == _accepted &&
		    [object readIntoBuffer: &buf
				    length: 1] == 1 && buf == '0')
			[_testsAppDelegate
			    outputSuccess: @"-[observe] with data ready to read"
				 inModule: module];
		else {
			[_testsAppDelegate
			    outputFailure: @"-[observe] with data ready to read"
				 inModule: module];
			_fails++;
		}

		[_client close];

		[_testsAppDelegate
		    outputTesting: @"-[observe] with closed connection"
			 inModule: module];

		break;
	case 2:
		if (object == _accepted &&
		    [object readIntoBuffer: &buf
				    length: 1] == 0)
			[_testsAppDelegate
			    outputSuccess: @"-[observe] with closed connection"
				 inModule: module];
		else {
			[_testsAppDelegate
			    outputFailure: @"-[observe] with closed connection"
				 inModule: module];
			_fails++;
		}

		break;
	default:
		OF_ENSURE(0);
	}
}
@end

@implementation TestsAppDelegate (OFKernelEventObserverTests)
- (void)kernelEventObserverTestsWithClass: (Class)class
{







|
|



















|
<












|







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
		[_testsAppDelegate
		    outputTesting: @"-[observe] with data ready to read"
			 inModule: module];

		break;
	case 1:
		if (object == _accepted &&
		    [object readIntoBuffer: &buffer length: 1] == 1 &&
		    buffer == '0')
			[_testsAppDelegate
			    outputSuccess: @"-[observe] with data ready to read"
				 inModule: module];
		else {
			[_testsAppDelegate
			    outputFailure: @"-[observe] with data ready to read"
				 inModule: module];
			_fails++;
		}

		[_client close];

		[_testsAppDelegate
		    outputTesting: @"-[observe] with closed connection"
			 inModule: module];

		break;
	case 2:
		if (object == _accepted &&
		    [object readIntoBuffer: &buffer length: 1] == 0)

			[_testsAppDelegate
			    outputSuccess: @"-[observe] with closed connection"
				 inModule: module];
		else {
			[_testsAppDelegate
			    outputFailure: @"-[observe] with closed connection"
				 inModule: module];
			_fails++;
		}

		break;
	default:
		OFEnsure(0);
	}
}
@end

@implementation TestsAppDelegate (OFKernelEventObserverTests)
- (void)kernelEventObserverTestsWithClass: (Class)class
{

Modified tests/OFListTests.m from [21b92f7bc2] to [43f56ef79d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFList";
static OFString *strings[] = {
	@"Foo",
	@"Bar",
	@"Baz"
};

@implementation TestsAppDelegate (OFListTests)
- (void)listTests
{
	void *pool = objc_autoreleasePoolPush();
	OFList *list;
	OFEnumerator *enumerator;
	of_list_object_t *loe;
	OFString *obj;
	size_t i;
	bool ok;

	TEST(@"+[list]", (list = [OFList list]))

	TEST(@"-[appendObject:]", [list appendObject: strings[0]] &&
	    [list appendObject: strings[1]] && [list appendObject: strings[2]])

	TEST(@"-[firstListObject]",
	    [list.firstListObject->object isEqual: strings[0]])

	TEST(@"-[firstListObject]->next",

	    [list.firstListObject->next->object isEqual: strings[1]])

	TEST(@"-[lastListObject]",
	    [list.lastListObject->object isEqual: strings[2]])

	TEST(@"-[lastListObject]->previous",

	    [list.lastListObject->previous->object isEqual: strings[1]])

	TEST(@"-[removeListObject:]",
	    R([list removeListObject: list.lastListObject]) &&
	    [list.lastListObject->object isEqual: strings[1]] &&
	    R([list removeListObject: list.firstListObject]) &&
	    [list.firstListObject->object isEqual: list.lastListObject->object])

	TEST(@"-[insertObject:beforeListObject:]",
	    [list insertObject: strings[0]
	      beforeListObject: list.lastListObject] &&
	    [list.lastListObject->previous->object isEqual: strings[0]])

	TEST(@"-[insertObject:afterListObject:]",
	    [list insertObject: strings[2]
	       afterListObject: list.firstListObject->next] &&
	    [list.lastListObject->object isEqual: strings[2]])

	TEST(@"-[count]", list.count == 3)

	TEST(@"-[containsObject:]",
	    [list containsObject: strings[1]] &&
	    ![list containsObject: @"nonexistent"])

	TEST(@"-[containsObjectIdenticalTo:]",
	    [list containsObjectIdenticalTo: strings[1]] &&
	    ![list containsObjectIdenticalTo:
	    [OFString stringWithString: strings[1]]])

	TEST(@"-[copy]", (list = [[list copy] autorelease]) &&
	    [list.firstListObject->object isEqual: strings[0]] &&

	    [list.firstListObject->next->object isEqual: strings[1]] &&
	    [list.lastListObject->object isEqual: strings[2]])

	TEST(@"-[isEqual:]", [list isEqual: [[list copy] autorelease]])

	TEST(@"-[description]",
	    [list.description isEqual: @"[\n\tFoo,\n\tBar,\n\tBaz\n]"])

	TEST(@"-[objectEnumerator]", (enumerator = [list objectEnumerator]))

	loe = list.firstListObject;
	i = 0;
	ok = true;
	while ((obj = [enumerator nextObject]) != nil) {
		if (![obj isEqual: loe->object])
			ok = false;

		loe = loe->next;
		i++;
	}

	if (list.count != i)
		ok = false;

	TEST(@"OFEnumerator's -[nextObject]", ok);

	[list removeListObject: list.firstListObject];

	EXPECT_EXCEPTION(@"Detection of mutation during enumeration",
	    OFEnumerationMutationException, [enumerator nextObject])

	[list prependObject: strings[0]];

	loe = list.firstListObject;
	i = 0;
	ok = true;

	for (OFString *object in list) {
		if (![object isEqual: loe->object])
			ok = false;

		loe = loe->next;
		i++;
	}

	if (list.count != i)
		ok = false;

	TEST(@"Fast Enumeration", ok)

	ok = false;
	@try {
		for (OFString *object in list) {
			(void)object;

			[list removeListObject: list.lastListObject];
		}
	} @catch (OFEnumerationMutationException *e) {
		ok = true;
	}

	TEST(@"Detection of mutation during Fast Enumeration", ok)

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|












|
|








|
|

|
>
|

|
|

|
>
|

|
|
|
|
|

|
|
|
|

|

|
|













|
>
|
|








|


|
|


|








|






|



|
|


|










|
|

|










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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFList";
static OFString *strings[] = {
	@"Foo",
	@"Bar",
	@"Baz"
};

@implementation TestsAppDelegate (OFListTests)
- (void)listTests
{
	void *pool = objc_autoreleasePoolPush();
	OFList *list;
	OFEnumerator *enumerator;
	OFListItem iter;
	OFString *object;
	size_t i;
	bool ok;

	TEST(@"+[list]", (list = [OFList list]))

	TEST(@"-[appendObject:]", [list appendObject: strings[0]] &&
	    [list appendObject: strings[1]] && [list appendObject: strings[2]])

	TEST(@"-[firstListItem]",
	    [OFListItemObject(list.firstListItem) isEqual: strings[0]])

	TEST(@"OFListItemNext()",
	    [OFListItemObject(OFListItemNext(list.firstListItem))
	    isEqual: strings[1]])

	TEST(@"-[lastListItem]",
	    [OFListItemObject(list.lastListItem) isEqual: strings[2]])

	TEST(@"OFListItemPrevious()",
	    [OFListItemObject(OFListItemPrevious(list.lastListItem))
	    isEqual: strings[1]])

	TEST(@"-[removeListItem:]",
	    R([list removeListItem: list.lastListItem]) &&
	    [list.lastObject isEqual: strings[1]] &&
	    R([list removeListItem: list.firstListItem]) &&
	    [list.firstObject isEqual: list.lastObject])

	TEST(@"-[insertObject:beforeListItem:]",
	    [list insertObject: strings[0] beforeListItem: list.lastListItem] &&
	    [OFListItemObject(OFListItemPrevious(list.lastListItem))
	    isEqual: strings[0]])

	TEST(@"-[insertObject:afterListItem:]",
	    [list insertObject: strings[2]
		 afterListItem: OFListItemNext(list.firstListItem)] &&
	    [list.lastObject isEqual: strings[2]])

	TEST(@"-[count]", list.count == 3)

	TEST(@"-[containsObject:]",
	    [list containsObject: strings[1]] &&
	    ![list containsObject: @"nonexistent"])

	TEST(@"-[containsObjectIdenticalTo:]",
	    [list containsObjectIdenticalTo: strings[1]] &&
	    ![list containsObjectIdenticalTo:
	    [OFString stringWithString: strings[1]]])

	TEST(@"-[copy]", (list = [[list copy] autorelease]) &&
	    [list.firstObject isEqual: strings[0]] &&
	    [OFListItemObject(OFListItemNext(list.firstListItem))
	    isEqual: strings[1]] &&
	    [list.lastObject isEqual: strings[2]])

	TEST(@"-[isEqual:]", [list isEqual: [[list copy] autorelease]])

	TEST(@"-[description]",
	    [list.description isEqual: @"[\n\tFoo,\n\tBar,\n\tBaz\n]"])

	TEST(@"-[objectEnumerator]", (enumerator = [list objectEnumerator]))

	iter = list.firstListItem;
	i = 0;
	ok = true;
	while ((object = [enumerator nextObject]) != nil) {
		if (![object isEqual: OFListItemObject(iter)])
			ok = false;

		iter = OFListItemNext(iter);
		i++;
	}

	if (list.count != i)
		ok = false;

	TEST(@"OFEnumerator's -[nextObject]", ok);

	[list removeListItem: list.firstListItem];

	EXPECT_EXCEPTION(@"Detection of mutation during enumeration",
	    OFEnumerationMutationException, [enumerator nextObject])

	[list prependObject: strings[0]];

	iter = list.firstListItem;
	i = 0;
	ok = true;

	for (OFString *object_ in list) {
		if (![object_ isEqual: OFListItemObject(iter)])
			ok = false;

		iter = OFListItemNext(iter);
		i++;
	}

	if (list.count != i)
		ok = false;

	TEST(@"Fast Enumeration", ok)

	ok = false;
	@try {
		for (OFString *object_ in list) {
			(void)object_;

			[list removeListItem: list.lastListItem];
		}
	} @catch (OFEnumerationMutationException *e) {
		ok = true;
	}

	TEST(@"Detection of mutation during Fast Enumeration", ok)

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFLocaleTests.m from [dbaa1191c5] to [08f116fb95].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#import "TestsAppDelegate.h"

@implementation TestsAppDelegate (OFLocaleTests)
- (void)localeTests
{
	void *pool = objc_autoreleasePoolPush();

	[of_stdout setForegroundColor: [OFColor lime]];

	[of_stdout writeFormat: @"[OFLocale] Language: %@\n",
	    [OFLocale language]];

	[of_stdout writeFormat: @"[OFLocale] Territory: %@\n",
	    [OFLocale territory]];

	[of_stdout writeFormat: @"[OFLocale] Encoding: %@\n",
	    of_string_name_of_encoding([OFLocale encoding])];

	[of_stdout writeFormat: @"[OFLocale] Decimal point: %@\n",
	    [OFLocale decimalPoint]];

	objc_autoreleasePoolPop(pool);
}
@end







|

|


|


|
|

|





18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#import "TestsAppDelegate.h"

@implementation TestsAppDelegate (OFLocaleTests)
- (void)localeTests
{
	void *pool = objc_autoreleasePoolPush();

	[OFStdOut setForegroundColor: [OFColor lime]];

	[OFStdOut writeFormat: @"[OFLocale] Language: %@\n",
	    [OFLocale language]];

	[OFStdOut writeFormat: @"[OFLocale] Territory: %@\n",
	    [OFLocale territory]];

	[OFStdOut writeFormat: @"[OFLocale] Encoding: %@\n",
	    OFStringEncodingName([OFLocale encoding])];

	[OFStdOut writeFormat: @"[OFLocale] Decimal point: %@\n",
	    [OFLocale decimalPoint]];

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFMD5HashTests.m from [ab5377abbe] to [6eb26d1d7b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFMD5Hash";

const uint8_t testfile_md5[16] =
	"\x00\x8B\x9D\x1B\x58\xDF\xF8\xFE\xEE\xF3\xAE\x8D\xBB\x68\x2D\x38";

@implementation TestsAppDelegate (OFMD5HashTests)
- (void)MD5HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFMD5Hash *md5, *copy;
	OFFile *f = [OFFile fileWithPath: @"testfile.bin"
				    mode: @"r"];

	TEST(@"+[cryptoHashWithAllowsSwappableMemory:]",
	    (md5 = [OFMD5Hash cryptoHashWithAllowsSwappableMemory: true]))

	while (!f.atEndOfStream) {
		char buf[64];
		size_t len = [f readIntoBuffer: buf
					length: 64];
		[md5 updateWithBuffer: buf
			       length: len];
	}
	[f close];

	TEST(@"-[copy]", (copy = [[md5 copy] autorelease]))



	TEST(@"-[digest]",
	    memcmp(md5.digest, testfile_md5, 16) == 0 &&
	    memcmp(copy.digest, testfile_md5, 16) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length]", OFHashAlreadyCalculatedException,
	    [md5 updateWithBuffer: ""
			   length: 1])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|

|
|





|
|
<

|
|

|
|
|
<
|
<

|

|

>
>

|
|



|
<




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFMD5Hash";

const uint8_t testFileMD5[16] =
    "\x00\x8B\x9D\x1B\x58\xDF\xF8\xFE\xEE\xF3\xAE\x8D\xBB\x68\x2D\x38";

@implementation TestsAppDelegate (OFMD5HashTests)
- (void)MD5HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFMD5Hash *MD5, *MD5Copy;
	OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"];


	TEST(@"+[hashWithAllowsSwappableMemory:]",
	    (MD5 = [OFMD5Hash hashWithAllowsSwappableMemory: true]))

	while (!file.atEndOfStream) {
		char buffer[64];
		size_t length = [file readIntoBuffer: buffer length: 64];

		[MD5 updateWithBuffer: buffer length: length];

	}
	[file close];

	TEST(@"-[copy]", (MD5Copy = [[MD5 copy] autorelease]))

	TEST(@"-[calculate]", R([MD5 calculate]) && R([MD5Copy calculate]))

	TEST(@"-[digest]",
	    memcmp(MD5.digest, testFileMD5, 16) == 0 &&
	    memcmp(MD5Copy.digest, testFileMD5, 16) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length]", OFHashAlreadyCalculatedException,
	    [MD5 updateWithBuffer: "" length: 1])


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFMethodSignatureTests.m from [9cc11e0ceb] to [8b3d41a60c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H)
# include <complex.h>
#endif

#import "TestsAppDelegate.h"

static OFString *module = @"OFMethodSignature";

struct test1_struct {
	char c;
	int i;
	char d;
};

struct test2_struct {
	char c;
	struct {
		short s;
		int i;
	} st;
	union {
		char c;
		int i;
	} u;
	double d;
};

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H)
struct test3_struct {
	char c;
	complex double cd;
};
#endif

union test3_union {
	char c;
	int i;
	double d;
};

union test4_union {
	char c;
	struct {
		short x, y;
	} st;
	int i;
	union {
		float f;
		double d;
	} u;
};

@implementation TestsAppDelegate (OFMethodSignatureTests)
- (void)methodSignatureTests
{
	void *pool = objc_autoreleasePoolPush();
	OFMethodSignature *ms;

	TEST(@"-[signatureWithObjCTypes:] #1",
	    (ms = [OFMethodSignature signatureWithObjCTypes:
	    "i28@0:8S16*20"]) && ms.numberOfArguments == 4 &&

	    strcmp(ms.methodReturnType, "i") == 0 &&
	    strcmp([ms argumentTypeAtIndex: 0], "@") == 0 &&
	    strcmp([ms argumentTypeAtIndex: 1], ":") == 0 &&
	    strcmp([ms argumentTypeAtIndex: 2], "S") == 0 &&
	    strcmp([ms argumentTypeAtIndex: 3], "*") == 0 &&

	    ms.frameLength == 28 && [ms argumentOffsetAtIndex: 0] == 0 &&
	    [ms argumentOffsetAtIndex: 1] == 8 &&
	    [ms argumentOffsetAtIndex: 2] == 16 &&
	    [ms argumentOffsetAtIndex: 3] == 20)

	TEST(@"-[signatureWithObjCTypes:] #2",
	    (ms = [OFMethodSignature signatureWithObjCTypes:
	    "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}24@0:8"
	    "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}16"]) &&
	    ms.numberOfArguments == 3 && strcmp(ms.methodReturnType,

	    "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}") == 0 &&
	    strcmp([ms argumentTypeAtIndex: 0], "@") == 0 &&
	    strcmp([ms argumentTypeAtIndex: 1], ":") == 0 &&
	    strcmp([ms argumentTypeAtIndex: 2],
	    "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}") == 0 &&

	    ms.frameLength == 24 && [ms argumentOffsetAtIndex: 0] == 0 &&
	    [ms argumentOffsetAtIndex: 1] == 8 &&
	    [ms argumentOffsetAtIndex: 2] == 16)

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #3",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: "{ii"])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #4",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: ""])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #5",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: "0"])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #6",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: "{{}0"])

	TEST(@"of_sizeof_type_encoding() #1",
	    of_sizeof_type_encoding(@encode(struct test1_struct)) ==
	    sizeof(struct test1_struct))

	TEST(@"of_sizeof_type_encoding() #2",
	    of_sizeof_type_encoding(@encode(struct test2_struct)) ==
	    sizeof(struct test2_struct))

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) && \
    OF_GCC_VERSION >= 402
	TEST(@"of_sizeof_type_encoding() #3",
	    of_sizeof_type_encoding(@encode(struct test3_struct)) ==
	    sizeof(struct test3_struct))
#endif

	TEST(@"of_sizeof_type_encoding() #4",
	    of_sizeof_type_encoding(@encode(union test3_union)) ==
	    sizeof(union test3_union))

	TEST(@"of_sizeof_type_encoding() #5",
	    of_sizeof_type_encoding(@encode(union test4_union)) ==
	    sizeof(union test4_union))

	TEST(@"of_sizeof_type_encoding() #6",
	    of_sizeof_type_encoding(@encode(struct test1_struct [5])) ==
	    sizeof(struct test1_struct [5]))

	TEST(@"of_alignof_type_encoding() #1",
	    of_alignof_type_encoding(@encode(struct test1_struct)) ==
	    OF_ALIGNOF(struct test1_struct))

	TEST(@"of_alignof_type_encoding() #2",
	    of_alignof_type_encoding(@encode(struct test2_struct)) ==
	    OF_ALIGNOF(struct test2_struct))

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) && \
    OF_GCC_VERSION >= 402
	TEST(@"of_alignof_type_encoding() #3",
	    of_alignof_type_encoding(@encode(struct test3_struct)) ==
	    OF_ALIGNOF(struct test3_struct))
#endif

	TEST(@"of_alignof_type_encoding() #4",
	    of_alignof_type_encoding(@encode(union test3_union)) ==
	    OF_ALIGNOF(union test3_union))

	TEST(@"of_alignof_type_encoding() #5",
	    of_alignof_type_encoding(@encode(union test4_union)) ==
	    OF_ALIGNOF(union test4_union))

	TEST(@"of_alignof_type_encoding() #6",
	    of_alignof_type_encoding(@encode(struct test1_struct [5])) ==
	    OF_ALIGNOF(struct test1_struct [5]))

	objc_autoreleasePoolPop(pool);
}
@end







|

|





|













|





|





|















|


|
|
>
|
|
|
|
|
>
|
|
|
|


|


|
>

|
|
|

>
|
|
|

















|
|
|

|
|
|



|
|
|


|
|
|

|
|
|

|
|
|

|
|
|

|
|
|



|
|
|


|
|
|

|
|
|

|
|
|




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

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H)
# include <complex.h>
#endif

#import "TestsAppDelegate.h"

static OFString *const module = @"OFMethodSignature";

struct Test1Struct {
	char c;
	int i;
	char d;
};

struct Test2Struct {
	char c;
	struct {
		short s;
		int i;
	} st;
	union {
		char c;
		int i;
	} u;
	double d;
};

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H)
struct Test3Struct {
	char c;
	complex double cd;
};
#endif

union Test3Union {
	char c;
	int i;
	double d;
};

union Test4Union {
	char c;
	struct {
		short x, y;
	} st;
	int i;
	union {
		float f;
		double d;
	} u;
};

@implementation TestsAppDelegate (OFMethodSignatureTests)
- (void)methodSignatureTests
{
	void *pool = objc_autoreleasePoolPush();
	OFMethodSignature *methodSignature;

	TEST(@"-[signatureWithObjCTypes:] #1",
	    (methodSignature = [OFMethodSignature signatureWithObjCTypes:
	    "i28@0:8S16*20"]) &&
	    methodSignature.numberOfArguments == 4 &&
	    strcmp(methodSignature.methodReturnType, "i") == 0 &&
	    strcmp([methodSignature argumentTypeAtIndex: 0], "@") == 0 &&
	    strcmp([methodSignature argumentTypeAtIndex: 1], ":") == 0 &&
	    strcmp([methodSignature argumentTypeAtIndex: 2], "S") == 0 &&
	    strcmp([methodSignature argumentTypeAtIndex: 3], "*") == 0 &&
	    methodSignature.frameLength == 28 &&
	    [methodSignature argumentOffsetAtIndex: 0] == 0 &&
	    [methodSignature argumentOffsetAtIndex: 1] == 8 &&
	    [methodSignature argumentOffsetAtIndex: 2] == 16 &&
	    [methodSignature argumentOffsetAtIndex: 3] == 20)

	TEST(@"-[signatureWithObjCTypes:] #2",
	    (methodSignature = [OFMethodSignature signatureWithObjCTypes:
	    "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}24@0:8"
	    "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}16"]) &&
	    methodSignature.numberOfArguments == 3 &&
	    strcmp(methodSignature.methodReturnType,
	    "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}") == 0 &&
	    strcmp([methodSignature argumentTypeAtIndex: 0], "@") == 0 &&
	    strcmp([methodSignature argumentTypeAtIndex: 1], ":") == 0 &&
	    strcmp([methodSignature argumentTypeAtIndex: 2],
	    "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}") == 0 &&
	    methodSignature.frameLength == 24 &&
	    [methodSignature argumentOffsetAtIndex: 0] == 0 &&
	    [methodSignature argumentOffsetAtIndex: 1] == 8 &&
	    [methodSignature argumentOffsetAtIndex: 2] == 16)

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #3",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: "{ii"])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #4",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: ""])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #5",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: "0"])

	EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #6",
	    OFInvalidFormatException,
	    [OFMethodSignature signatureWithObjCTypes: "{{}0"])

	TEST(@"OFSizeOfTypeEncoding() #1",
	    OFSizeOfTypeEncoding(@encode(struct Test1Struct)) ==
	    sizeof(struct Test1Struct))

	TEST(@"OFSizeOfTypeEncoding() #2",
	    OFSizeOfTypeEncoding(@encode(struct Test2Struct)) ==
	    sizeof(struct Test2Struct))

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) && \
    OF_GCC_VERSION >= 402
	TEST(@"OFSizeOfTypeEncoding() #3",
	    OFSizeOfTypeEncoding(@encode(struct Test3Struct)) ==
	    sizeof(struct Test3Struct))
#endif

	TEST(@"OFSizeOfTypeEncoding() #4",
	    OFSizeOfTypeEncoding(@encode(union Test3Union)) ==
	    sizeof(union Test3Union))

	TEST(@"OFSizeOfTypeEncoding() #5",
	    OFSizeOfTypeEncoding(@encode(union Test4Union)) ==
	    sizeof(union Test4Union))

	TEST(@"OFSizeOfTypeEncoding() #6",
	    OFSizeOfTypeEncoding(@encode(struct Test1Struct [5])) ==
	    sizeof(struct Test1Struct [5]))

	TEST(@"OFAlignmentOfTypeEncoding() #1",
	    OFAlignmentOfTypeEncoding(@encode(struct Test1Struct)) ==
	    OF_ALIGNOF(struct Test1Struct))

	TEST(@"OFAlignmentOfTypeEncoding() #2",
	    OFAlignmentOfTypeEncoding(@encode(struct Test2Struct)) ==
	    OF_ALIGNOF(struct Test2Struct))

#if !defined(__STDC_NO_COMPLEX__) && defined(HAVE_COMPLEX_H) && \
    OF_GCC_VERSION >= 402
	TEST(@"OFAlignmentOfTypeEncoding() #3",
	    OFAlignmentOfTypeEncoding(@encode(struct Test3Struct)) ==
	    OF_ALIGNOF(struct Test3Struct))
#endif

	TEST(@"OFAlignmentOfTypeEncoding() #4",
	    OFAlignmentOfTypeEncoding(@encode(union Test3Union)) ==
	    OF_ALIGNOF(union Test3Union))

	TEST(@"OFAlignmentOfTypeEncoding() #5",
	    OFAlignmentOfTypeEncoding(@encode(union Test4Union)) ==
	    OF_ALIGNOF(union Test4Union))

	TEST(@"OFAlignmentOfTypeEncoding() #6",
	    OFAlignmentOfTypeEncoding(@encode(struct Test1Struct [5])) ==
	    OF_ALIGNOF(struct Test1Struct [5]))

	objc_autoreleasePoolPop(pool);
}
@end

Added tests/OFNotificationCenterTests.m version [4d3ddfb6ba].































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFNotificationCenter";
static const OFNotificationName notificationName =
    @"OFNotificationCenterTestName";
static const OFNotificationName otherNotificationName =
    @"OFNotificationCenterTestOtherName";

@interface OFNotificationCenterTest: OFObject
{
@public
	id _expectedObject;
	int _received;
}

- (void)handleNotification: (OFNotification *)notification;
@end

@implementation OFNotificationCenterTest
- (void)handleNotification: (OFNotification *)notification
{
	OFEnsure([notification.name isEqual: notificationName]);
	OFEnsure(_expectedObject == nil ||
	    notification.object == _expectedObject);

	_received++;
}
@end

@implementation TestsAppDelegate (OFNotificationCenterTests)
- (void)notificationCenterTests
{
	void *pool = objc_autoreleasePoolPush();
	OFNotificationCenter *center = [OFNotificationCenter defaultCenter];
	OFNotificationCenterTest *test1, *test2, *test3, *test4;
	OFNotification *notification;

	test1 =
	    [[[OFNotificationCenterTest alloc] init] autorelease];
	test1->_expectedObject = self;
	test2 =
	    [[[OFNotificationCenterTest alloc] init] autorelease];
	test3 =
	    [[[OFNotificationCenterTest alloc] init] autorelease];
	test3->_expectedObject = self;
	test4 =
	    [[[OFNotificationCenterTest alloc] init] autorelease];

	/* First one intentionally added twice to test deduplication. */
	TEST(@"-[addObserver:selector:name:object:]",
	    R([center addObserver: test1
			 selector: @selector(handleNotification:)
			     name: notificationName
			   object: self]) &&
	    R([center addObserver: test1
			 selector: @selector(handleNotification:)
			     name: notificationName
			   object: self]) &&
	    R([center addObserver: test2
			 selector: @selector(handleNotification:)
			     name: notificationName
			   object: nil]) &&
	    R([center addObserver: test3
			 selector: @selector(handleNotification:)
			     name: otherNotificationName
			   object: self]) &&
	    R([center addObserver: test4
			 selector: @selector(handleNotification:)
			     name: otherNotificationName
			   object: nil]))

	notification = [OFNotification notificationWithName: notificationName
						     object: nil];
	TEST(@"-[postNotification:] #1",
	    R([center postNotification: notification]) &&
	    test1->_received == 0 && test2->_received == 1 &&
	    test3->_received == 0 && test4->_received == 0)

	notification = [OFNotification notificationWithName: notificationName
						     object: self];
	TEST(@"-[postNotification:] #2",
	    R([center postNotification: notification]) &&
	    test1->_received == 1 && test2->_received == 2 &&
	    test3->_received == 0 && test4->_received == 0)

	notification = [OFNotification notificationWithName: notificationName
						     object: @"foo"];
	TEST(@"-[postNotification:] #3",
	    R([center postNotification: notification]) &&
	    test1->_received == 1 && test2->_received == 3 &&
	    test3->_received == 0 && test4->_received == 0)

#ifdef OF_HAVE_BLOCKS
	__block bool received = false;
	OFNotificationCenterHandle *handle;

	notification = [OFNotification notificationWithName: notificationName
						     object: self];
	TEST(@"-[addObserverForName:object:usingBlock:]",
	    (handle = [center addObserverForName: notificationName
					  object: self
				      usingBlock: ^ (OFNotification *notif) {
		OFEnsure(notif == notification && !received);
		received = true;
	    }]) && R([center postNotification: notification]) && received &&
	    test1->_received == 2 && test2->_received == 4 &&
	    test3->_received == 0 && test4->_received == 0)

	/* Act like the block test didn't happen. */
	[center removeObserver: handle];
	test1->_received--;
	test2->_received--;
#endif

	TEST(@"-[removeObserver:selector:name:object:]",
	    R([center removeObserver: test1
			    selector: @selector(handleNotification:)
				name: notificationName
			      object: self]) &&
	    R([center removeObserver: test2
			    selector: @selector(handleNotification:)
				name: notificationName
			      object: nil]) &&
	    R([center removeObserver: test3
			    selector: @selector(handleNotification:)
				name: otherNotificationName
			      object: self]) &&
	    R([center removeObserver: test4
			    selector: @selector(handleNotification:)
				name: otherNotificationName
			      object: nil]))

	notification = [OFNotification notificationWithName: notificationName
						     object: self];
	TEST(@"-[postNotification:] with no observers",
	    R([center postNotification: notification]) &&
	    test1->_received == 1 && test2->_received == 3 &&
	    test3->_received == 0 && test4->_received == 0)

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFNumberTests.m from [c5bef94b63] to [c4180587af].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFNumber";

@implementation TestsAppDelegate (OFNumberTests)
- (void)numberTests
{
	void *pool = objc_autoreleasePoolPush();
	OFNumber *num;

	TEST(@"+[numberWithLongLong:]",
	    (num = [OFNumber numberWithLongLong: 123456789]))

	TEST(@"-[isEqual:]",
	    [num isEqual: [OFNumber numberWithLong: 123456789]])

	TEST(@"-[hash]", num.hash == 0x82D8BC42)

	TEST(@"-[charValue]", num.charValue == 21)

	TEST(@"-[doubleValue]", num.doubleValue == 123456789.L)

	TEST(@"signed char minimum & maximum unmodified",
	    (num = [OFNumber numberWithChar: SCHAR_MIN]) &&
	    num.charValue == SCHAR_MIN &&
	    (num = [OFNumber numberWithChar: SCHAR_MAX]) &&
	    num.charValue == SCHAR_MAX)

	TEST(@"short minimum & maximum unmodified",
	    (num = [OFNumber numberWithShort: SHRT_MIN]) &&
	    num.shortValue == SHRT_MIN &&
	    (num = [OFNumber numberWithShort: SHRT_MAX]) &&
	    num.shortValue == SHRT_MAX)

	TEST(@"int minimum & maximum unmodified",
	    (num = [OFNumber numberWithInt: INT_MIN]) &&
	    num.intValue == INT_MIN &&
	    (num = [OFNumber numberWithInt: INT_MAX]) &&
	    num.intValue == INT_MAX)

	TEST(@"long minimum & maximum unmodified",
	    (num = [OFNumber numberWithLong: LONG_MIN]) &&
	    num.longValue == LONG_MIN &&
	    (num = [OFNumber numberWithLong: LONG_MAX]) &&
	    num.longValue == LONG_MAX)

	TEST(@"long long minimum & maximum unmodified",
	    (num = [OFNumber numberWithLongLong: LLONG_MIN]) &&
	    num.longLongValue == LLONG_MIN &&
	    (num = [OFNumber numberWithLongLong: LLONG_MAX]) &&
	    num.longLongValue == LLONG_MAX)

	TEST(@"unsigned char maximum unmodified",
	    (num = [OFNumber numberWithUnsignedChar: UCHAR_MAX]) &&
	    num.unsignedCharValue == UCHAR_MAX)

	TEST(@"unsigned short maximum unmodified",
	    (num = [OFNumber numberWithUnsignedShort: USHRT_MAX]) &&
	    num.unsignedShortValue == USHRT_MAX)

	TEST(@"unsigned int maximum unmodified",
	    (num = [OFNumber numberWithUnsignedInt: UINT_MAX]) &&
	    num.unsignedIntValue == UINT_MAX)

	TEST(@"unsigned long maximum unmodified",
	    (num = [OFNumber numberWithUnsignedLong: ULONG_MAX]) &&
	    num.unsignedLongValue == ULONG_MAX)

	TEST(@"unsigned long long maximum unmodified",
	    (num = [OFNumber numberWithUnsignedLongLong: ULLONG_MAX]) &&
	    num.unsignedLongLongValue == ULLONG_MAX)

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|





|


|


|

|

|

|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|


|
|


|
|


|
|


|
|




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.
 */

#include "config.h"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFNumber";

@implementation TestsAppDelegate (OFNumberTests)
- (void)numberTests
{
	void *pool = objc_autoreleasePoolPush();
	OFNumber *number;

	TEST(@"+[numberWithLongLong:]",
	    (number = [OFNumber numberWithLongLong: 123456789]))

	TEST(@"-[isEqual:]",
	    [number isEqual: [OFNumber numberWithLong: 123456789]])

	TEST(@"-[hash]", number.hash == 0x82D8BC42)

	TEST(@"-[charValue]", number.charValue == 21)

	TEST(@"-[doubleValue]", number.doubleValue == 123456789.L)

	TEST(@"signed char minimum & maximum unmodified",
	    (number = [OFNumber numberWithChar: SCHAR_MIN]) &&
	    number.charValue == SCHAR_MIN &&
	    (number = [OFNumber numberWithChar: SCHAR_MAX]) &&
	    number.charValue == SCHAR_MAX)

	TEST(@"short minimum & maximum unmodified",
	    (number = [OFNumber numberWithShort: SHRT_MIN]) &&
	    number.shortValue == SHRT_MIN &&
	    (number = [OFNumber numberWithShort: SHRT_MAX]) &&
	    number.shortValue == SHRT_MAX)

	TEST(@"int minimum & maximum unmodified",
	    (number = [OFNumber numberWithInt: INT_MIN]) &&
	    number.intValue == INT_MIN &&
	    (number = [OFNumber numberWithInt: INT_MAX]) &&
	    number.intValue == INT_MAX)

	TEST(@"long minimum & maximum unmodified",
	    (number = [OFNumber numberWithLong: LONG_MIN]) &&
	    number.longValue == LONG_MIN &&
	    (number = [OFNumber numberWithLong: LONG_MAX]) &&
	    number.longValue == LONG_MAX)

	TEST(@"long long minimum & maximum unmodified",
	    (number = [OFNumber numberWithLongLong: LLONG_MIN]) &&
	    number.longLongValue == LLONG_MIN &&
	    (number = [OFNumber numberWithLongLong: LLONG_MAX]) &&
	    number.longLongValue == LLONG_MAX)

	TEST(@"unsigned char maximum unmodified",
	    (number = [OFNumber numberWithUnsignedChar: UCHAR_MAX]) &&
	    number.unsignedCharValue == UCHAR_MAX)

	TEST(@"unsigned short maximum unmodified",
	    (number = [OFNumber numberWithUnsignedShort: USHRT_MAX]) &&
	    number.unsignedShortValue == USHRT_MAX)

	TEST(@"unsigned int maximum unmodified",
	    (number = [OFNumber numberWithUnsignedInt: UINT_MAX]) &&
	    number.unsignedIntValue == UINT_MAX)

	TEST(@"unsigned long maximum unmodified",
	    (number = [OFNumber numberWithUnsignedLong: ULONG_MAX]) &&
	    number.unsignedLongValue == ULONG_MAX)

	TEST(@"unsigned long long maximum unmodified",
	    (number = [OFNumber numberWithUnsignedLongLong: ULLONG_MAX]) &&
	    number.unsignedLongLongValue == ULLONG_MAX)

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFObjectTests.m from [5ded62534c] to [fcfeee4a72].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

#if (defined(OF_DRAGONFLYBSD) && defined(__LP64__)) || defined(OF_NINTENDO_3DS)
# define TOO_BIG (SIZE_MAX / 3)
#else
# define TOO_BIG (SIZE_MAX - 128)
#endif

static OFString *module = @"OFObject";

@interface MyObj: OFObject
{
	id _objectValue;
	Class _classValue;
	bool _boolValue;
	char _charValue;
	short _shortValue;
	int _intValue;







|

|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

#if (defined(OF_DRAGONFLYBSD) && defined(__LP64__)) || defined(OF_NINTENDO_3DS)
# define TOO_BIG (SIZE_MAX / 3)
#else
# define TOO_BIG (SIZE_MAX - 128)
#endif

static OFString *const module = @"OFObject";

@interface MyObject: OFObject
{
	id _objectValue;
	Class _classValue;
	bool _boolValue;
	char _charValue;
	short _shortValue;
	int _intValue;
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@property (nonatomic) unsigned int unsignedIntValue;
@property (nonatomic) unsigned long unsignedLongValue;
@property (nonatomic) unsigned long long unsignedLongLongValue;
@property (nonatomic) float floatValue;
@property (nonatomic) double doubleValue;
@end

@implementation MyObj
@synthesize objectValue = _objectValue, classValue = _classValue;
@synthesize boolValue = _boolValue, charValue = _charValue;
@synthesize shortValue = _shortValue, intValue = _intValue;
@synthesize longValue = _longValue, longLongValue = _longLongValue;
@synthesize unsignedCharValue = _unsignedCharValue;
@synthesize unsignedShortValue = _unsignedShortValue;
@synthesize unsignedIntValue = _unsignedIntValue;







|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
@property (nonatomic) unsigned int unsignedIntValue;
@property (nonatomic) unsigned long unsignedLongValue;
@property (nonatomic) unsigned long long unsignedLongLongValue;
@property (nonatomic) float floatValue;
@property (nonatomic) double doubleValue;
@end

@implementation MyObject
@synthesize objectValue = _objectValue, classValue = _classValue;
@synthesize boolValue = _boolValue, charValue = _charValue;
@synthesize shortValue = _shortValue, intValue = _intValue;
@synthesize longValue = _longValue, longLongValue = _longLongValue;
@synthesize unsignedCharValue = _unsignedCharValue;
@synthesize unsignedShortValue = _unsignedShortValue;
@synthesize unsignedIntValue = _unsignedIntValue;
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
}
@end

@implementation TestsAppDelegate (OFObjectTests)
- (void)objectTests
{
	void *pool = objc_autoreleasePoolPush();
	OFObject *obj = [[[OFObject alloc] init] autorelease];
	void *p, *q, *r;
	OFObject *o;
	MyObj *m;
	char *tmp;

	TEST(@"Allocating 4096 bytes",
	    (p = [obj allocMemoryWithSize: 4096]) != NULL)

	TEST(@"Freeing memory", R([obj freeMemory: p]))

	TEST(@"Allocating and freeing 4096 bytes 3 times",
	    (p = [obj allocMemoryWithSize: 4096]) != NULL &&
	    (q = [obj allocMemoryWithSize: 4096]) != NULL &&
	    (r = [obj allocMemoryWithSize: 4096]) != NULL &&
	    R([obj freeMemory: p]) && R([obj freeMemory: q]) &&
	    R([obj freeMemory: r]))

	tmp = [self allocMemoryWithSize: 1024];
	EXPECT_EXCEPTION(@"Detect freeing of memory not allocated by object",
	    OFMemoryNotPartOfObjectException, [obj freeMemory: tmp])

	EXPECT_EXCEPTION(@"Detect out of memory on alloc",
	    OFOutOfMemoryException, tmp = [obj allocMemoryWithSize: TOO_BIG])

	EXPECT_EXCEPTION(@"Detect out of memory on resize",
	    OFOutOfMemoryException,
	    {
		p = [obj allocMemoryWithSize: 1];
		p = [obj resizeMemory: p
				 size: TOO_BIG];
	    })
	[obj freeMemory: p];

	TEST(@"Allocate when trying to resize NULL",
	    (p = [obj resizeMemory: NULL
			      size: 1024]) != NULL)
	[obj freeMemory: p];

	EXPECT_EXCEPTION(@"Detect resizing of memory not allocated by object",
	    OFMemoryNotPartOfObjectException, tmp = [obj resizeMemory: tmp
								 size: 2048])
	[self freeMemory: tmp];

	TEST(@"+[description]",
	    [[OFObject description] isEqual: @"OFObject"] &&
	    [[MyObj description] isEqual: @"MyObj"])

	o = [[[OFObject alloc] init] autorelease];
	m = [[[MyObj alloc] init] autorelease];

	TEST(@"-[description]",
	    [o.description isEqual: @"<OFObject>"] &&
	    [m.description isEqual: @"<MyObj>"])

	m.objectValue = @"Hello";
	m.classValue = [m class];
	TEST(@"-[valueForKey:]",
	    [[m valueForKey: @"objectValue"] isEqual: @"Hello"] &&
	    [[m valueForKey: @"classValue"] isEqual: m.class] &&
	    [[m valueForKey: @"class"] isEqual: m.class])

	EXPECT_EXCEPTION(@"-[valueForKey:] with undefined key",
	    OFUndefinedKeyException, [m valueForKey: @"undefined"])

	TEST(@"-[setValue:forKey:]",
	    R([m setValue: @"World"
		   forKey: @"objectValue"]) &&
	    R([m setValue: [OFObject class]
		   forKey: @"classValue"]) &&
	    [m.objectValue isEqual: @"World"] &&
	    [m.classValue isEqual: [OFObject class]])

	EXPECT_EXCEPTION(@"-[setValue:forKey:] with undefined key",
	    OFUndefinedKeyException, [m setValue: @"x"
					  forKey: @"undefined"])

	m.boolValue = 1;
	m.charValue = 2;
	m.shortValue = 3;
	m.intValue = 4;
	m.longValue = 5;
	m.longLongValue = 6;
	m.unsignedCharValue = 7;
	m.unsignedShortValue = 8;
	m.unsignedIntValue = 9;
	m.unsignedLongValue = 10;
	m.unsignedLongLongValue = 11;
	m.floatValue = 12;
	m.doubleValue = 13;
	TEST(@"Auto-wrapping of -[valueForKey:]",
	    [[m valueForKey: @"boolValue"] isEqual:
	    [OFNumber numberWithBool: 1]] &&
	    [[m valueForKey: @"charValue"] isEqual:
	    [OFNumber numberWithChar: 2]] &&
	    [[m valueForKey: @"shortValue"] isEqual:
	    [OFNumber numberWithShort: 3]] &&
	    [[m valueForKey: @"intValue"] isEqual:
	    [OFNumber numberWithInt: 4]] &&
	    [[m valueForKey: @"longValue"] isEqual:
	    [OFNumber numberWithLong: 5]] &&
	    [[m valueForKey: @"longLongValue"] isEqual:
	    [OFNumber numberWithLongLong: 6]] &&
	    [[m valueForKey: @"unsignedCharValue"] isEqual:
	    [OFNumber numberWithUnsignedChar: 7]] &&
	    [[m valueForKey: @"unsignedShortValue"] isEqual:
	    [OFNumber numberWithUnsignedShort: 8]] &&
	    [[m valueForKey: @"unsignedIntValue"] isEqual:
	    [OFNumber numberWithUnsignedInt: 9]] &&
	    [[m valueForKey: @"unsignedLongValue"] isEqual:
	    [OFNumber numberWithUnsignedLong: 10]] &&
	    [[m valueForKey: @"unsignedLongLongValue"] isEqual:
	    [OFNumber numberWithUnsignedLongLong: 11]] &&
	    [[m valueForKey: @"floatValue"] isEqual:
	    [OFNumber numberWithFloat: 12]] &&
	    [[m valueForKey: @"doubleValue"] isEqual:
	    [OFNumber numberWithDouble: 13]])

	TEST(@"Auto-wrapping of -[setValue:forKey:]",
	    R([m setValue: [OFNumber numberWithBool: 0]
		   forKey: @"boolValue"]) &&
	    R([m setValue: [OFNumber numberWithChar: 10]
		   forKey: @"charValue"]) &&
	    R([m setValue: [OFNumber numberWithShort: 20]
		   forKey: @"shortValue"]) &&
	    R([m setValue: [OFNumber numberWithInt: 30]
		   forKey: @"intValue"]) &&
	    R([m setValue: [OFNumber numberWithLong: 40]
		   forKey: @"longValue"]) &&
	    R([m setValue: [OFNumber numberWithLongLong: 50]
		   forKey: @"longLongValue"]) &&
	    R([m setValue: [OFNumber numberWithUnsignedChar: 60]
		   forKey: @"unsignedCharValue"]) &&
	    R([m setValue: [OFNumber numberWithUnsignedShort: 70]
		   forKey: @"unsignedShortValue"]) &&
	    R([m setValue: [OFNumber numberWithUnsignedInt: 80]
		   forKey: @"unsignedIntValue"]) &&
	    R([m setValue: [OFNumber numberWithUnsignedLong: 90]
		   forKey: @"unsignedLongValue"]) &&
	    R([m setValue: [OFNumber numberWithUnsignedLongLong: 100]
		   forKey: @"unsignedLongLongValue"]) &&
	    R([m setValue: [OFNumber numberWithFloat: 110]
		   forKey: @"floatValue"]) &&
	    R([m setValue: [OFNumber numberWithDouble: 120]
		   forKey: @"doubleValue"]) &&
	    m.isBoolValue == 0 && m.charValue == 10 && m.shortValue == 20 &&

	    m.intValue == 30 && m.longValue == 40 && m.longLongValue == 50 &&

	    m.unsignedCharValue == 60 && m.unsignedShortValue == 70 &&

	    m.unsignedIntValue == 80 && m.unsignedLongValue == 90 &&
	    m.unsignedLongLongValue == 100 && m.floatValue == 110 &&

	    m.doubleValue == 120)

	EXPECT_EXCEPTION(@"Catch -[setValue:forKey:] with nil key for scalar",
	    OFInvalidArgumentException, [m setValue: (id _Nonnull)nil
					     forKey: @"intValue"])

	TEST(@"-[valueForKeyPath:]",
	    (m = [[[MyObj alloc] init] autorelease]) &&
	    (m.objectValue = [[[MyObj alloc] init] autorelease]) &&
	    R([m.objectValue
	    setObjectValue: [[[MyObj alloc] init] autorelease]]) &&
	    R([[m.objectValue objectValue] setDoubleValue: 0.5]) &&
	    [[m valueForKeyPath: @"objectValue.objectValue.doubleValue"]
	    doubleValue] == 0.5)

	TEST(@"[-setValue:forKeyPath:]",
	    R([m setValue: [OFNumber numberWithDouble: 0.75]
	       forKeyPath: @"objectValue.objectValue.doubleValue"]) &&
	    [[m.objectValue objectValue] doubleValue] == 0.75)

	objc_autoreleasePoolPop(pool);
}
@end







<
<
|
|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


|

|
|


|
|

|
|

|
|
|


|


<
|
<
|
|
|


|
|

|
|
|
|
|
|
|
|
|
|
|
|
|

|

|

|

|

|

|

|

|

|

|

|

|

|



|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
|
>
|
>
|
|
>
|


|
|


|
|
|
|
|
|



|
|
|




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
}
@end

@implementation TestsAppDelegate (OFObjectTests)
- (void)objectTests
{
	void *pool = objc_autoreleasePoolPush();


	OFObject *object;
	MyObject *myObject;








































	TEST(@"+[description]",
	    [[OFObject description] isEqual: @"OFObject"] &&
	    [[MyObject description] isEqual: @"MyObject"])

	object = [[[OFObject alloc] init] autorelease];
	myObject = [[[MyObject alloc] init] autorelease];

	TEST(@"-[description]",
	    [object.description isEqual: @"<OFObject>"] &&
	    [myObject.description isEqual: @"<MyObject>"])

	myObject.objectValue = @"Hello";
	myObject.classValue = myObject.class;
	TEST(@"-[valueForKey:]",
	    [[myObject valueForKey: @"objectValue"] isEqual: @"Hello"] &&
	    [[myObject valueForKey: @"classValue"] isEqual: myObject.class] &&
	    [[myObject valueForKey: @"class"] isEqual: myObject.class])

	EXPECT_EXCEPTION(@"-[valueForKey:] with undefined key",
	    OFUndefinedKeyException, [myObject valueForKey: @"undefined"])

	TEST(@"-[setValue:forKey:]",

	    R([myObject setValue: @"World" forKey: @"objectValue"]) &&

	    R([myObject setValue: [OFObject class] forKey: @"classValue"]) &&
	    [myObject.objectValue isEqual: @"World"] &&
	    [myObject.classValue isEqual: [OFObject class]])

	EXPECT_EXCEPTION(@"-[setValue:forKey:] with undefined key",
	    OFUndefinedKeyException,
	    [myObject setValue: @"x" forKey: @"undefined"])

	myObject.boolValue = 1;
	myObject.charValue = 2;
	myObject.shortValue = 3;
	myObject.intValue = 4;
	myObject.longValue = 5;
	myObject.longLongValue = 6;
	myObject.unsignedCharValue = 7;
	myObject.unsignedShortValue = 8;
	myObject.unsignedIntValue = 9;
	myObject.unsignedLongValue = 10;
	myObject.unsignedLongLongValue = 11;
	myObject.floatValue = 12;
	myObject.doubleValue = 13;
	TEST(@"Auto-wrapping of -[valueForKey:]",
	    [[myObject valueForKey: @"boolValue"] isEqual:
	    [OFNumber numberWithBool: 1]] &&
	    [[myObject valueForKey: @"charValue"] isEqual:
	    [OFNumber numberWithChar: 2]] &&
	    [[myObject valueForKey: @"shortValue"] isEqual:
	    [OFNumber numberWithShort: 3]] &&
	    [[myObject valueForKey: @"intValue"] isEqual:
	    [OFNumber numberWithInt: 4]] &&
	    [[myObject valueForKey: @"longValue"] isEqual:
	    [OFNumber numberWithLong: 5]] &&
	    [[myObject valueForKey: @"longLongValue"] isEqual:
	    [OFNumber numberWithLongLong: 6]] &&
	    [[myObject valueForKey: @"unsignedCharValue"] isEqual:
	    [OFNumber numberWithUnsignedChar: 7]] &&
	    [[myObject valueForKey: @"unsignedShortValue"] isEqual:
	    [OFNumber numberWithUnsignedShort: 8]] &&
	    [[myObject valueForKey: @"unsignedIntValue"] isEqual:
	    [OFNumber numberWithUnsignedInt: 9]] &&
	    [[myObject valueForKey: @"unsignedLongValue"] isEqual:
	    [OFNumber numberWithUnsignedLong: 10]] &&
	    [[myObject valueForKey: @"unsignedLongLongValue"] isEqual:
	    [OFNumber numberWithUnsignedLongLong: 11]] &&
	    [[myObject valueForKey: @"floatValue"] isEqual:
	    [OFNumber numberWithFloat: 12]] &&
	    [[myObject valueForKey: @"doubleValue"] isEqual:
	    [OFNumber numberWithDouble: 13]])

	TEST(@"Auto-wrapping of -[setValue:forKey:]",
	    R([myObject setValue: [OFNumber numberWithBool: 0]
			  forKey: @"boolValue"]) &&
	    R([myObject setValue: [OFNumber numberWithChar: 10]
			  forKey: @"charValue"]) &&
	    R([myObject setValue: [OFNumber numberWithShort: 20]
			  forKey: @"shortValue"]) &&
	    R([myObject setValue: [OFNumber numberWithInt: 30]
			  forKey: @"intValue"]) &&
	    R([myObject setValue: [OFNumber numberWithLong: 40]
			  forKey: @"longValue"]) &&
	    R([myObject setValue: [OFNumber numberWithLongLong: 50]
			  forKey: @"longLongValue"]) &&
	    R([myObject setValue: [OFNumber numberWithUnsignedChar: 60]
			  forKey: @"unsignedCharValue"]) &&
	    R([myObject setValue: [OFNumber numberWithUnsignedShort: 70]
			  forKey: @"unsignedShortValue"]) &&
	    R([myObject setValue: [OFNumber numberWithUnsignedInt: 80]
			  forKey: @"unsignedIntValue"]) &&
	    R([myObject setValue: [OFNumber numberWithUnsignedLong: 90]
			  forKey: @"unsignedLongValue"]) &&
	    R([myObject setValue: [OFNumber numberWithUnsignedLongLong: 100]
			  forKey: @"unsignedLongLongValue"]) &&
	    R([myObject setValue: [OFNumber numberWithFloat: 110]
			  forKey: @"floatValue"]) &&
	    R([myObject setValue: [OFNumber numberWithDouble: 120]
			  forKey: @"doubleValue"]) &&
	    myObject.isBoolValue == 0 && myObject.charValue == 10 &&
	    myObject.shortValue == 20 && myObject.intValue == 30 &&
	    myObject.longValue == 40 && myObject.longLongValue == 50 &&
	    myObject.unsignedCharValue == 60 &&
	    myObject.unsignedShortValue == 70 &&
	    myObject.unsignedIntValue == 80 &&
	    myObject.unsignedLongValue == 90 &&
	    myObject.unsignedLongLongValue == 100 &&
	    myObject.floatValue == 110 &&
	    myObject.doubleValue == 120)

	EXPECT_EXCEPTION(@"Catch -[setValue:forKey:] with nil key for scalar",
	    OFInvalidArgumentException,
	    [myObject setValue: (id _Nonnull)nil forKey: @"intValue"])

	TEST(@"-[valueForKeyPath:]",
	    (myObject = [[[MyObject alloc] init] autorelease]) &&
	    (myObject.objectValue = [[[MyObject alloc] init] autorelease]) &&
	    R([myObject.objectValue
	    setObjectValue: [[[MyObject alloc] init] autorelease]]) &&
	    R([[myObject.objectValue objectValue] setDoubleValue: 0.5]) &&
	    [[myObject valueForKeyPath: @"objectValue.objectValue.doubleValue"]
	    doubleValue] == 0.5)

	TEST(@"[-setValue:forKeyPath:]",
	    R([myObject setValue: [OFNumber numberWithDouble: 0.75]
		      forKeyPath: @"objectValue.objectValue.doubleValue"]) &&
	    [[myObject.objectValue objectValue] doubleValue] == 0.75)

	objc_autoreleasePoolPop(pool);
}
@end

Renamed and modified tests/PBKDF2Tests.m [1bccd3d759] to tests/OFPBKDF2Tests.m [9316c48e09].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"PBKDF2";

@implementation TestsAppDelegate (PBKDF2Tests)
- (void)PBKDF2Tests
{
	void *pool = objc_autoreleasePoolPush();
	OFHMAC *HMAC = [OFHMAC HMACWithHashClass: [OFSHA1Hash class]
			   allowsSwappableMemory: true];
	unsigned char key[25];

	/* Test vectors from RFC 6070 */

	TEST(@"PBKDF2-SHA1, 1 iteration",
	    R(of_pbkdf2((of_pbkdf2_parameters_t){
		.HMAC                  = HMAC,
		.iterations            = 1,
		.salt                  = (unsigned char *)"salt",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = key,
		.keyLength             = 20,
		.allowsSwappableMemory = true
	    })) && memcmp(key, "\x0C\x60\xC8\x0F\x96\x1F\x0E\x71\xF3\xA9\xB5"
		"\x24\xAF\x60\x12\x06\x2F\xE0\x37\xA6", 20) == 0)

	TEST(@"PBKDF2-SHA1, 2 iterations",
	    R(of_pbkdf2((of_pbkdf2_parameters_t){
		.HMAC                  = HMAC,
		.iterations            = 2,
		.salt                  = (unsigned char *)"salt",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = key,
		.keyLength             = 20,
		.allowsSwappableMemory = true
	    })) && memcmp(key, "\xEA\x6C\x01\x4D\xC7\x2D\x6F\x8C\xCD\x1E\xD9"
	        "\x2A\xCE\x1D\x41\xF0\xD8\xDE\x89\x57", 20) == 0)

	TEST(@"PBKDF2-SHA1, 4096 iterations",
	    R(of_pbkdf2((of_pbkdf2_parameters_t){
		.HMAC                  = HMAC,
		.iterations            = 4096,
		.salt                  = (unsigned char *)"salt",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = key,
		.keyLength             = 20,
		.allowsSwappableMemory = true
	    })) && memcmp(key, "\x4B\x00\x79\x01\xB7\x65\x48\x9A\xBE\xAD\x49"
	        "\xD9\x26\xF7\x21\xD0\x65\xA4\x29\xC1", 20) == 0)

	/* This test takes too long, even on a fast machine. */
#if 0
	TEST(@"PBKDF2-SHA1, 16777216 iterations",
	    R(of_pbkdf2((of_pbkdf2_parameters_t){
		.HMAC                  = HMAC,
		.iterations            = 16777216,
		.salt                  = (unsigned char *)"salt",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = key,
		.keyLength             = 20,
		.allowsSwappableMemory = true
	    })) && memcmp(key, "\xEE\xFE\x3D\x61\xCD\x4D\xA4\xE4\xE9\x94\x5B"
	        "\x3D\x6B\xA2\x15\x8C\x26\x34\xE9\x84", 20) == 0)
#endif

	TEST(@"PBKDF2-SHA1, 4096 iterations, key > 1 block",
	    R(of_pbkdf2((of_pbkdf2_parameters_t){
		.HMAC                  = HMAC,
		.iterations            = 4096,
		.salt                  = (unsigned char *)"saltSALTsaltSALTsalt"
		                         "SALTsaltSALTsalt",
		.saltLength            = 36,
		.password              = "passwordPASSWORDpassword",
		.passwordLength        = 24,
		.key                   = key,
		.keyLength             = 25,
		.allowsSwappableMemory = true
	    })) &&
	    memcmp(key, "\x3D\x2E\xEC\x4F\xE4\x1C\x84\x9B\x80\xC8\xD8\x36\x62"
	        "\xC0\xE4\x4A\x8B\x29\x1A\x96\x4C\xF2\xF0\x70\x38", 25) == 0)

	TEST(@"PBKDF2-SHA1, 4096 iterations, key < 1 block",
	    R(of_pbkdf2((of_pbkdf2_parameters_t){
		.HMAC                  = HMAC,
		.iterations            = 4096,
		.salt                  = (unsigned char *)"sa\0lt",
		.saltLength            = 5,
		.password              = "pass\0word",
		.passwordLength        = 9,
		.key                   = key,

<
<
|



















|

|










|













|













|















|














|















|







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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFPBKDF2";

@implementation TestsAppDelegate (OFPBKDF2Tests)
- (void)PBKDF2Tests
{
	void *pool = objc_autoreleasePoolPush();
	OFHMAC *HMAC = [OFHMAC HMACWithHashClass: [OFSHA1Hash class]
			   allowsSwappableMemory: true];
	unsigned char key[25];

	/* Test vectors from RFC 6070 */

	TEST(@"PBKDF2-SHA1, 1 iteration",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
		.iterations            = 1,
		.salt                  = (unsigned char *)"salt",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = key,
		.keyLength             = 20,
		.allowsSwappableMemory = true
	    })) && memcmp(key, "\x0C\x60\xC8\x0F\x96\x1F\x0E\x71\xF3\xA9\xB5"
		"\x24\xAF\x60\x12\x06\x2F\xE0\x37\xA6", 20) == 0)

	TEST(@"PBKDF2-SHA1, 2 iterations",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
		.iterations            = 2,
		.salt                  = (unsigned char *)"salt",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = key,
		.keyLength             = 20,
		.allowsSwappableMemory = true
	    })) && memcmp(key, "\xEA\x6C\x01\x4D\xC7\x2D\x6F\x8C\xCD\x1E\xD9"
	        "\x2A\xCE\x1D\x41\xF0\xD8\xDE\x89\x57", 20) == 0)

	TEST(@"PBKDF2-SHA1, 4096 iterations",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
		.iterations            = 4096,
		.salt                  = (unsigned char *)"salt",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = key,
		.keyLength             = 20,
		.allowsSwappableMemory = true
	    })) && memcmp(key, "\x4B\x00\x79\x01\xB7\x65\x48\x9A\xBE\xAD\x49"
	        "\xD9\x26\xF7\x21\xD0\x65\xA4\x29\xC1", 20) == 0)

	/* This test takes too long, even on a fast machine. */
#if 0
	TEST(@"PBKDF2-SHA1, 16777216 iterations",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
		.iterations            = 16777216,
		.salt                  = (unsigned char *)"salt",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = key,
		.keyLength             = 20,
		.allowsSwappableMemory = true
	    })) && memcmp(key, "\xEE\xFE\x3D\x61\xCD\x4D\xA4\xE4\xE9\x94\x5B"
	        "\x3D\x6B\xA2\x15\x8C\x26\x34\xE9\x84", 20) == 0)
#endif

	TEST(@"PBKDF2-SHA1, 4096 iterations, key > 1 block",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
		.iterations            = 4096,
		.salt                  = (unsigned char *)"saltSALTsaltSALTsalt"
		                         "SALTsaltSALTsalt",
		.saltLength            = 36,
		.password              = "passwordPASSWORDpassword",
		.passwordLength        = 24,
		.key                   = key,
		.keyLength             = 25,
		.allowsSwappableMemory = true
	    })) &&
	    memcmp(key, "\x3D\x2E\xEC\x4F\xE4\x1C\x84\x9B\x80\xC8\xD8\x36\x62"
	        "\xC0\xE4\x4A\x8B\x29\x1A\x96\x4C\xF2\xF0\x70\x38", 25) == 0)

	TEST(@"PBKDF2-SHA1, 4096 iterations, key < 1 block",
	    R(OFPBKDF2((OFPBKDF2Parameters){
		.HMAC                  = HMAC,
		.iterations            = 4096,
		.salt                  = (unsigned char *)"sa\0lt",
		.saltLength            = 5,
		.password              = "pass\0word",
		.passwordLength        = 9,
		.key                   = key,

Modified tests/OFPluginTests.m from [cf1db3ca7b] to [bb55705657].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

#import "plugin/TestPlugin.h"

#ifndef OF_IOS
# define PLUGIN_PATH @"plugin/TestPlugin"
#else
# define PLUGIN_PATH @"PlugIns/TestPlugin"
#endif

static OFString *module = @"OFPlugin";

@implementation TestsAppDelegate (OFPluginTests)
- (void)pluginTests
{
	void *pool = objc_autoreleasePoolPush();



	TestPlugin *plugin;



	TEST(@"+[pluginFromFile:]",
	    (plugin = [OFPlugin pluginFromFile: PLUGIN_PATH]))






	TEST(@"TestPlugin's -[test:]", [plugin test: 1234] == 2468)




	objc_autoreleasePoolPop(pool);
}
@end

<
<
|




















|

|


|





>
>
>
|

>
>
|
|
>
>
>

>
>
|
>
>
>




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
/*


 * 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"

#import "TestsAppDelegate.h"

#import "plugin/TestPlugin.h"

#ifndef OF_IOS
static OFString *const pluginName = @"plugin/TestPlugin";
#else
static OFString *const pluginName = @"PlugIns/TestPlugin";
#endif

static OFString *const module = @"OFPlugin";

@implementation TestsAppDelegate (OFPluginTests)
- (void)pluginTests
{
	void *pool = objc_autoreleasePoolPush();
	OFString *path;
	OFPlugin *plugin;
	Class (*class)(void);
	TestPlugin *test;

	TEST(@"+[pathForName:]", (path = [OFPlugin pathForName: pluginName]))

	TEST(@"+[pluginWithPath:]", (plugin = [OFPlugin pluginWithPath: path]))

	TEST(@"-[addressForSymbol:]",
	    (class = (Class (*)(void))(uintptr_t)
	    [plugin addressForSymbol: @"class"]))

	test = [[class() alloc] init];
	@try {
		TEST(@"TestPlugin's -[test:]", [test test: 1234] == 2468)
	} @finally {
		[test release];
	}

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFPropertyListTests.m from [f260520db3] to [6b258c8840].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"			\
	@"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "	\
	@"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"		\
	@"<plist version=\"1.0\">\n"					\
	x @"\n"								\
	@"</plist>"

static OFString *module = @"OFPropertyList";
static OFString *PLIST1 = PLIST(@"<string>Hello</string>");
static OFString *PLIST2 = PLIST(
    @"<array>"
    @" <string>Hello</string>"
    @" <data>V29ybGQh</data>"
    @" <date>2018-03-14T12:34:56Z</date>"
    @" <true/>"
    @" <false/>"
    @" <real>12.25</real>"
    @" <integer>-10</integer>"
    @"</array>");
static OFString *PLIST3 = PLIST(
    @"<dict>"
    @" <key>array</key>"
    @" <array>"
    @"  <string>Hello</string>"
    @"  <data>V29ybGQh</data>"
    @"  <date>2018-03-14T12:34:56Z</date>"
    @"  <true/>"







|
|
|









|







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
	@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"			\
	@"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "	\
	@"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"		\
	@"<plist version=\"1.0\">\n"					\
	x @"\n"								\
	@"</plist>"

static OFString *const module = @"OFPropertyList";
static OFString *const PLIST1 = PLIST(@"<string>Hello</string>");
static OFString *const PLIST2 = PLIST(
    @"<array>"
    @" <string>Hello</string>"
    @" <data>V29ybGQh</data>"
    @" <date>2018-03-14T12:34:56Z</date>"
    @" <true/>"
    @" <false/>"
    @" <real>12.25</real>"
    @" <integer>-10</integer>"
    @"</array>");
static OFString *const PLIST3 = PLIST(
    @"<dict>"
    @" <key>array</key>"
    @" <array>"
    @"  <string>Hello</string>"
    @"  <data>V29ybGQh</data>"
    @"  <date>2018-03-14T12:34:56Z</date>"
    @"  <true/>"
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

@implementation TestsAppDelegate (OFPLISTParser)
- (void)propertyListTests
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *array = [OFArray arrayWithObjects:
	    @"Hello",
	    [OFData dataWithItems: "World!"
			    count: 6],
	    [OFDate dateWithTimeIntervalSince1970: 1521030896],
	    [OFNumber numberWithBool: true],
	    [OFNumber numberWithBool: false],
	    [OFNumber numberWithFloat: 12.25f],
	    [OFNumber numberWithInt: -10],
	    nil];








|
<







55
56
57
58
59
60
61
62

63
64
65
66
67
68
69

@implementation TestsAppDelegate (OFPLISTParser)
- (void)propertyListTests
{
	void *pool = objc_autoreleasePoolPush();
	OFArray *array = [OFArray arrayWithObjects:
	    @"Hello",
	    [OFData dataWithItems: "World!" count: 6],

	    [OFDate dateWithTimeIntervalSince1970: 1521030896],
	    [OFNumber numberWithBool: true],
	    [OFNumber numberWithBool: false],
	    [OFNumber numberWithFloat: 12.25f],
	    [OFNumber numberWithInt: -10],
	    nil];

Modified tests/OFRIPEMD160HashTests.m from [e2102eafad] to [4b80756e64].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFRIPEMD160Hash";

const uint8_t testfile_rmd160[20] =
	"\x46\x02\x97\xF5\x85\xDF\xB9\x21\x00\xC8\xF9\x87\xC6\xEC\x84\x0D\xCE"
	"\xE6\x08\x8B";

@implementation TestsAppDelegate (OFRIPEMD160HashTests)
- (void)RIPEMD160HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFRIPEMD160Hash *rmd160, *copy;
	OFFile *f = [OFFile fileWithPath: @"testfile.bin"
				    mode: @"r"];

	TEST(@"+[cryptoHashWithAllowsSwappableMemory:]",
	    (rmd160 = [OFRIPEMD160Hash
	    cryptoHashWithAllowsSwappableMemory: true]))

	while (!f.atEndOfStream) {
		char buf[64];
		size_t len = [f readIntoBuffer: buf
					length: 64];
		[rmd160 updateWithBuffer: buf
				  length: len];
	}
	[f close];


	TEST(@"-[copy]", (copy = [[rmd160 copy] autorelease]))



	TEST(@"-[digest]",
	    memcmp(rmd160.digest, testfile_rmd160, 20) == 0 &&
	    memcmp(copy.digest, testfile_rmd160, 20) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length]", OFHashAlreadyCalculatedException,
	    [rmd160 updateWithBuffer: ""
			      length: 1])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|

|







|
|
<

|
<
|

|
|
|
<
|
<

|

>
|
>
>


|
|



|
<




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFRIPEMD160Hash";

const uint8_t testFileRIPEMD160[20] =
	"\x46\x02\x97\xF5\x85\xDF\xB9\x21\x00\xC8\xF9\x87\xC6\xEC\x84\x0D\xCE"
	"\xE6\x08\x8B";

@implementation TestsAppDelegate (OFRIPEMD160HashTests)
- (void)RIPEMD160HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFRIPEMD160Hash *RIPEMD160, *RIPEMD160Copy;
	OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"];


	TEST(@"+[hashWithAllowsSwappableMemory:]",

	    (RIPEMD160 = [OFRIPEMD160Hash hashWithAllowsSwappableMemory: true]))

	while (!file.atEndOfStream) {
		char buffer[64];
		size_t length = [file readIntoBuffer: buffer length: 64];

		[RIPEMD160 updateWithBuffer: buffer length: length];

	}
	[file close];

	TEST(@"-[copy]", (RIPEMD160Copy = [[RIPEMD160 copy] autorelease]))

	TEST(@"-[calculate]",
	    R([RIPEMD160 calculate]) && R([RIPEMD160Copy calculate]))

	TEST(@"-[digest]",
	    memcmp(RIPEMD160.digest, testFileRIPEMD160, 20) == 0 &&
	    memcmp(RIPEMD160Copy.digest, testFileRIPEMD160, 20) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length]", OFHashAlreadyCalculatedException,
	    [RIPEMD160 updateWithBuffer: "" length: 1])


	objc_autoreleasePoolPop(pool);
}
@end

Deleted tests/OFSCTPSocketTests.m version [810fa289fa].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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>
#include <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFSCTPSocket";

@implementation TestsAppDelegate (OFSCTPSocketTests)
- (void)SCTPSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSCTPSocket *server, *client = nil, *accepted;
	uint16_t port;
	char buf[6];

	TEST(@"+[socket]", (server = [OFSCTPSocket socket]) &&
	    (client = [OFSCTPSocket socket]))

	@try {
		TEST(@"-[bindToHost:port:]",
		    (port = [server bindToHost: @"127.0.0.1"
					  port: 0]))
	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EPROTONOSUPPORT:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSCTPSocket] -[bindToHost:port:]: "
			    @"SCTP unsupported, skipping tests"];
			break;
		default:
			@throw e;
		}

		objc_autoreleasePoolPop(pool);
		return;
	}

	TEST(@"-[listen]", R([server listen]))

	TEST(@"-[connectToHost:port:]",
	    R([client connectToHost: @"127.0.0.1"
			       port: port]))

	TEST(@"-[accept]", (accepted = [server accept]))

	TEST(@"-[remoteAddress]",
	    [of_socket_address_ip_string(accepted.remoteAddress, NULL)
	    isEqual: @"127.0.0.1"])

	TEST(@"-[sendBuffer:length:]", R([client sendBuffer: "Hello!"
						     length: 6]))

	TEST(@"-[receiveIntoBuffer:length:]", [accepted receiveIntoBuffer: buf
								   length: 6] &&
	    !memcmp(buf, "Hello!", 6))

	objc_autoreleasePoolPop(pool);
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































































































Modified tests/OFSHA1HashTests.m from [ccf0765a92] to [f5847018f6].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFSHA1Hash";

const uint8_t testfile_sha1[20] =
	"\xC9\x9A\xB8\x7E\x1E\xC8\xEC\x65\xD5\xEB\xE4\x2E\x0D\xA6\x80\x96\xF5"
	"\x94\xE7\x17";

@implementation TestsAppDelegate (SHA1HashTests)
- (void)SHA1HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA1Hash *sha1, *copy;
	OFFile *f = [OFFile fileWithPath: @"testfile.bin"
				    mode: @"r"];

	TEST(@"+[cryptoHashWithAllowsSwappableMemory:]",
	    (sha1 = [OFSHA1Hash cryptoHashWithAllowsSwappableMemory: true]))

	while (!f.atEndOfStream) {
		char buf[64];
		size_t len = [f readIntoBuffer: buf
					length: 64];
		[sha1 updateWithBuffer: buf
				length: len];
	}
	[f close];

	TEST(@"-[copy]", (copy = [[sha1 copy] autorelease]))



	TEST(@"-[digest]",
	    memcmp(sha1.digest, testfile_sha1, 20) == 0 &&
	    memcmp(copy.digest, testfile_sha1, 20) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [sha1 updateWithBuffer: ""
			    length: 1])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|

|







|
|
<

|
|

|
|
|
<
|
<

|

|

>
>

|
|



|
<




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFSHA1Hash";

const uint8_t testFileSHA1[20] =
	"\xC9\x9A\xB8\x7E\x1E\xC8\xEC\x65\xD5\xEB\xE4\x2E\x0D\xA6\x80\x96\xF5"
	"\x94\xE7\x17";

@implementation TestsAppDelegate (SHA1HashTests)
- (void)SHA1HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA1Hash *SHA1, *SHA1Copy;
	OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"];


	TEST(@"+[hashWithAllowsSwappableMemory:]",
	    (SHA1 = [OFSHA1Hash hashWithAllowsSwappableMemory: true]))

	while (!file.atEndOfStream) {
		char buffer[64];
		size_t length = [file readIntoBuffer: buffer length: 64];

		[SHA1 updateWithBuffer: buffer length: length];

	}
	[file close];

	TEST(@"-[copy]", (SHA1Copy = [[SHA1 copy] autorelease]))

	TEST(@"-[calculate]", R([SHA1 calculate]) && R([SHA1Copy calculate]))

	TEST(@"-[digest]",
	    memcmp(SHA1.digest, testFileSHA1, 20) == 0 &&
	    memcmp(SHA1Copy.digest, testFileSHA1, 20) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [SHA1 updateWithBuffer: "" length: 1])


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSHA224HashTests.m from [be6b7ce0e2] to [f0d335ef4f].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFSHA224Hash";

const uint8_t testfile_sha224[28] =
	"\x27\x69\xD8\x04\x2D\x0F\xCA\x84\x6C\xF1\x62\x44\xBA\x0C\xBD\x46\x64"
	"\x5F\x4F\x20\x02\x4D\x15\xED\x1C\x61\x1F\xF7";

@implementation TestsAppDelegate (SHA224HashTests)
- (void)SHA224HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA224Hash *sha224, *copy;
	OFFile *f = [OFFile fileWithPath: @"testfile.bin"
				    mode: @"r"];

	TEST(@"+[cryptoHashWithAllowsSwappableMemory:]",
	    (sha224 = [OFSHA224Hash cryptoHashWithAllowsSwappableMemory: true]))

	while (!f.atEndOfStream) {
		char buf[64];
		size_t len = [f readIntoBuffer: buf
					length: 64];
		[sha224 updateWithBuffer: buf
				  length: len];
	}
	[f close];


	TEST(@"-[copy]", (copy = [[sha224 copy] autorelease]))



	TEST(@"-[digest]",
	    memcmp(sha224.digest, testfile_sha224, 28) == 0 &&
	    memcmp(copy.digest, testfile_sha224, 28) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [sha224 updateWithBuffer: ""
			      length: 1])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|

|







|
|
<

|
|

|
|
|
<
|
<

|

>
|
>
>


|
|



|
<




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFSHA224Hash";

const uint8_t testFileSHA224[28] =
	"\x27\x69\xD8\x04\x2D\x0F\xCA\x84\x6C\xF1\x62\x44\xBA\x0C\xBD\x46\x64"
	"\x5F\x4F\x20\x02\x4D\x15\xED\x1C\x61\x1F\xF7";

@implementation TestsAppDelegate (SHA224HashTests)
- (void)SHA224HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA224Hash *SHA224, *SHA224Copy;
	OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"];


	TEST(@"+[hashWithAllowsSwappableMemory:]",
	    (SHA224 = [OFSHA224Hash hashWithAllowsSwappableMemory: true]))

	while (!file.atEndOfStream) {
		char buffer[64];
		size_t length = [file readIntoBuffer: buffer length: 64];

		[SHA224 updateWithBuffer: buffer length: length];

	}
	[file close];

	TEST(@"-[copy]", (SHA224Copy = [[SHA224 copy] autorelease]))

	TEST(@"-[calculate]",
	    R([SHA224 calculate]) && R([SHA224Copy calculate]))

	TEST(@"-[digest]",
	    memcmp(SHA224.digest, testFileSHA224, 28) == 0 &&
	    memcmp(SHA224Copy.digest, testFileSHA224, 28) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [SHA224 updateWithBuffer: "" length: 1])


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSHA256HashTests.m from [207defcf79] to [2075144684].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFSHA256Hash";

const uint8_t testfile_sha256[32] =
	"\x1A\x02\xD6\x46\xF5\xA6\xBA\xAA\xFF\x7F\xD5\x87\xBA\xC3\xF6\xC6\xB5"
	"\x67\x93\x8F\x0F\x44\x90\xB8\xF5\x35\x89\xF0\x5A\x23\x7F\x69";

@implementation TestsAppDelegate (SHA256HashTests)
- (void)SHA256HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA256Hash *sha256, *copy;
	OFFile *f = [OFFile fileWithPath: @"testfile.bin"
				    mode: @"r"];

	TEST(@"+[cryptoHashWithAllowsSwappableMemory:]",
	    (sha256 = [OFSHA256Hash cryptoHashWithAllowsSwappableMemory: true]))

	while (!f.atEndOfStream) {
		char buf[64];
		size_t len = [f readIntoBuffer: buf
					length: 64];
		[sha256 updateWithBuffer: buf
				  length: len];
	}
	[f close];


	TEST(@"-[copy]", (copy = [[sha256 copy] autorelease]))



	TEST(@"-[digest]",
	    memcmp(sha256.digest, testfile_sha256, 32) == 0 &&
	    memcmp(copy.digest, testfile_sha256, 32) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [sha256 updateWithBuffer: ""
			      length: 1])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|

|







|
|
<

|
|

|
|
|
<
|
<

|

>
|
>
>


|
|



|
<




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFSHA256Hash";

const uint8_t testFileSHA256[32] =
	"\x1A\x02\xD6\x46\xF5\xA6\xBA\xAA\xFF\x7F\xD5\x87\xBA\xC3\xF6\xC6\xB5"
	"\x67\x93\x8F\x0F\x44\x90\xB8\xF5\x35\x89\xF0\x5A\x23\x7F\x69";

@implementation TestsAppDelegate (SHA256HashTests)
- (void)SHA256HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA256Hash *SHA256, *SHA256Copy;
	OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"];


	TEST(@"+[hashWithAllowsSwappableMemory:]",
	    (SHA256 = [OFSHA256Hash hashWithAllowsSwappableMemory: true]))

	while (!file.atEndOfStream) {
		char buffer[64];
		size_t length = [file readIntoBuffer: buffer length: 64];

		[SHA256 updateWithBuffer: buffer length: length];

	}
	[file close];

	TEST(@"-[copy]", (SHA256Copy = [[SHA256 copy] autorelease]))

	TEST(@"-[calculate]",
	    R([SHA256 calculate]) && R([SHA256Copy calculate]))

	TEST(@"-[digest]",
	    memcmp(SHA256.digest, testFileSHA256, 32) == 0 &&
	    memcmp(SHA256Copy.digest, testFileSHA256, 32) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [SHA256 updateWithBuffer: "" length: 1])


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSHA384HashTests.m from [3b765b947e] to [9815671ea1].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFSHA384Hash";

const uint8_t testfile_sha384[48] =
	"\x7E\xDE\x62\xE2\x10\xA5\x1E\x18\x8A\x11\x7F\x78\xD7\xC7\x55\xB6\x43"
	"\x94\x1B\xD2\x78\x5C\xCF\xF3\x8A\xB8\x98\x22\xC7\x0E\xFE\xF1\xEC\x53"
	"\xE9\x1A\xB3\x51\x70\x8C\x1F\x3F\x56\x12\x44\x01\x91\x54";

@implementation TestsAppDelegate (SHA384HashTests)
- (void)SHA384HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA384Hash *sha384, *copy;
	OFFile *f = [OFFile fileWithPath: @"testfile.bin"
				    mode: @"r"];

	TEST(@"+[cryptoHashWithAllowsSwappableMemory:]",
	    (sha384 = [OFSHA384Hash cryptoHashWithAllowsSwappableMemory: true]))

	while (!f.atEndOfStream) {
		char buf[128];
		size_t len = [f readIntoBuffer: buf
					length: 128];
		[sha384 updateWithBuffer: buf
				  length: len];
	}
	[f close];


	TEST(@"-[copy]", (copy = [[sha384 copy] autorelease]))



	TEST(@"-[digest]",
	    memcmp(sha384.digest, testfile_sha384, 48) == 0 &&
	    memcmp(copy.digest, testfile_sha384, 48) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [sha384 updateWithBuffer: ""
			      length: 1])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|

|








|
|
<

|
|

|
|
|
<
|
<

|

>
|
>
>


|
|



|
<




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFSHA384Hash";

const uint8_t testFileSHA384[48] =
	"\x7E\xDE\x62\xE2\x10\xA5\x1E\x18\x8A\x11\x7F\x78\xD7\xC7\x55\xB6\x43"
	"\x94\x1B\xD2\x78\x5C\xCF\xF3\x8A\xB8\x98\x22\xC7\x0E\xFE\xF1\xEC\x53"
	"\xE9\x1A\xB3\x51\x70\x8C\x1F\x3F\x56\x12\x44\x01\x91\x54";

@implementation TestsAppDelegate (SHA384HashTests)
- (void)SHA384HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA384Hash *SHA384, *SHA384Copy;
	OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"];


	TEST(@"+[hashWithAllowsSwappableMemory:]",
	    (SHA384 = [OFSHA384Hash hashWithAllowsSwappableMemory: true]))

	while (!file.atEndOfStream) {
		char buffer[128];
		size_t length = [file readIntoBuffer: buffer length: 128];

		[SHA384 updateWithBuffer: buffer length: length];

	}
	[file close];

	TEST(@"-[copy]", (SHA384Copy = [[SHA384 copy] autorelease]))

	TEST(@"-[calculate]",
	    R([SHA384 calculate]) && R([SHA384Copy calculate]))

	TEST(@"-[digest]",
	    memcmp(SHA384.digest, testFileSHA384, 48) == 0 &&
	    memcmp(SHA384Copy.digest, testFileSHA384, 48) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [SHA384 updateWithBuffer: "" length: 1])


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSHA512HashTests.m from [9088f78182] to [8ea1d53446].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFSHA512Hash";

const uint8_t testfile_sha512[64] =
	"\x8F\x36\x6E\x3C\x19\x4B\xBB\xC7\x82\xAA\xCD\x7D\x55\xA2\xD3\x29\x29"
	"\x97\x6A\x3F\xEB\x9B\xB2\xCB\x75\xC9\xEC\xC8\x10\x07\xD6\x07\x31\x4A"
	"\xB1\x30\x97\x82\x58\xA5\x1F\x71\x42\xE6\x56\x07\x99\x57\xB2\xB8\x3B"
	"\xA1\x8A\x41\x64\x33\x69\x21\x8C\x2A\x44\x6D\xF2\xA0";

@implementation TestsAppDelegate (SHA512HashTests)
- (void)SHA512HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA512Hash *sha512, *copy;
	OFFile *f = [OFFile fileWithPath: @"testfile.bin"
				    mode: @"r"];

	TEST(@"+[cryptoHashWithAllowsSwappableMemory:]",
	    (sha512 = [OFSHA512Hash cryptoHashWithAllowsSwappableMemory: true]))

	while (!f.atEndOfStream) {
		char buf[128];
		size_t len = [f readIntoBuffer: buf
					length: 128];
		[sha512 updateWithBuffer: buf
				  length: len];
	}
	[f close];


	TEST(@"-[copy]", (copy = [[sha512 copy] autorelease]))



	TEST(@"-[digest]",
	    memcmp(sha512.digest, testfile_sha512, 64) == 0 &&
	    memcmp(copy.digest, testfile_sha512, 64) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [sha512 updateWithBuffer: ""
			      length: 1])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|

|









|
|
<

|
|

|
|
|
<
|
<

|

>
|
>
>


|
|



|
<




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFSHA512Hash";

const uint8_t testFileSHA512[64] =
	"\x8F\x36\x6E\x3C\x19\x4B\xBB\xC7\x82\xAA\xCD\x7D\x55\xA2\xD3\x29\x29"
	"\x97\x6A\x3F\xEB\x9B\xB2\xCB\x75\xC9\xEC\xC8\x10\x07\xD6\x07\x31\x4A"
	"\xB1\x30\x97\x82\x58\xA5\x1F\x71\x42\xE6\x56\x07\x99\x57\xB2\xB8\x3B"
	"\xA1\x8A\x41\x64\x33\x69\x21\x8C\x2A\x44\x6D\xF2\xA0";

@implementation TestsAppDelegate (SHA512HashTests)
- (void)SHA512HashTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSHA512Hash *SHA512, *SHA512Copy;
	OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"];


	TEST(@"+[hashWithAllowsSwappableMemory:]",
	    (SHA512 = [OFSHA512Hash hashWithAllowsSwappableMemory: true]))

	while (!file.atEndOfStream) {
		char buffer[128];
		size_t length = [file readIntoBuffer: buffer length: 128];

		[SHA512 updateWithBuffer: buffer length: length];

	}
	[file close];

	TEST(@"-[copy]", (SHA512Copy = [[SHA512 copy] autorelease]))

	TEST(@"-[calculate]",
	    R([SHA512 calculate]) && R([SHA512Copy calculate]))

	TEST(@"-[digest]",
	    memcmp(SHA512.digest, testFileSHA512, 64) == 0 &&
	    memcmp(SHA512Copy.digest, testFileSHA512, 64) == 0)

	EXPECT_EXCEPTION(@"Detect invalid call of "
	    @"-[updateWithBuffer:length:]", OFHashAlreadyCalculatedException,
	    [SHA512 updateWithBuffer: "" length: 1])


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSPXSocketTests.m from [e438c57a59] to [3e5f030bd3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "TestsAppDelegate.h"

static OFString *module = @"OFSPXSocket";

@interface SPXSocketDelegate: OFObject <OFSPXSocketDelegate>
{
@public
	OFSequencedPacketSocket *_expectedServerSocket;
	OFSPXSocket *_expectedClientSocket;
	unsigned char _expectedNode[IPX_NODE_LEN];
	uint32_t _expectedNetwork;
	uint16_t _expectedPort;
	bool _accepted;
	bool _connected;
}
@end

@implementation SPXSocketDelegate
-    (bool)socket: (OFSequencedPacketSocket *)sock
  didAcceptSocket: (OFSequencedPacketSocket *)accepted
	exception: (id)exception
{
	OF_ENSURE(!_accepted);

	_accepted = (sock == _expectedServerSocket && accepted != nil &&
	    exception == nil);

	if (_accepted && _connected)
		[[OFRunLoop mainRunLoop] stop];

	return false;
}

-     (void)socket: (OFSPXSocket *)sock
  didConnectToNode: (unsigned char [IPX_NODE_LEN])node
	   network: (uint32_t)network
	      port: (uint16_t)port
	 exception: (id)exception
{
	OF_ENSURE(!_connected);

	_connected = (sock == _expectedClientSocket &&
	    memcmp(node, _expectedNode, IPX_NODE_LEN) == 0 &&
	    network == _expectedNetwork && port == _expectedPort &&
	    exception == nil);

	if (_accepted && _connected)
		[[OFRunLoop mainRunLoop] stop];
}
@end

@implementation TestsAppDelegate (OFSPXSocketTests)
- (void)SPXSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSPXSocket *sockClient, *sockServer, *sockAccepted;;
	of_socket_address_t address1;
	const of_socket_address_t *address2;
	unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN];
	uint32_t network;
	uint16_t port;
	char buffer[5];
	SPXSocketDelegate *delegate;

	TEST(@"+[socket]", (sockClient = [OFSPXSocket socket]) &&
	    (sockServer = [OFSPXSocket socket]))

	@try {
		TEST(@"-[bindToPort:]",
		    R(address1 = [sockServer bindToPort: 0]))
	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EAFNOSUPPORT:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSPXSocket] -[bindToPort:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case ESOCKTNOSUPPORT:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSPXSocket] -[bindToPort:]: "
			    @"SPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSPXSocket] -[bindToPort:]: "
			    @"IPX not configured, skipping tests"];
			break;
		default:
			@throw e;
		}

		objc_autoreleasePoolPop(pool);
		return;
	}

	of_socket_address_get_ipx_node(&address1, node);
	network = of_socket_address_get_ipx_network(&address1);
	port = of_socket_address_get_port(&address1);

	TEST(@"-[listen]", R([sockServer listen]))

	TEST(@"-[connectToNode:network:port:]",
	    R([sockClient connectToNode: node
				network: network
				   port: port]))

	TEST(@"-[accept]", (sockAccepted = [sockServer accept]))

	TEST(@"-[sendBuffer:length:]",
	    R([sockAccepted sendBuffer: "Hello"
				length: 5]))

	TEST(@"-[receiveIntoBuffer:length:]",
	    [sockClient receiveIntoBuffer: buffer
				   length: 5] == 5 &&
	    memcmp(buffer, "Hello", 5) == 0)

	TEST(@"-[remoteAddress]",
	    (address2 = sockAccepted.remoteAddress) &&
	    R(of_socket_address_get_ipx_node(address2, node2)) &&
	    memcmp(node, node2, IPX_NODE_LEN) == 0 &&
	    of_socket_address_get_ipx_network(address2) == network)

	delegate = [[[SPXSocketDelegate alloc] init] autorelease];

	sockServer = [OFSPXSocket socket];
	delegate->_expectedServerSocket = sockServer;
	sockServer.delegate = delegate;

	sockClient = [OFSPXSocket socket];
	delegate->_expectedClientSocket = sockClient;
	sockClient.delegate = delegate;

	address1 = [sockServer bindToPort: 0];
	[sockServer listen];
	[sockServer asyncAccept];

	of_socket_address_get_ipx_node(&address1, node);
	memcpy(delegate->_expectedNode, node, IPX_NODE_LEN);
	delegate->_expectedNetwork = network =
	    of_socket_address_get_ipx_network(&address1);
	delegate->_expectedPort = port = of_socket_address_get_port(&address1);

	@try {
		[sockClient asyncConnectToNode: node
				       network: network
					  port: port];

		[[OFRunLoop mainRunLoop] runUntilDate:
		    [OFDate dateWithTimeIntervalSinceNow: 2]];

		TEST(@"-[asyncAccept] & -[asyncConnectToNode:network:port:]",
		    delegate->_accepted && delegate->_connected)
	} @catch (OFObserveFailedException *e) {
		switch (e.errNo) {
		case ENOTSOCK:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSPXSocket] -[asyncAccept] & "
			    @"-[asyncConnectToNode:network:port:]: select() "
			    @"not supported for SPX, skipping test"];
			break;
		default:
			@throw e;
		}
	}

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|



















|
















|















|
|
|















|
|
|



|
|
|



|
|
|










|
|
|




|
<
<




|
<


|
<




|

|















|


|
|














|
|
|











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
/*


 * 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 "TestsAppDelegate.h"

static OFString *const module = @"OFSPXSocket";

@interface SPXSocketDelegate: OFObject <OFSPXSocketDelegate>
{
@public
	OFSequencedPacketSocket *_expectedServerSocket;
	OFSPXSocket *_expectedClientSocket;
	unsigned char _expectedNode[IPX_NODE_LEN];
	uint32_t _expectedNetwork;
	uint16_t _expectedPort;
	bool _accepted;
	bool _connected;
}
@end

@implementation SPXSocketDelegate
-    (bool)socket: (OFSequencedPacketSocket *)sock
  didAcceptSocket: (OFSequencedPacketSocket *)accepted
	exception: (id)exception
{
	OFEnsure(!_accepted);

	_accepted = (sock == _expectedServerSocket && accepted != nil &&
	    exception == nil);

	if (_accepted && _connected)
		[[OFRunLoop mainRunLoop] stop];

	return false;
}

-     (void)socket: (OFSPXSocket *)sock
  didConnectToNode: (unsigned char [IPX_NODE_LEN])node
	   network: (uint32_t)network
	      port: (uint16_t)port
	 exception: (id)exception
{
	OFEnsure(!_connected);

	_connected = (sock == _expectedClientSocket &&
	    memcmp(node, _expectedNode, IPX_NODE_LEN) == 0 &&
	    network == _expectedNetwork && port == _expectedPort &&
	    exception == nil);

	if (_accepted && _connected)
		[[OFRunLoop mainRunLoop] stop];
}
@end

@implementation TestsAppDelegate (OFSPXSocketTests)
- (void)SPXSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSPXSocket *sockClient, *sockServer = nil, *sockAccepted;
	OFSocketAddress address1;
	const OFSocketAddress *address2;
	unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN];
	uint32_t network;
	uint16_t port;
	char buffer[5];
	SPXSocketDelegate *delegate;

	TEST(@"+[socket]", (sockClient = [OFSPXSocket socket]) &&
	    (sockServer = [OFSPXSocket socket]))

	@try {
		TEST(@"-[bindToPort:]",
		    R(address1 = [sockServer bindToPort: 0]))
	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EAFNOSUPPORT:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFSPXSocket] -[bindToPort:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case ESOCKTNOSUPPORT:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFSPXSocket] -[bindToPort:]: "
			    @"SPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFSPXSocket] -[bindToPort:]: "
			    @"IPX not configured, skipping tests"];
			break;
		default:
			@throw e;
		}

		objc_autoreleasePoolPop(pool);
		return;
	}

	OFSocketAddressIPXNode(&address1, node);
	network = OFSocketAddressIPXNetwork(&address1);
	port = OFSocketAddressPort(&address1);

	TEST(@"-[listen]", R([sockServer listen]))

	TEST(@"-[connectToNode:network:port:]",
	    R([sockClient connectToNode: node network: network port: port]))



	TEST(@"-[accept]", (sockAccepted = [sockServer accept]))

	TEST(@"-[sendBuffer:length:]",
	    R([sockAccepted sendBuffer: "Hello" length: 5]))


	TEST(@"-[receiveIntoBuffer:length:]",
	    [sockClient receiveIntoBuffer: buffer length: 5] == 5 &&

	    memcmp(buffer, "Hello", 5) == 0)

	TEST(@"-[remoteAddress]",
	    (address2 = sockAccepted.remoteAddress) &&
	    R(OFSocketAddressIPXNode(address2, node2)) &&
	    memcmp(node, node2, IPX_NODE_LEN) == 0 &&
	    OFSocketAddressIPXNetwork(address2) == network)

	delegate = [[[SPXSocketDelegate alloc] init] autorelease];

	sockServer = [OFSPXSocket socket];
	delegate->_expectedServerSocket = sockServer;
	sockServer.delegate = delegate;

	sockClient = [OFSPXSocket socket];
	delegate->_expectedClientSocket = sockClient;
	sockClient.delegate = delegate;

	address1 = [sockServer bindToPort: 0];
	[sockServer listen];
	[sockServer asyncAccept];

	OFSocketAddressIPXNode(&address1, node);
	memcpy(delegate->_expectedNode, node, IPX_NODE_LEN);
	delegate->_expectedNetwork = network =
	    OFSocketAddressIPXNetwork(&address1);
	delegate->_expectedPort = port = OFSocketAddressPort(&address1);

	@try {
		[sockClient asyncConnectToNode: node
				       network: network
					  port: port];

		[[OFRunLoop mainRunLoop] runUntilDate:
		    [OFDate dateWithTimeIntervalSinceNow: 2]];

		TEST(@"-[asyncAccept] & -[asyncConnectToNode:network:port:]",
		    delegate->_accepted && delegate->_connected)
	} @catch (OFObserveFailedException *e) {
		switch (e.errNo) {
		case ENOTSOCK:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFSPXSocket] -[asyncAccept] & "
			    @"-[asyncConnectToNode:network:port:]: select() "
			    @"not supported for SPX, skipping test"];
			break;
		default:
			@throw e;
		}
	}

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSPXStreamSocketTests.m from [4e87ecb1d0] to [30183cbd9e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "TestsAppDelegate.h"

static OFString *module = @"OFSPXStreamSocket";

@interface SPXStreamSocketDelegate: OFObject <OFSPXStreamSocketDelegate>
{
@public
	OFStreamSocket *_expectedServerSocket;
	OFSPXStreamSocket *_expectedClientSocket;
	unsigned char _expectedNode[IPX_NODE_LEN];
	uint32_t _expectedNetwork;
	uint16_t _expectedPort;
	bool _accepted;
	bool _connected;
}
@end

@implementation SPXStreamSocketDelegate
-    (bool)socket: (OFStreamSocket *)sock
  didAcceptSocket: (OFStreamSocket *)accepted
	exception: (id)exception
{
	OF_ENSURE(!_accepted);

	_accepted = (sock == _expectedServerSocket && accepted != nil &&
	    exception == nil);

	if (_accepted && _connected)
		[[OFRunLoop mainRunLoop] stop];

	return false;
}

-     (void)socket: (OFSPXStreamSocket *)sock
  didConnectToNode: (unsigned char [IPX_NODE_LEN])node
	   network: (uint32_t)network
	      port: (uint16_t)port
	 exception: (id)exception
{
	OF_ENSURE(!_connected);

	_connected = (sock == _expectedClientSocket &&
	    memcmp(node, _expectedNode, IPX_NODE_LEN) == 0 &&
	    network == _expectedNetwork && port == _expectedPort &&
	    exception == nil);

	if (_accepted && _connected)
		[[OFRunLoop mainRunLoop] stop];
}
@end

@implementation TestsAppDelegate (OFSPXStreamSocketTests)
- (void)SPXStreamSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSPXStreamSocket *sockClient, *sockServer, *sockAccepted;;
	of_socket_address_t address1;
	const of_socket_address_t *address2;
	unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN];
	uint32_t network;
	uint16_t port;
	char buffer[5];
	SPXStreamSocketDelegate *delegate;

	TEST(@"+[socket]", (sockClient = [OFSPXStreamSocket socket]) &&
	    (sockServer = [OFSPXStreamSocket socket]))

	@try {
		TEST(@"-[bindToPort:]",
		    R(address1 = [sockServer bindToPort: 0]))
	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EAFNOSUPPORT:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSPXStreamSocket] -[bindToPort:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case ESOCKTNOSUPPORT:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSPXStreamSocket] -[bindToPort:]: "
			    @"SPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSPXStreamSocket] -[bindToPort:]: "
			    @"IPX not configured, skipping tests"];
			break;
		default:
			@throw e;
		}

		objc_autoreleasePoolPop(pool);
		return;
	}

	of_socket_address_get_ipx_node(&address1, node);
	network = of_socket_address_get_ipx_network(&address1);
	port = of_socket_address_get_port(&address1);

	TEST(@"-[listen]", R([sockServer listen]))

	TEST(@"-[connectToNode:network:port:]",
	    R([sockClient connectToNode: node
				network: network
				   port: port]))

	TEST(@"-[accept]", (sockAccepted = [sockServer accept]))

	/* Test reassembly (this would not work with OFSPXSocket) */
	TEST(@"-[writeBuffer:length:]",
	    R([sockAccepted writeBuffer: "Hello"
				 length: 5]))

	TEST(@"-[readIntoBuffer:length:]",
	    [sockClient readIntoBuffer: buffer
				length: 2] == 2 &&
	    memcmp(buffer, "He", 2) == 0 &&
	    [sockClient readIntoBuffer: buffer
				length: 3] == 3 &&
	    memcmp(buffer, "llo", 3) == 0)

	TEST(@"-[remoteAddress]",
	    (address2 = sockAccepted.remoteAddress) &&
	    R(of_socket_address_get_ipx_node(address2, node2)) &&
	    memcmp(node, node2, IPX_NODE_LEN) == 0 &&
	    of_socket_address_get_ipx_network(address2) == network)

	delegate = [[[SPXStreamSocketDelegate alloc] init] autorelease];

	sockServer = [OFSPXStreamSocket socket];
	delegate->_expectedServerSocket = sockServer;
	sockServer.delegate = delegate;

	sockClient = [OFSPXStreamSocket socket];
	delegate->_expectedClientSocket = sockClient;
	sockClient.delegate = delegate;

	address1 = [sockServer bindToPort: 0];
	[sockServer listen];
	[sockServer asyncAccept];

	of_socket_address_get_ipx_node(&address1, node);
	memcpy(delegate->_expectedNode, node, IPX_NODE_LEN);
	delegate->_expectedNetwork = network =
	    of_socket_address_get_ipx_network(&address1);
	delegate->_expectedPort = port = of_socket_address_get_port(&address1);

	@try {
		[sockClient asyncConnectToNode: node
				       network: network
					  port: port];

		[[OFRunLoop mainRunLoop] runUntilDate:
		    [OFDate dateWithTimeIntervalSinceNow: 2]];

		TEST(@"-[asyncAccept] & -[asyncConnectToNode:network:port:]",
		    delegate->_accepted && delegate->_connected)
	} @catch (OFObserveFailedException *e) {
		switch (e.errNo) {
		case ENOTSOCK:
			[of_stdout setForegroundColor: [OFColor lime]];
			[of_stdout writeLine:
			    @"[OFSPXStreamSocket] -[asyncAccept] & "
			    @"-[asyncConnectToNode:network:port:]: select() "
			    @"not supported for SPX, skipping test"];
			break;
		default:
			@throw e;
		}
	}

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|



















|
















|















|
|
|















|
|
|



|
|
|



|
|
|










|
|
|




|
<
<





|
<


|
<

|
<




|

|















|


|
|














|
|
|











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
/*


 * 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 "TestsAppDelegate.h"

static OFString *const module = @"OFSPXStreamSocket";

@interface SPXStreamSocketDelegate: OFObject <OFSPXStreamSocketDelegate>
{
@public
	OFStreamSocket *_expectedServerSocket;
	OFSPXStreamSocket *_expectedClientSocket;
	unsigned char _expectedNode[IPX_NODE_LEN];
	uint32_t _expectedNetwork;
	uint16_t _expectedPort;
	bool _accepted;
	bool _connected;
}
@end

@implementation SPXStreamSocketDelegate
-    (bool)socket: (OFStreamSocket *)sock
  didAcceptSocket: (OFStreamSocket *)accepted
	exception: (id)exception
{
	OFEnsure(!_accepted);

	_accepted = (sock == _expectedServerSocket && accepted != nil &&
	    exception == nil);

	if (_accepted && _connected)
		[[OFRunLoop mainRunLoop] stop];

	return false;
}

-     (void)socket: (OFSPXStreamSocket *)sock
  didConnectToNode: (unsigned char [IPX_NODE_LEN])node
	   network: (uint32_t)network
	      port: (uint16_t)port
	 exception: (id)exception
{
	OFEnsure(!_connected);

	_connected = (sock == _expectedClientSocket &&
	    memcmp(node, _expectedNode, IPX_NODE_LEN) == 0 &&
	    network == _expectedNetwork && port == _expectedPort &&
	    exception == nil);

	if (_accepted && _connected)
		[[OFRunLoop mainRunLoop] stop];
}
@end

@implementation TestsAppDelegate (OFSPXStreamSocketTests)
- (void)SPXStreamSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSPXStreamSocket *sockClient, *sockServer = nil, *sockAccepted;
	OFSocketAddress address1;
	const OFSocketAddress *address2;
	unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN];
	uint32_t network;
	uint16_t port;
	char buffer[5];
	SPXStreamSocketDelegate *delegate;

	TEST(@"+[socket]", (sockClient = [OFSPXStreamSocket socket]) &&
	    (sockServer = [OFSPXStreamSocket socket]))

	@try {
		TEST(@"-[bindToPort:]",
		    R(address1 = [sockServer bindToPort: 0]))
	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EAFNOSUPPORT:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFSPXStreamSocket] -[bindToPort:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case ESOCKTNOSUPPORT:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFSPXStreamSocket] -[bindToPort:]: "
			    @"SPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFSPXStreamSocket] -[bindToPort:]: "
			    @"IPX not configured, skipping tests"];
			break;
		default:
			@throw e;
		}

		objc_autoreleasePoolPop(pool);
		return;
	}

	OFSocketAddressIPXNode(&address1, node);
	network = OFSocketAddressIPXNetwork(&address1);
	port = OFSocketAddressPort(&address1);

	TEST(@"-[listen]", R([sockServer listen]))

	TEST(@"-[connectToNode:network:port:]",
	    R([sockClient connectToNode: node network: network port: port]))



	TEST(@"-[accept]", (sockAccepted = [sockServer accept]))

	/* Test reassembly (this would not work with OFSPXSocket) */
	TEST(@"-[writeBuffer:length:]",
	    R([sockAccepted writeBuffer: "Hello" length: 5]))


	TEST(@"-[readIntoBuffer:length:]",
	    [sockClient readIntoBuffer: buffer length: 2] == 2 &&

	    memcmp(buffer, "He", 2) == 0 &&
	    [sockClient readIntoBuffer: buffer length: 3] == 3 &&

	    memcmp(buffer, "llo", 3) == 0)

	TEST(@"-[remoteAddress]",
	    (address2 = sockAccepted.remoteAddress) &&
	    R(OFSocketAddressIPXNode(address2, node2)) &&
	    memcmp(node, node2, IPX_NODE_LEN) == 0 &&
	    OFSocketAddressIPXNetwork(address2) == network)

	delegate = [[[SPXStreamSocketDelegate alloc] init] autorelease];

	sockServer = [OFSPXStreamSocket socket];
	delegate->_expectedServerSocket = sockServer;
	sockServer.delegate = delegate;

	sockClient = [OFSPXStreamSocket socket];
	delegate->_expectedClientSocket = sockClient;
	sockClient.delegate = delegate;

	address1 = [sockServer bindToPort: 0];
	[sockServer listen];
	[sockServer asyncAccept];

	OFSocketAddressIPXNode(&address1, node);
	memcpy(delegate->_expectedNode, node, IPX_NODE_LEN);
	delegate->_expectedNetwork = network =
	    OFSocketAddressIPXNetwork(&address1);
	delegate->_expectedPort = port = OFSocketAddressPort(&address1);

	@try {
		[sockClient asyncConnectToNode: node
				       network: network
					  port: port];

		[[OFRunLoop mainRunLoop] runUntilDate:
		    [OFDate dateWithTimeIntervalSinceNow: 2]];

		TEST(@"-[asyncAccept] & -[asyncConnectToNode:network:port:]",
		    delegate->_accepted && delegate->_connected)
	} @catch (OFObserveFailedException *e) {
		switch (e.errNo) {
		case ENOTSOCK:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFSPXStreamSocket] -[asyncAccept] & "
			    @"-[asyncConnectToNode:network:port:]: select() "
			    @"not supported for SPX, skipping test"];
			break;
		default:
			@throw e;
		}
	}

	objc_autoreleasePoolPop(pool);
}
@end

Renamed and modified tests/ScryptTests.m [b2b1c50b4e] to tests/OFScryptTests.m [b11c7bebe9].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"scrypt";
/* Test vectors form RFC 7914 */
static const unsigned char salsa20Input[64] = {
	0x7E, 0x87, 0x9A, 0x21, 0x4F, 0x3E, 0xC9, 0x86, 0x7C, 0xA9, 0x40, 0xE6,
	0x41, 0x71, 0x8F, 0x26, 0xBA, 0xEE, 0x55, 0x5B, 0x8C, 0x61, 0xC1, 0xB5,
	0x0D, 0xF8, 0x46, 0x11, 0x6D, 0xCD, 0x3B, 0x1D, 0xEE, 0x24, 0xF3, 0x19,
	0xDF, 0x9B, 0x3D, 0x85, 0x14, 0x12, 0x1E, 0x4B, 0x5A, 0xC5, 0xAA, 0x32,
	0x76, 0x02, 0x1D, 0x29, 0x09, 0xC7, 0x48, 0x29, 0xED, 0xEB, 0xC6, 0x8D,

<
<
|



















|







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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFScrypt";
/* Test vectors form RFC 7914 */
static const unsigned char salsa20Input[64] = {
	0x7E, 0x87, 0x9A, 0x21, 0x4F, 0x3E, 0xC9, 0x86, 0x7C, 0xA9, 0x40, 0xE6,
	0x41, 0x71, 0x8F, 0x26, 0xBA, 0xEE, 0x55, 0x5B, 0x8C, 0x61, 0xC1, 0xB5,
	0x0D, 0xF8, 0x46, 0x11, 0x6D, 0xCD, 0x3B, 0x1D, 0xEE, 0x24, 0xF3, 0x19,
	0xDF, 0x9B, 0x3D, 0x85, 0x14, 0x12, 0x1E, 0x4B, 0x5A, 0xC5, 0xAA, 0x32,
	0x76, 0x02, 0x1D, 0x29, 0x09, 0xC7, 0x48, 0x29, 0xED, 0xEB, 0xC6, 0x8D,
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
	0xFD, 0xBA, 0xBE, 0x1C, 0x9D, 0x34, 0x72, 0x00, 0x78, 0x56, 0xE7, 0x19,
	0x0D, 0x01, 0xE9, 0xFE, 0x7C, 0x6A, 0xD7, 0xCB, 0xC8, 0x23, 0x78, 0x30,
	0xE7, 0x73, 0x76, 0x63, 0x4B, 0x37, 0x31, 0x62, 0x2E, 0xAF, 0x30, 0xD9,
	0x2E, 0x22, 0xA3, 0x88, 0x6F, 0xF1, 0x09, 0x27, 0x9D, 0x98, 0x30, 0xDA,
	0xC7, 0x27, 0xAF, 0xB9, 0x4A, 0x83, 0xEE, 0x6D, 0x83, 0x60, 0xCB, 0xDF,
	0xA2, 0xCC, 0x06, 0x40
};


static const unsigned char testVector3[64] = {
	0x70, 0x23, 0xBD, 0xCB, 0x3A, 0xFD, 0x73, 0x48, 0x46, 0x1C, 0x06, 0xCD,
	0x81, 0xFD, 0x38, 0xEB, 0xFD, 0xA8, 0xFB, 0xBA, 0x90, 0x4F, 0x8E, 0x3E,
	0xA9, 0xB5, 0x43, 0xF6, 0x54, 0x5D, 0xA1, 0xF2, 0xD5, 0x43, 0x29, 0x55,
	0x61, 0x3F, 0x0F, 0xCF, 0x62, 0xD4, 0x97, 0x05, 0x24, 0x2A, 0x9A, 0xF9,
	0xE6, 0x1E, 0x85, 0xDC, 0x0D, 0x65, 0x1E, 0x40, 0xDF, 0xCF, 0x01, 0x7B,
	0x45, 0x57, 0x58, 0x87
};

/* The forth test vector is too expensive to include it in the tests. */
#if 0
static const unsigned char testVector4[64] = {
	0x21, 0x01, 0xCB, 0x9B, 0x6A, 0x51, 0x1A, 0xAE, 0xAD, 0xDB, 0xBE, 0x09,
	0xCF, 0x70, 0xF8, 0x81, 0xEC, 0x56, 0x8D, 0x57, 0x4A, 0x2F, 0xFD, 0x4D,
	0xAB, 0xE5, 0xEE, 0x98, 0x20, 0xAD, 0xAA, 0x47, 0x8E, 0x56, 0xFD, 0x8F,
	0x4B, 0xA5, 0xD0, 0x9F, 0xFA, 0x1C, 0x6D, 0x92, 0x7C, 0x40, 0xF4, 0xC3,
	0x37, 0x30, 0x40, 0x49, 0xE8, 0xA9, 0x52, 0xFB, 0xCB, 0xF4, 0x5C, 0x6F,
	0xA7, 0x7A, 0x41, 0xA4
};
#endif

@implementation TestsAppDelegate (ScryptTests)
- (void)scryptTests
{
	void *pool = objc_autoreleasePoolPush();
	uint32_t salsa20Buffer[16];
	uint32_t blockMixBuffer[32];
	uint32_t ROMixBuffer[32], ROMixTmp[17 * 32];
	unsigned char output[64];

	TEST(@"Salsa20/8 Core",
	    R(memcpy(salsa20Buffer, salsa20Input, 64)) &&
	    R(of_salsa20_8_core(salsa20Buffer)) &&
	    memcmp(salsa20Buffer, salsa20Output, 64) == 0)

	TEST(@"Block mix",
	    R(of_scrypt_block_mix(blockMixBuffer, blockMixInput.u32, 1)) &&
	    memcmp(blockMixBuffer, blockMixOutput, 128) == 0)

	TEST(@"ROMix",
	    R(memcpy(ROMixBuffer, ROMixInput, 128)) &&
	    R(of_scrypt_romix(ROMixBuffer, 1, 16, ROMixTmp)) &&
	    memcmp(ROMixBuffer, ROMixOutput, 128) == 0)

	TEST(@"scrypt test vector #1",
	    R(of_scrypt((of_scrypt_parameters_t){
		.blockSize             = 1,
		.costFactor            = 16,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"",
		.saltLength            = 0,
		.password              = "",
		.passwordLength        = 0,
		.key                   = output,
		.keyLength             = 64,
		.allowsSwappableMemory = true
	    })) && memcmp(output, testVector1, 64) == 0)

	TEST(@"scrypt test vector #2",
	    R(of_scrypt((of_scrypt_parameters_t){
		.blockSize             = 8,
		.costFactor            = 1024,
		.parallelization       = 16,
		.salt                  = (unsigned char *)"NaCl",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = output,
		.keyLength             = 64,
		.allowsSwappableMemory = true
	    })) && memcmp(output, testVector2, 64) == 0)



	TEST(@"scrypt test vector #3",
	    R(of_scrypt((of_scrypt_parameters_t){
		.blockSize             = 8,
		.costFactor            = 16384,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"SodiumChloride",
		.saltLength            = 14,
		.password              = "pleaseletmein",
		.passwordLength        = 13,
		.key                   = output,
		.keyLength             = 64,
		.allowsSwappableMemory = true
	    })) && memcmp(output, testVector3, 64) == 0)


	/* The forth test vector is too expensive to include it in the tests. */
#if 0
	TEST(@"scrypt test vector #4",
	    R(of_scrypt((of_scrypt_parameters_t){
		.blockSize             = 8,
		.costFactor            = 1048576,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"SodiumChloride",
		.saltLength            = 14,
		.password              = "pleaseletmein",
		.passwordLength        = 13,







>
>








>












|










|



|




|



|













|












>
>

|











>




|







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
	0xFD, 0xBA, 0xBE, 0x1C, 0x9D, 0x34, 0x72, 0x00, 0x78, 0x56, 0xE7, 0x19,
	0x0D, 0x01, 0xE9, 0xFE, 0x7C, 0x6A, 0xD7, 0xCB, 0xC8, 0x23, 0x78, 0x30,
	0xE7, 0x73, 0x76, 0x63, 0x4B, 0x37, 0x31, 0x62, 0x2E, 0xAF, 0x30, 0xD9,
	0x2E, 0x22, 0xA3, 0x88, 0x6F, 0xF1, 0x09, 0x27, 0x9D, 0x98, 0x30, 0xDA,
	0xC7, 0x27, 0xAF, 0xB9, 0x4A, 0x83, 0xEE, 0x6D, 0x83, 0x60, 0xCB, 0xDF,
	0xA2, 0xCC, 0x06, 0x40
};
/* The third test vector is too expensive for m68k. */
#ifndef OF_M68K
static const unsigned char testVector3[64] = {
	0x70, 0x23, 0xBD, 0xCB, 0x3A, 0xFD, 0x73, 0x48, 0x46, 0x1C, 0x06, 0xCD,
	0x81, 0xFD, 0x38, 0xEB, 0xFD, 0xA8, 0xFB, 0xBA, 0x90, 0x4F, 0x8E, 0x3E,
	0xA9, 0xB5, 0x43, 0xF6, 0x54, 0x5D, 0xA1, 0xF2, 0xD5, 0x43, 0x29, 0x55,
	0x61, 0x3F, 0x0F, 0xCF, 0x62, 0xD4, 0x97, 0x05, 0x24, 0x2A, 0x9A, 0xF9,
	0xE6, 0x1E, 0x85, 0xDC, 0x0D, 0x65, 0x1E, 0x40, 0xDF, 0xCF, 0x01, 0x7B,
	0x45, 0x57, 0x58, 0x87
};
#endif
/* The forth test vector is too expensive to include it in the tests. */
#if 0
static const unsigned char testVector4[64] = {
	0x21, 0x01, 0xCB, 0x9B, 0x6A, 0x51, 0x1A, 0xAE, 0xAD, 0xDB, 0xBE, 0x09,
	0xCF, 0x70, 0xF8, 0x81, 0xEC, 0x56, 0x8D, 0x57, 0x4A, 0x2F, 0xFD, 0x4D,
	0xAB, 0xE5, 0xEE, 0x98, 0x20, 0xAD, 0xAA, 0x47, 0x8E, 0x56, 0xFD, 0x8F,
	0x4B, 0xA5, 0xD0, 0x9F, 0xFA, 0x1C, 0x6D, 0x92, 0x7C, 0x40, 0xF4, 0xC3,
	0x37, 0x30, 0x40, 0x49, 0xE8, 0xA9, 0x52, 0xFB, 0xCB, 0xF4, 0x5C, 0x6F,
	0xA7, 0x7A, 0x41, 0xA4
};
#endif

@implementation TestsAppDelegate (OFScryptTests)
- (void)scryptTests
{
	void *pool = objc_autoreleasePoolPush();
	uint32_t salsa20Buffer[16];
	uint32_t blockMixBuffer[32];
	uint32_t ROMixBuffer[32], ROMixTmp[17 * 32];
	unsigned char output[64];

	TEST(@"Salsa20/8 Core",
	    R(memcpy(salsa20Buffer, salsa20Input, 64)) &&
	    R(OFSalsa20_8Core(salsa20Buffer)) &&
	    memcmp(salsa20Buffer, salsa20Output, 64) == 0)

	TEST(@"Block mix",
	    R(OFScryptBlockMix(blockMixBuffer, blockMixInput.u32, 1)) &&
	    memcmp(blockMixBuffer, blockMixOutput, 128) == 0)

	TEST(@"ROMix",
	    R(memcpy(ROMixBuffer, ROMixInput, 128)) &&
	    R(OFScryptROMix(ROMixBuffer, 1, 16, ROMixTmp)) &&
	    memcmp(ROMixBuffer, ROMixOutput, 128) == 0)

	TEST(@"scrypt test vector #1",
	    R(OFScrypt((OFScryptParameters){
		.blockSize             = 1,
		.costFactor            = 16,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"",
		.saltLength            = 0,
		.password              = "",
		.passwordLength        = 0,
		.key                   = output,
		.keyLength             = 64,
		.allowsSwappableMemory = true
	    })) && memcmp(output, testVector1, 64) == 0)

	TEST(@"scrypt test vector #2",
	    R(OFScrypt((OFScryptParameters){
		.blockSize             = 8,
		.costFactor            = 1024,
		.parallelization       = 16,
		.salt                  = (unsigned char *)"NaCl",
		.saltLength            = 4,
		.password              = "password",
		.passwordLength        = 8,
		.key                   = output,
		.keyLength             = 64,
		.allowsSwappableMemory = true
	    })) && memcmp(output, testVector2, 64) == 0)

	/* The third test vector is too expensive for m68k. */
#ifndef OF_M68K
	TEST(@"scrypt test vector #3",
	    R(OFScrypt((OFScryptParameters){
		.blockSize             = 8,
		.costFactor            = 16384,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"SodiumChloride",
		.saltLength            = 14,
		.password              = "pleaseletmein",
		.passwordLength        = 13,
		.key                   = output,
		.keyLength             = 64,
		.allowsSwappableMemory = true
	    })) && memcmp(output, testVector3, 64) == 0)
#endif

	/* The forth test vector is too expensive to include it in the tests. */
#if 0
	TEST(@"scrypt test vector #4",
	    R(OFScrypt((OFScryptParameters){
		.blockSize             = 8,
		.costFactor            = 1048576,
		.parallelization       = 1,
		.salt                  = (unsigned char *)"SodiumChloride",
		.saltLength            = 14,
		.password              = "pleaseletmein",
		.passwordLength        = 13,

Modified tests/OFSerializationTests.m from [50a2e2daf3] to [5fdb00c13b].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFSerialization";

@implementation TestsAppDelegate (OFSerializationTests)
- (void)serializationTests
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary *d = [OFMutableDictionary dictionary];
	OFMutableArray *a = [OFMutableArray array];
	OFList *l = [OFList list];
	OFData *data;
	OFString *s;


	[a addObject: @"Qu\"xbar\ntest"];
	[a addObject: [OFNumber numberWithInt: 1234]];
	[a addObject: [OFNumber numberWithDouble: 1234.5678]];
	[a addObject: [OFMutableString stringWithString: @"asd"]];
	[a addObject: [OFDate dateWithTimeIntervalSince1970: 1234.5678]];

	[d setObject: @"Hello"
	      forKey: a];
	[d setObject: @"B\"la"
	      forKey: @"Blub"];

	[l appendObject: @"Hello"];
	[l appendObject: @"Wo\rld!\nHow are you?"];
	[l appendObject: [OFURL URLWithString: @"https://objfw.nil.im/"]];
	[l appendObject:
	    [OFXMLElement elementWithXMLString: @"<x><y/><![CDATA[<]]></x>"]];

	[l appendObject: [OFSet setWithObjects: @"foo", @"foo", @"bar", nil]];
	[l appendObject:
	    [OFCountedSet setWithObjects: @"foo", @"foo", @"bar", nil]];

	[d setObject: @"list"
	      forKey: l];

	data = [OFData dataWithItems: "0123456789:;<ABCDEFGHJIKLMNOPQRSTUVWXYZ"
			       count: 39];
	[d setObject: @"data"
	      forKey: data];




	TEST(@"-[stringBySerializing]",
	    (s = d.stringBySerializing) && [s isEqual:
	    [OFString stringWithContentsOfFile: @"serialization.xml"]])

	TEST(@"-[objectByDeserializing]", [s.objectByDeserializing isEqual: d])


	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|





|
|
|

|
>

|
|
|
|
|

|
<
|
<

|
|
|
|

>
|
|


|
<



|
|
>
>
>


|


|
>




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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFSerialization";

@implementation TestsAppDelegate (OFSerializationTests)
- (void)serializationTests
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary *dict = [OFMutableDictionary dictionary];
	OFMutableArray *array = [OFMutableArray array];
	OFList *list = [OFList list];
	OFData *data;
	OFString *string;
	OFUUID *UUID;

	[array addObject: @"Qu\"xbar\ntest"];
	[array addObject: [OFNumber numberWithInt: 1234]];
	[array addObject: [OFNumber numberWithDouble: 1234.5678]];
	[array addObject: [OFMutableString stringWithString: @"asd"]];
	[array addObject: [OFDate dateWithTimeIntervalSince1970: 1234.5678]];

	[dict setObject: @"Hello" forKey: array];

	[dict setObject: @"B\"la" forKey: @"Blub"];


	[list appendObject: @"Hello"];
	[list appendObject: @"Wo\rld!\nHow are you?"];
	[list appendObject: [OFURL URLWithString: @"https://objfw.nil.im/"]];
	[list appendObject:
	    [OFXMLElement elementWithXMLString: @"<x><y/><![CDATA[<]]></x>"]];
	[list appendObject:
	    [OFSet setWithObjects: @"foo", @"foo", @"bar", nil]];
	[list appendObject:
	    [OFCountedSet setWithObjects: @"foo", @"foo", @"bar", nil]];

	[dict setObject: @"list" forKey: list];


	data = [OFData dataWithItems: "0123456789:;<ABCDEFGHJIKLMNOPQRSTUVWXYZ"
			       count: 39];
	[dict setObject: @"data" forKey: data];

	UUID = [OFUUID
	    UUIDWithUUIDString: @"01234567-89AB-CDEF-FEDC-BA9876543210"];
	[dict setObject: @"uuid" forKey: UUID];

	TEST(@"-[stringBySerializing]",
	    (string = dict.stringBySerializing) && [string isEqual:
	    [OFString stringWithContentsOfFile: @"serialization.xml"]])

	TEST(@"-[objectByDeserializing]",
	    [string.objectByDeserializing isEqual: dict])

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSetTests.m from [1aa6d32b7b] to [d6c98b6473].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

#import "TestsAppDelegate.h"

#import "OFSet.h"
#import "OFMapTableSet.h"
#import "OFMutableMapTableSet.h"

static OFString *module = nil;

@interface SimpleSet: OFSet
{
	OFMutableSet *_set;
}
@end








|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#import "TestsAppDelegate.h"

#import "OFSet.h"
#import "OFMapTableSet.h"
#import "OFMutableMapTableSet.h"

static OFString *module;

@interface SimpleSet: OFSet
{
	OFMutableSet *_set;
}
@end

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)firstObject
		     arguments: (va_list)arguments
{
	self = [super init];

	@try {
		_set = [[OFMutableSet alloc] initWithObject: firstObject
						  arguments: arguments];
	} @catch (id e) {







|
<







75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithObject: (id)firstObject arguments: (va_list)arguments

{
	self = [super init];

	@try {
		_set = [[OFMutableSet alloc] initWithObject: firstObject
						  arguments: arguments];
	} @catch (id e) {
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

	[_set removeObject: object];

	if (existed)
		_mutations++;
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t *)state
			   objects: (id *)objects
			     count: (int)count
{
	int ret = [_set countByEnumeratingWithState: state
					    objects: objects
					      count: count];

	state->mutationsPtr = &_mutations;

	return ret;
}
@end

@implementation TestsAppDelegate (OFSetTests)
- (void)setTestsWithClass: (Class)setClass
	     mutableClass: (Class)mutableSetClass
{
	void *pool = objc_autoreleasePoolPush();
	OFSet *set1, *set2;
	OFMutableSet *mutableSet;
	bool ok;
	size_t i;








|














|
<







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

	[_set removeObject: object];

	if (existed)
		_mutations++;
}

- (int)countByEnumeratingWithState: (OFFastEnumerationState *)state
			   objects: (id *)objects
			     count: (int)count
{
	int ret = [_set countByEnumeratingWithState: state
					    objects: objects
					      count: count];

	state->mutationsPtr = &_mutations;

	return ret;
}
@end

@implementation TestsAppDelegate (OFSetTests)
- (void)setTestsWithClass: (Class)setClass mutableClass: (Class)mutableSetClass

{
	void *pool = objc_autoreleasePoolPush();
	OFSet *set1, *set2;
	OFMutableSet *mutableSet;
	bool ok;
	size_t i;

Renamed and modified tests/SocketTests.m [1f29b20927] to tests/OFSocketTests.m [3d043537f3].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	a.sockaddr.in6.sin6_addr.s6_addr[10] = a5 >> 8;		\
	a.sockaddr.in6.sin6_addr.s6_addr[11] = a5 & 0xFF;	\
	a.sockaddr.in6.sin6_addr.s6_addr[12] = a6 >> 8;		\
	a.sockaddr.in6.sin6_addr.s6_addr[13] = a6 & 0xFF;	\
	a.sockaddr.in6.sin6_addr.s6_addr[14] = a7 >> 8;		\
	a.sockaddr.in6.sin6_addr.s6_addr[15] = a7 & 0xFF;

static OFString *module = @"Socket";

@implementation TestsAppDelegate (SocketTests)
- (void)socketTests
{
	void *pool = objc_autoreleasePoolPush();
	of_socket_address_t addr;
	uint16_t port;

	TEST(@"Parsing an IPv4",
	    R(addr = of_socket_address_parse_ip(@"127.0.0.1", 1234)) &&
	    OF_BSWAP32_IF_LE(addr.sockaddr.in.sin_addr.s_addr) == 0x7F000001 &&
	    OF_BSWAP16_IF_LE(addr.sockaddr.in.sin_port) == 1234)

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #1",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"127.0.0.0.1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #2",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"127.0.0.256", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #3",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"127.0.0. 1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #4",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@" 127.0.0.1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #5",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"127.0.a.1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #6",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"127.0..1", 1234))



	TEST(@"Converting an IPv4 to a string",
	    [of_socket_address_ip_string(&addr, &port) isEqual: @"127.0.0.1"] &&
	    port == 1234)

	TEST(@"Parsing an IPv6 #1",
	    R(addr = of_socket_address_parse_ip(
	    @"1122:3344:5566:7788:99aa:bbCc:ddee:ff00", 1234)) &&
	    COMPARE_V6(addr,
	    0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) &&
	    OF_BSWAP16_IF_LE(addr.sockaddr.in6.sin6_port) == 1234)

	TEST(@"Parsing an IPv6 #2",
	    R(addr = of_socket_address_parse_ip(@"::", 1234)) &&
	    COMPARE_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0) &&
	    OF_BSWAP16_IF_LE(addr.sockaddr.in6.sin6_port) == 1234)

	TEST(@"Parsing an IPv6 #3",
	    R(addr = of_socket_address_parse_ip(@"aaAa::bBbb", 1234)) &&
	    COMPARE_V6(addr, 0xAAAA, 0, 0, 0, 0, 0, 0, 0xBBBB) &&
	    OF_BSWAP16_IF_LE(addr.sockaddr.in6.sin6_port) == 1234)

	TEST(@"Parsing an IPv6 #4",
	    R(addr = of_socket_address_parse_ip(@"aaAa::", 1234)) &&
	    COMPARE_V6(addr, 0xAAAA, 0, 0, 0, 0, 0, 0, 0) &&
	    OF_BSWAP16_IF_LE(addr.sockaddr.in6.sin6_port) == 1234)

	TEST(@"Parsing an IPv6 #5",
	    R(addr = of_socket_address_parse_ip(@"::aaAa", 1234)) &&
	    COMPARE_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0xAAAA) &&
	    OF_BSWAP16_IF_LE(addr.sockaddr.in6.sin6_port) == 1234)

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #1",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"1:::2", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #2",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"1: ::2", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #3",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"1:: :2", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #4",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"1::2::3", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #5",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"10000::1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #6",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"::10000", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #7",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"::1::", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #8",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"1:2:3:4:5:6:7:", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #9",
	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"1:2:3:4:5:6:7::", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #10",

	    OFInvalidFormatException,
	    of_socket_address_parse_ip(@"1:2", 1234))

	SET_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0)
	TEST(@"Converting an IPv6 to a string #1",
	    [of_socket_address_ip_string(&addr, &port) isEqual: @"::"] &&
	    port == 1234)

	SET_V6(addr, 0, 0, 0, 0, 0, 0, 0, 1)
	TEST(@"Converting an IPv6 to a string #2",
	    [of_socket_address_ip_string(&addr, &port) isEqual: @"::1"] &&
	    port == 1234)

	SET_V6(addr, 1, 0, 0, 0, 0, 0, 0, 0)
	TEST(@"Converting an IPv6 to a string #3",
	    [of_socket_address_ip_string(&addr, &port) isEqual: @"1::"] &&
	    port == 1234)

	SET_V6(addr,
	    0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #4",
	    [of_socket_address_ip_string(&addr, &port) isEqual:
	    @"1122:3344:5566:7788:99aa:bbcc:ddee:ff00"] &&
	    port == 1234)

	SET_V6(addr, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0)
	TEST(@"Converting an IPv6 to a string #5",
	    [of_socket_address_ip_string(&addr, &port) isEqual:
	    @"1122:3344:5566:7788:99aa:bbcc:ddee:0"] &&
	    port == 1234)

	SET_V6(addr, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0)
	TEST(@"Converting an IPv6 to a string #6",
	    [of_socket_address_ip_string(&addr, &port) isEqual:
	    @"1122:3344:5566:7788:99aa:bbcc::"] &&
	    port == 1234)

	SET_V6(addr, 0, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #7",
	    [of_socket_address_ip_string(&addr, &port) isEqual:
	    @"0:3344:5566:7788:99aa:bbcc:ddee:ff00"] &&
	    port == 1234)

	SET_V6(addr, 0, 0, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #8",
	    [of_socket_address_ip_string(&addr, &port) isEqual:
	    @"::5566:7788:99aa:bbcc:ddee:ff00"] &&
	    port == 1234)

	SET_V6(addr, 0, 0, 0x5566, 0, 0, 0, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #9",
	    [of_socket_address_ip_string(&addr, &port) isEqual:
	    @"0:0:5566::ddee:ff00"] &&
	    port == 1234)

	SET_V6(addr, 0, 0, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0)
	TEST(@"Converting an IPv6 to a string #10",
	    [of_socket_address_ip_string(&addr, &port) isEqual:
	    @"::5566:7788:99aa:bbcc:0:0"] &&
	    port == 1234)

	objc_autoreleasePoolPop(pool);
}
@end







|

|



|
<


|
|
|

|
<
|

|
<
|

|
<
|

|
<
|

|
<
|

|
<
|

>
>

|
<


|



|


|

|


|

|


|

|


|

|

|
<
|

|
<
|

|
<
|

|
<
|

|
<
|

|
<
|

|
<
|

|
<
|

|
<
|

|
>
|
|



|
<



|
<



|
<




|
|
<



|
|
<



|
|
<



|
|
<



|
|
<



<
|
<



|
|
<




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
	a.sockaddr.in6.sin6_addr.s6_addr[10] = a5 >> 8;		\
	a.sockaddr.in6.sin6_addr.s6_addr[11] = a5 & 0xFF;	\
	a.sockaddr.in6.sin6_addr.s6_addr[12] = a6 >> 8;		\
	a.sockaddr.in6.sin6_addr.s6_addr[13] = a6 & 0xFF;	\
	a.sockaddr.in6.sin6_addr.s6_addr[14] = a7 >> 8;		\
	a.sockaddr.in6.sin6_addr.s6_addr[15] = a7 & 0xFF;

static OFString *const module = @"OFSocket";

@implementation TestsAppDelegate (OFSocketTests)
- (void)socketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFSocketAddress addr;


	TEST(@"Parsing an IPv4",
	    R(addr = OFSocketAddressParseIP(@"127.0.0.1", 1234)) &&
	    OFFromBigEndian32(addr.sockaddr.in.sin_addr.s_addr) == 0x7F000001 &&
	    OFFromBigEndian16(addr.sockaddr.in.sin_port) == 1234)

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #1", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"127.0.0.0.1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #2", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"127.0.0.256", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #3", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"127.0.0. 1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #4", OFInvalidFormatException,

	    OFSocketAddressParseIP(@" 127.0.0.1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #5", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"127.0.a.1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv4 #6", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"127.0..1", 1234))

	TEST(@"Port of an IPv4 address", OFSocketAddressPort(&addr) == 1234)

	TEST(@"Converting an IPv4 to a string",
	    [OFSocketAddressString(&addr) isEqual: @"127.0.0.1"])


	TEST(@"Parsing an IPv6 #1",
	    R(addr = OFSocketAddressParseIP(
	    @"1122:3344:5566:7788:99aa:bbCc:ddee:ff00", 1234)) &&
	    COMPARE_V6(addr,
	    0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00) &&
	    OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234)

	TEST(@"Parsing an IPv6 #2",
	    R(addr = OFSocketAddressParseIP(@"::", 1234)) &&
	    COMPARE_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0) &&
	    OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234)

	TEST(@"Parsing an IPv6 #3",
	    R(addr = OFSocketAddressParseIP(@"aaAa::bBbb", 1234)) &&
	    COMPARE_V6(addr, 0xAAAA, 0, 0, 0, 0, 0, 0, 0xBBBB) &&
	    OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234)

	TEST(@"Parsing an IPv6 #4",
	    R(addr = OFSocketAddressParseIP(@"aaAa::", 1234)) &&
	    COMPARE_V6(addr, 0xAAAA, 0, 0, 0, 0, 0, 0, 0) &&
	    OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234)

	TEST(@"Parsing an IPv6 #5",
	    R(addr = OFSocketAddressParseIP(@"::aaAa", 1234)) &&
	    COMPARE_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0xAAAA) &&
	    OFFromBigEndian16(addr.sockaddr.in6.sin6_port) == 1234)

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #1", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"1:::2", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #2", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"1: ::2", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #3", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"1:: :2", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #4", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"1::2::3", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #5", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"10000::1", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #6", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"::10000", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #7", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"::1::", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #8", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"1:2:3:4:5:6:7:", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #9", OFInvalidFormatException,

	    OFSocketAddressParseIP(@"1:2:3:4:5:6:7::", 1234))

	EXPECT_EXCEPTION(@"Refusing invalid IPv6 #10", OFInvalidFormatException,
	    OFSocketAddressParseIP(@"1:2", 1234))

	TEST(@"Port of an IPv6 address", OFSocketAddressPort(&addr) == 1234)

	SET_V6(addr, 0, 0, 0, 0, 0, 0, 0, 0)
	TEST(@"Converting an IPv6 to a string #1",
	    [OFSocketAddressString(&addr) isEqual: @"::"])


	SET_V6(addr, 0, 0, 0, 0, 0, 0, 0, 1)
	TEST(@"Converting an IPv6 to a string #2",
	    [OFSocketAddressString(&addr) isEqual: @"::1"])


	SET_V6(addr, 1, 0, 0, 0, 0, 0, 0, 0)
	TEST(@"Converting an IPv6 to a string #3",
	    [OFSocketAddressString(&addr) isEqual: @"1::"])


	SET_V6(addr,
	    0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #4",
	    [OFSocketAddressString(&addr)
	    isEqual: @"1122:3344:5566:7788:99aa:bbcc:ddee:ff00"])


	SET_V6(addr, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0)
	TEST(@"Converting an IPv6 to a string #5",
	    [OFSocketAddressString(&addr)
	    isEqual: @"1122:3344:5566:7788:99aa:bbcc:ddee:0"])


	SET_V6(addr, 0x1122, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0)
	TEST(@"Converting an IPv6 to a string #6",
	    [OFSocketAddressString(&addr)
	    isEqual: @"1122:3344:5566:7788:99aa:bbcc::"])


	SET_V6(addr, 0, 0x3344, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #7",
	    [OFSocketAddressString(&addr)
	    isEqual: @"0:3344:5566:7788:99aa:bbcc:ddee:ff00"])


	SET_V6(addr, 0, 0, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #8",
	    [OFSocketAddressString(&addr)
	    isEqual: @"::5566:7788:99aa:bbcc:ddee:ff00"])


	SET_V6(addr, 0, 0, 0x5566, 0, 0, 0, 0xDDEE, 0xFF00)
	TEST(@"Converting an IPv6 to a string #9",

	    [OFSocketAddressString(&addr) isEqual: @"0:0:5566::ddee:ff00"])


	SET_V6(addr, 0, 0, 0x5566, 0x7788, 0x99AA, 0xBBCC, 0, 0)
	TEST(@"Converting an IPv6 to a string #10",
	    [OFSocketAddressString(&addr)
	    isEqual: @"::5566:7788:99aa:bbcc:0:0"])


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFStreamTests.m from [3c0c93a949] to [12c9753c4d].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFStream";

@interface StreamTester: OFStream
{
	int state;
}
@end

@implementation StreamTester
- (bool)lowlevelIsAtEndOfStream
{
	return (state > 1);
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)size
{
	size_t pageSize = [OFSystemInfo pageSize];

	switch (state) {
	case 0:
		if (size < 1)
			return 0;

<
<
|

















|

|





|





|
<







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFStream";

@interface StreamTest: OFStream
{
	int state;
}
@end

@implementation StreamTest
- (bool)lowlevelIsAtEndOfStream
{
	return (state > 1);
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)size

{
	size_t pageSize = [OFSystemInfo pageSize];

	switch (state) {
	case 0:
		if (size < 1)
			return 0;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77



78
79
80


81
82
83
84
@end

@implementation TestsAppDelegate (OFStreamTests)
- (void)streamTests
{
	void *pool = objc_autoreleasePoolPush();
	size_t pageSize = [OFSystemInfo pageSize];
	StreamTester *t = [[[StreamTester alloc] init] autorelease];
	OFString *str;
	char *cstr;

	cstr = [t allocMemoryWithSize: pageSize - 2];
	memset(cstr, 'X', pageSize - 3);
	cstr[pageSize - 3] = '\0';




	TEST(@"-[readLine]", [[t readLine] isEqual: @"foo"] &&
	    [(str = [t readLine]) length] == pageSize - 3 &&
	    !strcmp(str.UTF8String, cstr))



	objc_autoreleasePoolPop(pool);
}
@end







|
|
|

|
|
|

>
>
>
|
|
|
>
>




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
@end

@implementation TestsAppDelegate (OFStreamTests)
- (void)streamTests
{
	void *pool = objc_autoreleasePoolPush();
	size_t pageSize = [OFSystemInfo pageSize];
	StreamTest *test = [[[StreamTest alloc] init] autorelease];
	OFString *string;
	char *cString;

	cString = OFAllocMemory(pageSize - 2, 1);
	memset(cString, 'X', pageSize - 3);
	cString[pageSize - 3] = '\0';

	TEST(@"-[readLine] #1", [[test readLine] isEqual: @"foo"])

	string = [test readLine];
	TEST(@"-[readLine] #2", string != nil &&
	    string.length == pageSize - 3 &&
	    !strcmp(string.UTF8String, cString))

	OFFreeMemory(cString);

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFStringTests.m from [1ca0b0e0e8] to [7a4f20e376].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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

#import "TestsAppDelegate.h"

#import "OFString.h"
#import "OFMutableUTF8String.h"
#import "OFUTF8String.h"





static OFString *module = nil;
static OFString *whitespace[] = {
	@" \r \t\n\t \tasd  \t \t\t\r\n",
	@" \t\t  \t\t  \t \t"
};
static of_unichar_t ucstr[] = {
	0xFEFF, 'f', 0xF6, 0xF6, 'b', 0xE4, 'r', 0x1F03A, 0
};
static of_unichar_t sucstr[] = {
	0xFFFE0000, 0x66000000, 0xF6000000, 0xF6000000, 0x62000000, 0xE4000000,
	0x72000000, 0x3AF00100, 0
};
static uint16_t utf16str[] = {
	0xFEFF, 'f', 0xF6, 0xF6, 'b', 0xE4, 'r', 0xD83C, 0xDC3A, 0
};
static uint16_t sutf16str[] = {
	0xFFFE, 0x6600, 0xF600, 0xF600, 0x6200, 0xE400, 0x7200, 0x3CD8, 0x3ADC,
	0
};

@interface SimpleString: OFString
{
	OFMutableString *_string;







>
>
>
>
|
|



|


|



|


|







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

#import "TestsAppDelegate.h"

#import "OFString.h"
#import "OFMutableUTF8String.h"
#import "OFUTF8String.h"

#ifndef INFINITY
# define INFINITY __builtin_inf()
#endif

static OFString *module;
static OFString *const whitespace[] = {
	@" \r \t\n\t \tasd  \t \t\t\r\n",
	@" \t\t  \t\t  \t \t"
};
static const OFUnichar unicharString[] = {
	0xFEFF, 'f', 0xF6, 0xF6, 'b', 0xE4, 'r', 0x1F03A, 0
};
static const OFUnichar swappedUnicharString[] = {
	0xFFFE0000, 0x66000000, 0xF6000000, 0xF6000000, 0x62000000, 0xE4000000,
	0x72000000, 0x3AF00100, 0
};
static const OFChar16 char16String[] = {
	0xFEFF, 'f', 0xF6, 0xF6, 'b', 0xE4, 'r', 0xD83C, 0xDC3A, 0
};
static const OFChar16 swappedChar16String[] = {
	0xFFFE, 0x6600, 0xF600, 0xF600, 0x6200, 0xE400, 0x7200, 0x3CD8, 0x3ADC,
	0
};

@interface SimpleString: OFString
{
	OFMutableString *_string;
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
		@throw e;
	}

	return self;
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (of_string_encoding_t)encoding
			 length: (size_t)length
{
	self = [super init];

	@try {
		_string = [[OFMutableString alloc] initWithCString: cString
							  encoding: encoding
							    length: length];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithUTF16String: (const of_char16_t *)UTF16String
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	self = [super init];

	@try {
		_string = [[OFMutableString alloc]
		    initWithUTF16String: UTF16String
				 length: length
			      byteOrder: byteOrder];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithUTF32String: (const of_char32_t *)UTF32String
			     length: (size_t)length
			  byteOrder: (of_byte_order_t)byteOrder
{
	self = [super init];

	@try {
		_string = [[OFMutableString alloc]
		    initWithUTF32String: UTF32String
				 length: length







|
















|

|
















|

|







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
		@throw e;
	}

	return self;
}

- (instancetype)initWithCString: (const char *)cString
		       encoding: (OFStringEncoding)encoding
			 length: (size_t)length
{
	self = [super init];

	@try {
		_string = [[OFMutableString alloc] initWithCString: cString
							  encoding: encoding
							    length: length];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithUTF16String: (const OFChar16 *)UTF16String
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	self = [super init];

	@try {
		_string = [[OFMutableString alloc]
		    initWithUTF16String: UTF16String
				 length: length
			      byteOrder: byteOrder];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithUTF32String: (const OFChar32 *)UTF32String
			     length: (size_t)length
			  byteOrder: (OFByteOrder)byteOrder
{
	self = [super init];

	@try {
		_string = [[OFMutableString alloc]
		    initWithUTF32String: UTF32String
				 length: length
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
- (void)dealloc
{
	[_string release];

	[super dealloc];
}

- (of_unichar_t)characterAtIndex: (size_t)idx
{
	return [_string characterAtIndex: idx];
}

- (size_t)length
{
	return _string.length;
}
@end

@implementation SimpleMutableString
+ (void)initialize
{
	if (self == [SimpleMutableString class])
		[self inheritMethodsFromClass: [SimpleString class]];
}

- (void)replaceCharactersInRange: (of_range_t)range
		      withString: (OFString *)string
{
	[_string replaceCharactersInRange: range
			       withString: string];
}
@end

@interface EntityHandler: OFObject <OFStringXMLUnescapingDelegate>
@end

@implementation EntityHandler







|

















|


|
<







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
- (void)dealloc
{
	[_string release];

	[super dealloc];
}

- (OFUnichar)characterAtIndex: (size_t)idx
{
	return [_string characterAtIndex: idx];
}

- (size_t)length
{
	return _string.length;
}
@end

@implementation SimpleMutableString
+ (void)initialize
{
	if (self == [SimpleMutableString class])
		[self inheritMethodsFromClass: [SimpleString class]];
}

- (void)replaceCharactersInRange: (OFRange)range
		      withString: (OFString *)string
{
	[_string replaceCharactersInRange: range withString: string];

}
@end

@interface EntityHandler: OFObject <OFStringXMLUnescapingDelegate>
@end

@implementation EntityHandler
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
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
@end

@implementation TestsAppDelegate (OFStringTests)
- (void)stringTestsWithClass: (Class)stringClass
		mutableClass: (Class)mutableStringClass
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableString *s[3];
	OFString *is;
	OFArray *a;
	size_t i;
	const of_unichar_t *ua;
	const uint16_t *u16a;
	OFCharacterSet *cs;
	EntityHandler *h;
#ifdef OF_HAVE_BLOCKS
	__block int j;
	__block bool ok;
#endif

#define C(s) ((OFString *)[stringClass stringWithString: s])

	s[0] = [mutableStringClass stringWithString: @"täs€"];
	s[1] = [mutableStringClass string];
	s[2] = [[s[0] copy] autorelease];

	TEST(@"-[isEqual:]", [s[0] isEqual: s[2]] &&
	    ![s[0] isEqual: [[[OFObject alloc] init] autorelease]])

	TEST(@"-[compare:]", [s[0] compare: s[2]] == OF_ORDERED_SAME &&

	    [s[0] compare: @""] != OF_ORDERED_SAME &&
	    [C(@"") compare: @"a"] == OF_ORDERED_ASCENDING &&
	    [C(@"a") compare: @"b"] == OF_ORDERED_ASCENDING &&
	    [C(@"cd") compare: @"bc"] == OF_ORDERED_DESCENDING &&
	    [C(@"ä") compare: @"ö"] == OF_ORDERED_ASCENDING &&
	    [C(@"€") compare: @"ß"] == OF_ORDERED_DESCENDING &&
	    [C(@"aa") compare: @"z"] == OF_ORDERED_ASCENDING)

#ifdef OF_HAVE_UNICODE_TABLES
	TEST(@"-[caseInsensitiveCompare:]",
	    [C(@"a") caseInsensitiveCompare: @"A"] == OF_ORDERED_SAME &&
	    [C(@"Ä") caseInsensitiveCompare: @"ä"] == OF_ORDERED_SAME &&
	    [C(@"я") caseInsensitiveCompare: @"Я"] == OF_ORDERED_SAME &&
	    [C(@"€") caseInsensitiveCompare: @"ß"] == OF_ORDERED_DESCENDING &&
	    [C(@"ß") caseInsensitiveCompare: @"→"] == OF_ORDERED_ASCENDING &&
	    [C(@"AA") caseInsensitiveCompare: @"z"] == OF_ORDERED_ASCENDING &&
	    [[stringClass stringWithUTF8String: "ABC"] caseInsensitiveCompare:
	    [stringClass stringWithUTF8String: "AbD"]] ==
	    [C(@"abc") compare: @"abd"])
#else
	TEST(@"-[caseInsensitiveCompare:]",
	    [C(@"a") caseInsensitiveCompare: @"A"] == OF_ORDERED_SAME &&
	    [C(@"AA") caseInsensitiveCompare: @"z"] == OF_ORDERED_ASCENDING &&
	    [[stringClass stringWithUTF8String: "ABC"] caseInsensitiveCompare:
	    [stringClass stringWithUTF8String: "AbD"]] ==
	    [C(@"abc") compare: @"abd"])
#endif

	TEST(@"-[hash] is the same if -[isEqual:] is true",
	    s[0].hash == s[2].hash)

	TEST(@"-[description]", [s[0].description isEqual: s[0]])


	TEST(@"-[appendString:] and -[appendUTF8String:]",
	    R([s[1] appendUTF8String: "1𝄞"]) && R([s[1] appendString: @"3"]) &&


	    R([s[0] appendString: s[1]]) && [s[0] isEqual: @"täs€1𝄞3"])

	TEST(@"-[appendCharacters:length:]",
	    R([s[1] appendCharacters: ucstr + 6
			      length: 2]) && [s[1] isEqual: @"1𝄞3r🀺"])

	TEST(@"-[length]", s[0].length == 7)
	TEST(@"-[UTF8StringLength]", s[0].UTF8StringLength == 13)
	TEST(@"-[hash]", s[0].hash == 0x705583C0)

	TEST(@"-[characterAtIndex:]", [s[0] characterAtIndex: 0] == 't' &&

	    [s[0] characterAtIndex: 1] == 0xE4 &&
	    [s[0] characterAtIndex: 3] == 0x20AC &&
	    [s[0] characterAtIndex: 5] == 0x1D11E)

	EXPECT_EXCEPTION(@"Detect out of range in -[characterAtIndex:]",
	    OFOutOfRangeException, [s[0] characterAtIndex: 7])

	TEST(@"-[reverse]", R([s[0] reverse]) && [s[0] isEqual: @"3𝄞1€sät"])


	s[1] = [mutableStringClass stringWithString: @"abc"];

#ifdef OF_HAVE_UNICODE_TABLES
	TEST(@"-[uppercase]", R([s[0] uppercase]) &&
	    [s[0] isEqual: @"3𝄞1€SÄT"] &&
	    R([s[1] uppercase]) && [s[1] isEqual: @"ABC"])

	TEST(@"-[lowercase]", R([s[0] lowercase]) &&
	    [s[0] isEqual: @"3𝄞1€sät"] &&
	    R([s[1] lowercase]) && [s[1] isEqual: @"abc"])

	TEST(@"-[uppercaseString]",
	    [[s[0] uppercaseString] isEqual: @"3𝄞1€SÄT"])

	TEST(@"-[lowercaseString]", R([s[0] uppercase]) &&
	    [[s[0] lowercaseString] isEqual: @"3𝄞1€sät"])

	TEST(@"-[capitalizedString]", [C(@"džbla tdžst TDŽST").capitalizedString
	    isEqual: @"Džbla Tdžst Tdžst"])
#else
	TEST(@"-[uppercase]", R([s[0] uppercase]) &&
	    [s[0] isEqual: @"3𝄞1€SäT"] &&
	    R([s[1] uppercase]) && [s[1] isEqual: @"ABC"])

	TEST(@"-[lowercase]", R([s[0] lowercase]) &&
	    [s[0] isEqual: @"3𝄞1€sät"] &&
	    R([s[1] lowercase]) && [s[1] isEqual: @"abc"])

	TEST(@"-[uppercaseString]", [s[0].uppercaseString isEqual: @"3𝄞1€SäT"])


	TEST(@"-[lowercaseString]", R([s[0] uppercase]) &&

	    [s[0].lowercaseString isEqual: @"3𝄞1€sät"])

	TEST(@"-[capitalizedString]", [C(@"džbla tdžst TDŽST").capitalizedString
	    isEqual: @"džbla Tdžst TDŽst"])
#endif

	TEST(@"+[stringWithUTF8String:length:]",
	    (s[0] = [mutableStringClass stringWithUTF8String: "\xEF\xBB\xBF"
							      "foobar"
						      length: 6]) &&
	    [s[0] isEqual: @"foo"])

	TEST(@"+[stringWithUTF16String:]",
	    (is = [stringClass stringWithUTF16String: utf16str]) &&
	    [is isEqual: @"fööbär🀺"] &&
	    (is = [stringClass stringWithUTF16String: sutf16str]) &&
	    [is isEqual: @"fööbär🀺"])

	TEST(@"+[stringWithUTF32String:]",
	    (is = [stringClass stringWithUTF32String: ucstr]) &&
	    [is isEqual: @"fööbär🀺"] &&
	    (is = [stringClass stringWithUTF32String: sucstr]) &&
	    [is isEqual: @"fööbär🀺"])

#ifdef OF_HAVE_FILES
	TEST(@"+[stringWithContentsOfFile:encoding]", (is = [stringClass
	    stringWithContentsOfFile: @"testfile.txt"
			    encoding: OF_STRING_ENCODING_ISO_8859_1]) &&
	    [is isEqual: @"testäöü"])

	TEST(@"+[stringWithContentsOfURL:encoding]", (is = [stringClass
	    stringWithContentsOfURL: [OFURL fileURLWithPath: @"testfile.txt"]
			   encoding: OF_STRING_ENCODING_ISO_8859_1]) &&
	    [is isEqual: @"testäöü"])
#endif

	TEST(@"-[appendUTFString:length:]",
	    R([s[0] appendUTF8String: "\xEF\xBB\xBF" "barqux"
			      length: 6]) && [s[0] isEqual: @"foobar"])


	EXPECT_EXCEPTION(@"Detection of invalid UTF-8 encoding #1",
	    OFInvalidEncodingException,
	    [stringClass stringWithUTF8String: "\xE0\x80"])
	EXPECT_EXCEPTION(@"Detection of invalid UTF-8 encoding #2",
	    OFInvalidEncodingException,
	    [stringClass stringWithUTF8String: "\xF0\x80\x80\xC0"])

	TEST(@"Conversion of ISO 8859-1 to Unicode",
	    [[stringClass stringWithCString: "\xE4\xF6\xFC"
				   encoding: OF_STRING_ENCODING_ISO_8859_1]
	    isEqual: @"äöü"])

#ifdef HAVE_ISO_8859_15
	TEST(@"Conversion of ISO 8859-15 to Unicode",
	    [[stringClass stringWithCString: "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE"
				   encoding: OF_STRING_ENCODING_ISO_8859_15]
	    isEqual: @"€ŠšŽžŒœŸ"])
#endif

#ifdef HAVE_WINDOWS_1252
	TEST(@"Conversion of Windows 1252 to Unicode",
	    [[stringClass stringWithCString: "\x80\x82\x83\x84\x85\x86\x87\x88"
					     "\x89\x8A\x8B\x8C\x8E\x91\x92\x93"
					     "\x94\x95\x96\x97\x98\x99\x9A\x9B"
					     "\x9C\x9E\x9F"
				   encoding: OF_STRING_ENCODING_WINDOWS_1252]
	    isEqual: @"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ"])
#endif

#ifdef HAVE_CODEPAGE_437
	TEST(@"Conversion of Codepage 437 to Unicode",
	    [[stringClass stringWithCString: "\xB0\xB1\xB2\xDB"
				   encoding: OF_STRING_ENCODING_CODEPAGE_437]
	    isEqual: @"░▒▓█"])
#endif

	TEST(@"Conversion of Unicode to ASCII #1",
	    !strcmp([C(@"This is a test") cStringWithEncoding:
	    OF_STRING_ENCODING_ASCII], "This is a test"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to ASCII #2",
	    OFInvalidEncodingException,
	    [C(@"This is a tést")
	    cStringWithEncoding: OF_STRING_ENCODING_ASCII])

	TEST(@"Conversion of Unicode to ISO-8859-1 #1",
	    !strcmp([C(@"This is ä test") cStringWithEncoding:
	    OF_STRING_ENCODING_ISO_8859_1], "This is \xE4 test"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to ISO-8859-1 #2",
	    OFInvalidEncodingException,
	    [C(@"This is ä t€st") cStringWithEncoding:
	    OF_STRING_ENCODING_ISO_8859_1])

#ifdef HAVE_ISO_8859_15
	TEST(@"Conversion of Unicode to ISO-8859-15 #1",
	    !strcmp([C(@"This is ä t€st") cStringWithEncoding:
	    OF_STRING_ENCODING_ISO_8859_15], "This is \xE4 t\xA4st"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to ISO-8859-15 #2",
	    OFInvalidEncodingException,
	    [C(@"This is ä t€st…") cStringWithEncoding:
	    OF_STRING_ENCODING_ISO_8859_15])
#endif

#ifdef HAVE_WINDOWS_1252
	TEST(@"Conversion of Unicode to Windows-1252 #1",
	    !strcmp([C(@"This is ä t€st…") cStringWithEncoding:
	    OF_STRING_ENCODING_WINDOWS_1252], "This is \xE4 t\x80st\x85"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to Windows-1252 #2",
	    OFInvalidEncodingException, [C(@"This is ä t€st…‼")
	    cStringWithEncoding: OF_STRING_ENCODING_WINDOWS_1252])
#endif

#ifdef HAVE_CODEPAGE_437
	TEST(@"Conversion of Unicode to Codepage 437 #1",
	    !strcmp([C(@"Tést strîng ░▒▓") cStringWithEncoding:
	    OF_STRING_ENCODING_CODEPAGE_437], "T\x82st str\x8Cng \xB0\xB1\xB2"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to Codepage 437 #2",
	    OFInvalidEncodingException, [C(@"T€st strîng ░▒▓")
	    cStringWithEncoding: OF_STRING_ENCODING_CODEPAGE_437])
#endif

	TEST(@"Lossy conversion of Unicode to ASCII",
	    !strcmp([C(@"This is a tést") lossyCStringWithEncoding:
	    OF_STRING_ENCODING_ASCII], "This is a t?st"))

	TEST(@"Lossy conversion of Unicode to ISO-8859-1",
	    !strcmp([C(@"This is ä t€st") lossyCStringWithEncoding:
	    OF_STRING_ENCODING_ISO_8859_1], "This is \xE4 t?st"))

#ifdef HAVE_ISO_8859_15
	TEST(@"Lossy conversion of Unicode to ISO-8859-15",
	    !strcmp([C(@"This is ä t€st…") lossyCStringWithEncoding:
	    OF_STRING_ENCODING_ISO_8859_15], "This is \xE4 t\xA4st?"))
#endif

#ifdef HAVE_WINDOWS_1252
	TEST(@"Lossy conversion of Unicode to Windows-1252",
	    !strcmp([C(@"This is ä t€st…‼") lossyCStringWithEncoding:
	    OF_STRING_ENCODING_WINDOWS_1252], "This is \xE4 t\x80st\x85?"))
#endif

#ifdef HAVE_CODEPAGE_437
	TEST(@"Lossy conversion of Unicode to Codepage 437",
	    !strcmp([C(@"T€st strîng ░▒▓") lossyCStringWithEncoding:
	    OF_STRING_ENCODING_CODEPAGE_437], "T?st str\x8Cng \xB0\xB1\xB2"))
#endif

	TEST(@"+[stringWithFormat:]",
	    [(s[0] = [mutableStringClass stringWithFormat: @"%@:%d", @"test",

							   123])
	    isEqual: @"test:123"])

	TEST(@"-[appendFormat:]",
	    R(([s[0] appendFormat: @"%02X", 15])) &&
	    [s[0] isEqual: @"test:1230F"])

	TEST(@"-[rangeOfString:]",
	    [C(@"𝄞öö") rangeOfString: @"öö"].location == 1 &&
	    [C(@"𝄞öö") rangeOfString: @"ö"].location == 1 &&
	    [C(@"𝄞öö") rangeOfString: @"𝄞"].location == 0 &&
	    [C(@"𝄞öö") rangeOfString: @"x"].location == OF_NOT_FOUND &&
	    [C(@"𝄞öö") rangeOfString: @"öö"
	    options: OF_STRING_SEARCH_BACKWARDS].location == 1 &&
	    [C(@"𝄞öö") rangeOfString: @"ö"
	    options: OF_STRING_SEARCH_BACKWARDS].location == 2 &&
	    [C(@"𝄞öö") rangeOfString: @"𝄞"
	    options: OF_STRING_SEARCH_BACKWARDS].location == 0 &&
	    [C(@"𝄞öö") rangeOfString: @"x"
	    options: OF_STRING_SEARCH_BACKWARDS].location == OF_NOT_FOUND)

	EXPECT_EXCEPTION(
	    @"Detect out of range in -[rangeOfString:options:range:]",
	    OFOutOfRangeException,
	    [C(@"𝄞öö") rangeOfString: @"ö"
			     options: 0
			       range: of_range(3, 1)])


	cs = [OFCharacterSet characterSetWithCharactersInString: @"cđ"];
	TEST(@"-[indexOfCharacterFromSet:]",
	     [C(@"abcđabcđe") indexOfCharacterFromSet: cs] == 2 &&
	     [C(@"abcđabcđë")
	     indexOfCharacterFromSet: cs
			     options: OF_STRING_SEARCH_BACKWARDS] == 7 &&
	     [C(@"abcđabcđë")
	     indexOfCharacterFromSet: cs
			     options: 0
			       range: of_range(4, 4)] == 6 &&
	     [C(@"abcđabcđëf")
	     indexOfCharacterFromSet: cs
			     options: 0
			       range: of_range(8, 2)] == OF_NOT_FOUND)

	EXPECT_EXCEPTION(
	    @"Detect out of range in -[indexOfCharacterFromSet:options:range:]",
	    OFOutOfRangeException,
	    [C(@"𝄞öö") indexOfCharacterFromSet: cs
				       options: 0
					 range: of_range(3, 1)])

	TEST(@"-[substringWithRange:]",
	    [[C(@"𝄞öö") substringWithRange: of_range(1, 1)] isEqual: @"ö"] &&
	    [[C(@"𝄞öö") substringWithRange: of_range(3, 0)] isEqual: @""])

	EXPECT_EXCEPTION(@"Detect out of range in -[substringWithRange:] #1",
	    OFOutOfRangeException,
	    [C(@"𝄞öö") substringWithRange: of_range(2, 2)])
	EXPECT_EXCEPTION(@"Detect out of range in -[substringWithRange:] #2",
	    OFOutOfRangeException,
	    [C(@"𝄞öö") substringWithRange: of_range(4, 0)])

	TEST(@"-[stringByAppendingString:]",
	    [[C(@"foo") stringByAppendingString: @"bar"] isEqual: @"foobar"])

	TEST(@"-[stringByPrependingString:]",
	    [[C(@"foo") stringByPrependingString: @"bar"] isEqual: @"barfoo"])








|
|
|

|
|
|
|







|
|
|

|
|

|
>
|
|
|
|
|
|
|



|
|
|
|
|
|





|
|






|

|
>


|
>
>
|


|
|

|
|
|

|
>
|
|
|


|

|
>

|


|
|
|

|
|
|


|

|
|




|
|
|

|
|
|

|
>

|
>
|






|
|
|
|


|
|
|
|


|
|
|
|


|

|
|

|

|
|



|
|
>










|





|









|






|





|




|



|




|




|




|





|



|





|



|




|



|




|





|





|



|
>
|



|
|





|

|

|

|

|




|
<
<

>
|

|
|
|
|
|
<
|
|
|
|
|
|




|

|


|
|



|


|







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
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
@end

@implementation TestsAppDelegate (OFStringTests)
- (void)stringTestsWithClass: (Class)stringClass
		mutableClass: (Class)mutableStringClass
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableString *mutableString1, *mutableString2, *mutableString3;
	OFString *string;
	OFArray *array;
	size_t i;
	const OFUnichar *characters;
	const uint16_t *UTF16Characters;
	OFCharacterSet *characterSet;
	EntityHandler *entityHandler;
#ifdef OF_HAVE_BLOCKS
	__block int j;
	__block bool ok;
#endif

#define C(s) ((OFString *)[stringClass stringWithString: s])

	mutableString1 = [mutableStringClass stringWithString: @"täs€"];
	mutableString2 = [mutableStringClass string];
	mutableString3 = [[mutableString1 copy] autorelease];

	TEST(@"-[isEqual:]", [mutableString1 isEqual: mutableString3] &&
	    ![mutableString1 isEqual: [[[OFObject alloc] init] autorelease]])

	TEST(@"-[compare:]",
	    [mutableString1 compare: mutableString3] == OFOrderedSame &&
	    [mutableString1 compare: @""] != OFOrderedSame &&
	    [C(@"") compare: @"a"] == OFOrderedAscending &&
	    [C(@"a") compare: @"b"] == OFOrderedAscending &&
	    [C(@"cd") compare: @"bc"] == OFOrderedDescending &&
	    [C(@"ä") compare: @"ö"] == OFOrderedAscending &&
	    [C(@"€") compare: @"ß"] == OFOrderedDescending &&
	    [C(@"aa") compare: @"z"] == OFOrderedAscending)

#ifdef OF_HAVE_UNICODE_TABLES
	TEST(@"-[caseInsensitiveCompare:]",
	    [C(@"a") caseInsensitiveCompare: @"A"] == OFOrderedSame &&
	    [C(@"Ä") caseInsensitiveCompare: @"ä"] == OFOrderedSame &&
	    [C(@"я") caseInsensitiveCompare: @"Я"] == OFOrderedSame &&
	    [C(@"€") caseInsensitiveCompare: @"ß"] == OFOrderedDescending &&
	    [C(@"ß") caseInsensitiveCompare: @"→"] == OFOrderedAscending &&
	    [C(@"AA") caseInsensitiveCompare: @"z"] == OFOrderedAscending &&
	    [[stringClass stringWithUTF8String: "ABC"] caseInsensitiveCompare:
	    [stringClass stringWithUTF8String: "AbD"]] ==
	    [C(@"abc") compare: @"abd"])
#else
	TEST(@"-[caseInsensitiveCompare:]",
	    [C(@"a") caseInsensitiveCompare: @"A"] == OFOrderedSame &&
	    [C(@"AA") caseInsensitiveCompare: @"z"] == OFOrderedAscending &&
	    [[stringClass stringWithUTF8String: "ABC"] caseInsensitiveCompare:
	    [stringClass stringWithUTF8String: "AbD"]] ==
	    [C(@"abc") compare: @"abd"])
#endif

	TEST(@"-[hash] is the same if -[isEqual:] is true",
	    mutableString1.hash == mutableString3.hash)

	TEST(@"-[description]",
	    [mutableString1.description isEqual: mutableString1])

	TEST(@"-[appendString:] and -[appendUTF8String:]",
	    R([mutableString2 appendUTF8String: "1𝄞"]) &&
	    R([mutableString2 appendString: @"3"]) &&
	    R([mutableString1 appendString: mutableString2]) &&
	    [mutableString1 isEqual: @"täs€1𝄞3"])

	TEST(@"-[appendCharacters:length:]",
	    R([mutableString2 appendCharacters: unicharString + 6 length: 2]) &&
	    [mutableString2 isEqual: @"1𝄞3r🀺"])

	TEST(@"-[length]", mutableString1.length == 7)
	TEST(@"-[UTF8StringLength]", mutableString1.UTF8StringLength == 13)
	TEST(@"-[hash]", mutableString1.hash == 0x705583C0)

	TEST(@"-[characterAtIndex:]",
	    [mutableString1 characterAtIndex: 0] == 't' &&
	    [mutableString1 characterAtIndex: 1] == 0xE4 &&
	    [mutableString1 characterAtIndex: 3] == 0x20AC &&
	    [mutableString1 characterAtIndex: 5] == 0x1D11E)

	EXPECT_EXCEPTION(@"Detect out of range in -[characterAtIndex:]",
	    OFOutOfRangeException, [mutableString1 characterAtIndex: 7])

	TEST(@"-[reverse]",
	    R([mutableString1 reverse]) && [mutableString1 isEqual: @"3𝄞1€sät"])

	mutableString2 = [mutableStringClass stringWithString: @"abc"];

#ifdef OF_HAVE_UNICODE_TABLES
	TEST(@"-[uppercase]", R([mutableString1 uppercase]) &&
	    [mutableString1 isEqual: @"3𝄞1€SÄT"] &&
	    R([mutableString2 uppercase]) && [mutableString2 isEqual: @"ABC"])

	TEST(@"-[lowercase]", R([mutableString1 lowercase]) &&
	    [mutableString1 isEqual: @"3𝄞1€sät"] &&
	    R([mutableString2 lowercase]) && [mutableString2 isEqual: @"abc"])

	TEST(@"-[uppercaseString]",
	    [[mutableString1 uppercaseString] isEqual: @"3𝄞1€SÄT"])

	TEST(@"-[lowercaseString]", R([mutableString1 uppercase]) &&
	    [[mutableString1 lowercaseString] isEqual: @"3𝄞1€sät"])

	TEST(@"-[capitalizedString]", [C(@"džbla tdžst TDŽST").capitalizedString
	    isEqual: @"Džbla Tdžst Tdžst"])
#else
	TEST(@"-[uppercase]", R([mutableString1 uppercase]) &&
	    [mutableString1 isEqual: @"3𝄞1€SäT"] &&
	    R([mutableString2 uppercase]) && [mutableString2 isEqual: @"ABC"])

	TEST(@"-[lowercase]", R([mutableString1 lowercase]) &&
	    [mutableString1 isEqual: @"3𝄞1€sät"] &&
	    R([mutableString2 lowercase]) && [mutableString2 isEqual: @"abc"])

	TEST(@"-[uppercaseString]",
	    [mutableString1.uppercaseString isEqual: @"3𝄞1€SäT"])

	TEST(@"-[lowercaseString]",
	    R([mutableString1 uppercase]) &&
	    [mutableString1.lowercaseString isEqual: @"3𝄞1€sät"])

	TEST(@"-[capitalizedString]", [C(@"džbla tdžst TDŽST").capitalizedString
	    isEqual: @"džbla Tdžst TDŽst"])
#endif

	TEST(@"+[stringWithUTF8String:length:]",
	    (mutableString1 = [mutableStringClass
	    stringWithUTF8String: "\xEF\xBB\xBF" "foobar"
			  length: 6]) &&
	    [mutableString1 isEqual: @"foo"])

	TEST(@"+[stringWithUTF16String:]",
	    (string = [stringClass stringWithUTF16String: char16String]) &&
	    [string isEqual: @"fööbär🀺"] &&
	    (string = [stringClass stringWithUTF16String:
	    swappedChar16String]) && [string isEqual: @"fööbär🀺"])

	TEST(@"+[stringWithUTF32String:]",
	    (string = [stringClass stringWithUTF32String: unicharString]) &&
	    [string isEqual: @"fööbär🀺"] &&
	    (string = [stringClass stringWithUTF32String:
	    swappedUnicharString]) && [string isEqual: @"fööbär🀺"])

#ifdef OF_HAVE_FILES
	TEST(@"+[stringWithContentsOfFile:encoding]", (string = [stringClass
	    stringWithContentsOfFile: @"testfile.txt"
			    encoding: OFStringEncodingISO8859_1]) &&
	    [string isEqual: @"testäöü"])

	TEST(@"+[stringWithContentsOfURL:encoding]", (string = [stringClass
	    stringWithContentsOfURL: [OFURL fileURLWithPath: @"testfile.txt"]
			   encoding: OFStringEncodingISO8859_1]) &&
	    [string isEqual: @"testäöü"])
#endif

	TEST(@"-[appendUTFString:length:]",
	    R([mutableString1 appendUTF8String: "\xEF\xBB\xBF" "barqux"
					length: 6]) &&
	    [mutableString1 isEqual: @"foobar"])

	EXPECT_EXCEPTION(@"Detection of invalid UTF-8 encoding #1",
	    OFInvalidEncodingException,
	    [stringClass stringWithUTF8String: "\xE0\x80"])
	EXPECT_EXCEPTION(@"Detection of invalid UTF-8 encoding #2",
	    OFInvalidEncodingException,
	    [stringClass stringWithUTF8String: "\xF0\x80\x80\xC0"])

	TEST(@"Conversion of ISO 8859-1 to Unicode",
	    [[stringClass stringWithCString: "\xE4\xF6\xFC"
				   encoding: OFStringEncodingISO8859_1]
	    isEqual: @"äöü"])

#ifdef HAVE_ISO_8859_15
	TEST(@"Conversion of ISO 8859-15 to Unicode",
	    [[stringClass stringWithCString: "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE"
				   encoding: OFStringEncodingISO8859_15]
	    isEqual: @"€ŠšŽžŒœŸ"])
#endif

#ifdef HAVE_WINDOWS_1252
	TEST(@"Conversion of Windows 1252 to Unicode",
	    [[stringClass stringWithCString: "\x80\x82\x83\x84\x85\x86\x87\x88"
					     "\x89\x8A\x8B\x8C\x8E\x91\x92\x93"
					     "\x94\x95\x96\x97\x98\x99\x9A\x9B"
					     "\x9C\x9E\x9F"
				   encoding: OFStringEncodingWindows1252]
	    isEqual: @"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ"])
#endif

#ifdef HAVE_CODEPAGE_437
	TEST(@"Conversion of Codepage 437 to Unicode",
	    [[stringClass stringWithCString: "\xB0\xB1\xB2\xDB"
				   encoding: OFStringEncodingCodepage437]
	    isEqual: @"░▒▓█"])
#endif

	TEST(@"Conversion of Unicode to ASCII #1",
	    !strcmp([C(@"This is a test") cStringWithEncoding:
	    OFStringEncodingASCII], "This is a test"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to ASCII #2",
	    OFInvalidEncodingException,
	    [C(@"This is a tést")
	    cStringWithEncoding: OFStringEncodingASCII])

	TEST(@"Conversion of Unicode to ISO-8859-1 #1",
	    !strcmp([C(@"This is ä test") cStringWithEncoding:
	    OFStringEncodingISO8859_1], "This is \xE4 test"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to ISO-8859-1 #2",
	    OFInvalidEncodingException,
	    [C(@"This is ä t€st") cStringWithEncoding:
	    OFStringEncodingISO8859_1])

#ifdef HAVE_ISO_8859_15
	TEST(@"Conversion of Unicode to ISO-8859-15 #1",
	    !strcmp([C(@"This is ä t€st") cStringWithEncoding:
	    OFStringEncodingISO8859_15], "This is \xE4 t\xA4st"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to ISO-8859-15 #2",
	    OFInvalidEncodingException,
	    [C(@"This is ä t€st…") cStringWithEncoding:
	    OFStringEncodingISO8859_15])
#endif

#ifdef HAVE_WINDOWS_1252
	TEST(@"Conversion of Unicode to Windows-1252 #1",
	    !strcmp([C(@"This is ä t€st…") cStringWithEncoding:
	    OFStringEncodingWindows1252], "This is \xE4 t\x80st\x85"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to Windows-1252 #2",
	    OFInvalidEncodingException, [C(@"This is ä t€st…‼")
	    cStringWithEncoding: OFStringEncodingWindows1252])
#endif

#ifdef HAVE_CODEPAGE_437
	TEST(@"Conversion of Unicode to Codepage 437 #1",
	    !strcmp([C(@"Tést strîng ░▒▓") cStringWithEncoding:
	    OFStringEncodingCodepage437], "T\x82st str\x8Cng \xB0\xB1\xB2"))

	EXPECT_EXCEPTION(@"Conversion of Unicode to Codepage 437 #2",
	    OFInvalidEncodingException, [C(@"T€st strîng ░▒▓")
	    cStringWithEncoding: OFStringEncodingCodepage437])
#endif

	TEST(@"Lossy conversion of Unicode to ASCII",
	    !strcmp([C(@"This is a tést") lossyCStringWithEncoding:
	    OFStringEncodingASCII], "This is a t?st"))

	TEST(@"Lossy conversion of Unicode to ISO-8859-1",
	    !strcmp([C(@"This is ä t€st") lossyCStringWithEncoding:
	    OFStringEncodingISO8859_1], "This is \xE4 t?st"))

#ifdef HAVE_ISO_8859_15
	TEST(@"Lossy conversion of Unicode to ISO-8859-15",
	    !strcmp([C(@"This is ä t€st…") lossyCStringWithEncoding:
	    OFStringEncodingISO8859_15], "This is \xE4 t\xA4st?"))
#endif

#ifdef HAVE_WINDOWS_1252
	TEST(@"Lossy conversion of Unicode to Windows-1252",
	    !strcmp([C(@"This is ä t€st…‼") lossyCStringWithEncoding:
	    OFStringEncodingWindows1252], "This is \xE4 t\x80st\x85?"))
#endif

#ifdef HAVE_CODEPAGE_437
	TEST(@"Lossy conversion of Unicode to Codepage 437",
	    !strcmp([C(@"T€st strîng ░▒▓") lossyCStringWithEncoding:
	    OFStringEncodingCodepage437], "T?st str\x8Cng \xB0\xB1\xB2"))
#endif

	TEST(@"+[stringWithFormat:]",
	    [(mutableString1 = [mutableStringClass stringWithFormat: @"%@:%d",
								     @"test",
								     123])
	    isEqual: @"test:123"])

	TEST(@"-[appendFormat:]",
	    R(([mutableString1 appendFormat: @"%02X", 15])) &&
	    [mutableString1 isEqual: @"test:1230F"])

	TEST(@"-[rangeOfString:]",
	    [C(@"𝄞öö") rangeOfString: @"öö"].location == 1 &&
	    [C(@"𝄞öö") rangeOfString: @"ö"].location == 1 &&
	    [C(@"𝄞öö") rangeOfString: @"𝄞"].location == 0 &&
	    [C(@"𝄞öö") rangeOfString: @"x"].location == OFNotFound &&
	    [C(@"𝄞öö") rangeOfString: @"öö"
	    options: OFStringSearchBackwards].location == 1 &&
	    [C(@"𝄞öö") rangeOfString: @"ö"
	    options: OFStringSearchBackwards].location == 2 &&
	    [C(@"𝄞öö") rangeOfString: @"𝄞"
	    options: OFStringSearchBackwards].location == 0 &&
	    [C(@"𝄞öö") rangeOfString: @"x"
	    options: OFStringSearchBackwards].location == OFNotFound)

	EXPECT_EXCEPTION(
	    @"Detect out of range in -[rangeOfString:options:range:]",
	    OFOutOfRangeException,
	    [C(@"𝄞öö") rangeOfString: @"ö" options: 0 range: OFRangeMake(3, 1)])



	characterSet =
	    [OFCharacterSet characterSetWithCharactersInString: @"cđ"];
	TEST(@"-[indexOfCharacterFromSet:]",
	    [C(@"abcđabcđe") indexOfCharacterFromSet: characterSet] == 2 &&
	    [C(@"abcđabcđë")
	    indexOfCharacterFromSet: characterSet
			    options: OFStringSearchBackwards] == 7 &&
	    [C(@"abcđabcđë") indexOfCharacterFromSet: characterSet

					     options: 0
					       range: OFRangeMake(4, 4)] == 6 &&
	    [C(@"abcđabcđëf")
	    indexOfCharacterFromSet: characterSet
			    options: 0
			      range: OFRangeMake(8, 2)] == OFNotFound)

	EXPECT_EXCEPTION(
	    @"Detect out of range in -[indexOfCharacterFromSet:options:range:]",
	    OFOutOfRangeException,
	    [C(@"𝄞öö") indexOfCharacterFromSet: characterSet
				       options: 0
					 range: OFRangeMake(3, 1)])

	TEST(@"-[substringWithRange:]",
	    [[C(@"𝄞öö") substringWithRange: OFRangeMake(1, 1)] isEqual: @"ö"] &&
	    [[C(@"𝄞öö") substringWithRange: OFRangeMake(3, 0)] isEqual: @""])

	EXPECT_EXCEPTION(@"Detect out of range in -[substringWithRange:] #1",
	    OFOutOfRangeException,
	    [C(@"𝄞öö") substringWithRange: OFRangeMake(2, 2)])
	EXPECT_EXCEPTION(@"Detect out of range in -[substringWithRange:] #2",
	    OFOutOfRangeException,
	    [C(@"𝄞öö") substringWithRange: OFRangeMake(4, 0)])

	TEST(@"-[stringByAppendingString:]",
	    [[C(@"foo") stringByAppendingString: @"bar"] isEqual: @"foobar"])

	TEST(@"-[stringByPrependingString:]",
	    [[C(@"foo") stringByPrependingString: @"bar"] isEqual: @"barfoo"])

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
	    !C(@"foo/bar").absolutePath && !C(@"foo").absolutePath)
# else
	TEST(@"-[isAbsolutePath]",
	    C(@"/foo").absolutePath && C(@"/foo/bar").absolutePath &&
	    !C(@"foo/bar").absolutePath && !C(@"foo").absolutePath)
# endif

	s[0] = [mutableStringClass stringWithString: @"foo"];
# if defined(OF_WINDOWS) || defined(OF_MSDOS)
	[s[0] appendString: @"\\"];
# else
	[s[0] appendString: @"/"];
# endif
	[s[0] appendString: @"bar"];
	s[1] = [mutableStringClass stringWithString: s[0]];
# if defined(OF_WINDOWS) || defined(OF_MSDOS)
	[s[1] appendString: @"\\"];
# else
	[s[1] appendString: @"/"];
# endif
	is = [stringClass stringWithString: s[1]];
	[s[1] appendString: @"baz"];
	TEST(@"-[stringByAppendingPathComponent:]",
	    [[s[0] stringByAppendingPathComponent: @"baz"] isEqual: s[1]] &&

	    [[is stringByAppendingPathComponent: @"baz"] isEqual: s[1]])

#endif

	TEST(@"-[hasPrefix:]", [C(@"foobar") hasPrefix: @"foo"] &&
	    ![C(@"foobar") hasPrefix: @"foobar0"])

	TEST(@"-[hasSuffix:]", [C(@"foobar") hasSuffix: @"bar"] &&
	    ![C(@"foobar") hasSuffix: @"foobar0"])

	i = 0;
	TEST(@"-[componentsSeparatedByString:]",
	    (a = [C(@"fooXXbarXXXXbazXXXX")
	    componentsSeparatedByString: @"XX"]) &&
	    [[a objectAtIndex: i++] isEqual: @"foo"] &&
	    [[a objectAtIndex: i++] isEqual: @"bar"] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @"baz"] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    a.count == i)




	i = 0;
	TEST(@"-[componentsSeparatedByString:options:]",
	    (a = [C(@"fooXXbarXXXXbazXXXX")
	    componentsSeparatedByString: @"XX"
				options: OF_STRING_SKIP_EMPTY]) &&
	    [[a objectAtIndex: i++] isEqual: @"foo"] &&
	    [[a objectAtIndex: i++] isEqual: @"bar"] &&
	    [[a objectAtIndex: i++] isEqual: @"baz"] &&
	    a.count == i)


	cs = [OFCharacterSet characterSetWithCharactersInString: @"XYZ"];

	i = 0;
	TEST(@"-[componentsSeparatedByCharactersInSet:]",
	    (a = [C(@"fooXYbarXYZXbazXYXZx")
	    componentsSeparatedByCharactersInSet: cs]) &&
	    [[a objectAtIndex: i++] isEqual: @"foo"] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @"bar"] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @"baz"] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @""] &&
	    [[a objectAtIndex: i++] isEqual: @"x"] &&
	    a.count == i)

	i = 0;
	TEST(@"-[componentsSeparatedByCharactersInSet:options:]",
	    (a = [C(@"fooXYbarXYZXbazXYXZ")
	    componentsSeparatedByCharactersInSet: cs
					 options: OF_STRING_SKIP_EMPTY]) &&
	    [[a objectAtIndex: i++] isEqual: @"foo"] &&
	    [[a objectAtIndex: i++] isEqual: @"bar"] &&
	    [[a objectAtIndex: i++] isEqual: @"baz"] &&
	    a.count == i)

#ifdef OF_HAVE_FILES
# if defined(OF_WINDOWS)
	TEST(@"+[pathWithComponents:]",
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", @"bar", @"baz", nil]] isEqual: @"foo\\bar\\baz"] &&
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:







|

|

|

|
|

|

|

|
|

|
>
|
>










|

|
|
|
|
|
|
|
>
>
>



|

|
|
|
|
|

>
|



|
|
|
|
|
|
|
|
|
|
|
|
|
|



|
|
|
|
|
|
|







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
	    !C(@"foo/bar").absolutePath && !C(@"foo").absolutePath)
# else
	TEST(@"-[isAbsolutePath]",
	    C(@"/foo").absolutePath && C(@"/foo/bar").absolutePath &&
	    !C(@"foo/bar").absolutePath && !C(@"foo").absolutePath)
# endif

	mutableString1 = [mutableStringClass stringWithString: @"foo"];
# if defined(OF_WINDOWS) || defined(OF_MSDOS)
	[mutableString1 appendString: @"\\"];
# else
	[mutableString1 appendString: @"/"];
# endif
	[mutableString1 appendString: @"bar"];
	mutableString2 = [mutableStringClass stringWithString: mutableString1];
# if defined(OF_WINDOWS) || defined(OF_MSDOS)
	[mutableString2 appendString: @"\\"];
# else
	[mutableString2 appendString: @"/"];
# endif
	string = [stringClass stringWithString: mutableString2];
	[mutableString2 appendString: @"baz"];
	TEST(@"-[stringByAppendingPathComponent:]",
	    [[mutableString1 stringByAppendingPathComponent: @"baz"]
	    isEqual: mutableString2] &&
	    [[string stringByAppendingPathComponent: @"baz"]
	    isEqual: mutableString2])
#endif

	TEST(@"-[hasPrefix:]", [C(@"foobar") hasPrefix: @"foo"] &&
	    ![C(@"foobar") hasPrefix: @"foobar0"])

	TEST(@"-[hasSuffix:]", [C(@"foobar") hasSuffix: @"bar"] &&
	    ![C(@"foobar") hasSuffix: @"foobar0"])

	i = 0;
	TEST(@"-[componentsSeparatedByString:]",
	    (array = [C(@"fooXXbarXXXXbazXXXX")
	    componentsSeparatedByString: @"XX"]) &&
	    [[array objectAtIndex: i++] isEqual: @"foo"] &&
	    [[array objectAtIndex: i++] isEqual: @"bar"] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @"baz"] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    array.count == i &&
	    (array = [C(@"foo") componentsSeparatedByString: @""]) &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    array.count == 1)

	i = 0;
	TEST(@"-[componentsSeparatedByString:options:]",
	    (array = [C(@"fooXXbarXXXXbazXXXX")
	    componentsSeparatedByString: @"XX"
				options: OFStringSkipEmptyComponents]) &&
	    [[array objectAtIndex: i++] isEqual: @"foo"] &&
	    [[array objectAtIndex: i++] isEqual: @"bar"] &&
	    [[array objectAtIndex: i++] isEqual: @"baz"] &&
	    array.count == i)

	characterSet =
	    [OFCharacterSet characterSetWithCharactersInString: @"XYZ"];

	i = 0;
	TEST(@"-[componentsSeparatedByCharactersInSet:]",
	    (array = [C(@"fooXYbarXYZXbazXYXZx")
	    componentsSeparatedByCharactersInSet: characterSet]) &&
	    [[array objectAtIndex: i++] isEqual: @"foo"] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @"bar"] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @"baz"] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @""] &&
	    [[array objectAtIndex: i++] isEqual: @"x"] &&
	    array.count == i)

	i = 0;
	TEST(@"-[componentsSeparatedByCharactersInSet:options:]",
	    (array = [C(@"fooXYbarXYZXbazXYXZ")
	    componentsSeparatedByCharactersInSet: characterSet
	    options: OFStringSkipEmptyComponents]) &&
	    [[array objectAtIndex: i++] isEqual: @"foo"] &&
	    [[array objectAtIndex: i++] isEqual: @"bar"] &&
	    [[array objectAtIndex: i++] isEqual: @"baz"] &&
	    array.count == i)

#ifdef OF_HAVE_FILES
# if defined(OF_WINDOWS)
	TEST(@"+[pathWithComponents:]",
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", @"bar", @"baz", nil]] isEqual: @"foo\\bar\\baz"] &&
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
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
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", nil]] isEqual: @"foo"])
# endif

# if defined(OF_WINDOWS)
	TEST(@"-[pathComponents]",
	    /* c:/tmp */
	    (a = C(@"c:/tmp").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"c:/"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\tmp\ */
	    (a = C(@"c:\\tmp\\").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"c:\\"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\ */
	    (a = C(@"c:\\").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"c:\\"] &&
	    /* c:/ */
	    (a = C(@"c:/").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"c:/"] &&
	    /* c: */
	    (a = C(@"c:").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"c:"] &&
	    /* foo\bar */
	    (a = C(@"foo\\bar").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo\bar/baz/ */
	    (a = C(@"foo\\bar/baz/").pathComponents) && a.count == 3 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    [[a objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo\/ */
	    (a = C(@"foo\\/").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    /* \\foo\bar */
	    (a = C(@"\\\\foo\\bar").pathComponents) && a.count == 3 &&
	    [[a objectAtIndex: 0] isEqual: @"\\\\"] &&
	    [[a objectAtIndex: 1] isEqual: @"foo"] &&
	    [[a objectAtIndex: 2] isEqual: @"bar"] &&
	    C(@"").pathComponents.count == 0)
# elif defined(OF_MSDOS)
	TEST(@"-[pathComponents]",
	    /* c:/tmp */
	    (a = C(@"c:/tmp").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"c:/"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\tmp\ */
	    (a = C(@"c:\\tmp\\").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"c:\\"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\ */
	    (a = C(@"c:\\").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"c:\\"] &&
	    /* c:/ */
	    (a = C(@"c:/").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"c:/"] &&
	    /* c: */
	    (a = C(@"c:").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"c:"] &&
	    /* foo\bar */
	    (a = C(@"foo\\bar").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo\bar/baz/ */
	    (a = C(@"foo\\bar/baz/").pathComponents) && a.count == 3 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    [[a objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo\/ */
	    (a = C(@"foo\\/").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    C(@"").pathComponents.count == 0)
# elif defined(OF_AMIGAOS)
	TEST(@"-[pathComponents]",
	    /* dh0:tmp */
	    (a = C(@"dh0:tmp").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"dh0:"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* dh0:tmp/ */
	    (a = C(@"dh0:tmp/").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"dh0:"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* dh0: */
	    (a = C(@"dh0:/").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"dh0:"] &&
	    [[a objectAtIndex: 1] isEqual: @"/"] &&
	    /* foo/bar */
	    (a = C(@"foo/bar").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo/bar/baz/ */
	    (a = C(@"foo/bar/baz/").pathComponents) && a.count == 3 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    [[a objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo// */
	    (a = C(@"foo//").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"/"] &&
	    C(@"").pathComponents.count == 0)
# elif defined(OF_NINTENDO_3DS) || defined(OF_WII)
	TEST(@"-[pathComponents]",
	    /* sdmc:/tmp */
	    (a = C(@"sdmc:/tmp").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"sdmc:"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* sdmc:/ */
	    (a = C(@"sdmc:/").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"sdmc:"] &&
	    /* foo/bar */
	    (a = C(@"foo/bar").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo/bar/baz/ */
	    (a = C(@"foo/bar/baz/").pathComponents) && a.count == 3 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    [[a objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo// */
	    (a = C(@"foo//").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    C(@"").pathComponents.count == 0)
# else
	TEST(@"-[pathComponents]",
	    /* /tmp */
	    (a = C(@"/tmp").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"/"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* /tmp/ */
	    (a = C(@"/tmp/").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"/"] &&
	    [[a objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* / */
	    (a = C(@"/").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"/"] &&
	    /* foo/bar */
	    (a = C(@"foo/bar").pathComponents) && a.count == 2 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo/bar/baz/ */
	    (a = C(@"foo/bar/baz/").pathComponents) && a.count == 3 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    [[a objectAtIndex: 1] isEqual: @"bar"] &&
	    [[a objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo// */
	    (a = C(@"foo//").pathComponents) && a.count == 1 &&
	    [[a objectAtIndex: 0] isEqual: @"foo"] &&
	    C(@"").pathComponents.count == 0)
# endif

# if defined(OF_WINDOWS)
	TEST(@"-[lastPathComponent]",
	    [C(@"c:/tmp").lastPathComponent isEqual: @"tmp"] &&
	    [C(@"c:\\tmp\\").lastPathComponent isEqual: @"tmp"] &&







|
|
|

|
|
|

|
|

|
|

|
|

|
|
|

|
|
|
|

|
|

|
|
|
|




|
|
|

|
|
|

|
|

|
|

|
|

|
|
|

|
|
|
|

|
|




|
|
|

|
|
|

|
|
|

|
|
|

|
|
|
|

|
|
|




|
|
|

|
|

|
|
|

|
|
|
|

|
|




|
|
|

|
|
|

|
|

|
|
|

|
|
|
|

|
|







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
	    [[stringClass pathWithComponents: [OFArray arrayWithObjects:
	    @"foo", nil]] isEqual: @"foo"])
# endif

# if defined(OF_WINDOWS)
	TEST(@"-[pathComponents]",
	    /* c:/tmp */
	    (array = C(@"c:/tmp").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"c:/"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\tmp\ */
	    (array = C(@"c:\\tmp\\").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"c:\\"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\ */
	    (array = C(@"c:\\").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"c:\\"] &&
	    /* c:/ */
	    (array = C(@"c:/").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"c:/"] &&
	    /* c: */
	    (array = C(@"c:").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"c:"] &&
	    /* foo\bar */
	    (array = C(@"foo\\bar").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo\bar/baz/ */
	    (array = C(@"foo\\bar/baz/").pathComponents) && array.count == 3 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    [[array objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo\/ */
	    (array = C(@"foo\\/").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    /* \\foo\bar */
	    (array = C(@"\\\\foo\\bar").pathComponents) && array.count == 3 &&
	    [[array objectAtIndex: 0] isEqual: @"\\\\"] &&
	    [[array objectAtIndex: 1] isEqual: @"foo"] &&
	    [[array objectAtIndex: 2] isEqual: @"bar"] &&
	    C(@"").pathComponents.count == 0)
# elif defined(OF_MSDOS)
	TEST(@"-[pathComponents]",
	    /* c:/tmp */
	    (array = C(@"c:/tmp").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"c:/"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\tmp\ */
	    (array = C(@"c:\\tmp\\").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"c:\\"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* c:\ */
	    (array = C(@"c:\\").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"c:\\"] &&
	    /* c:/ */
	    (array = C(@"c:/").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"c:/"] &&
	    /* c: */
	    (array = C(@"c:").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"c:"] &&
	    /* foo\bar */
	    (array = C(@"foo\\bar").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo\bar/baz/ */
	    (array = C(@"foo\\bar/baz/").pathComponents) && array.count == 3 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    [[array objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo\/ */
	    (array = C(@"foo\\/").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    C(@"").pathComponents.count == 0)
# elif defined(OF_AMIGAOS)
	TEST(@"-[pathComponents]",
	    /* dh0:tmp */
	    (array = C(@"dh0:tmp").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"dh0:"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* dh0:tmp/ */
	    (array = C(@"dh0:tmp/").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"dh0:"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* dh0: */
	    (array = C(@"dh0:/").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"dh0:"] &&
	    [[array objectAtIndex: 1] isEqual: @"/"] &&
	    /* foo/bar */
	    (array = C(@"foo/bar").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo/bar/baz/ */
	    (array = C(@"foo/bar/baz/").pathComponents) && array.count == 3 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    [[array objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo// */
	    (array = C(@"foo//").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"/"] &&
	    C(@"").pathComponents.count == 0)
# elif defined(OF_NINTENDO_3DS) || defined(OF_WII)
	TEST(@"-[pathComponents]",
	    /* sdmc:/tmp */
	    (array = C(@"sdmc:/tmp").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"sdmc:"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* sdmc:/ */
	    (array = C(@"sdmc:/").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"sdmc:"] &&
	    /* foo/bar */
	    (array = C(@"foo/bar").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo/bar/baz/ */
	    (array = C(@"foo/bar/baz/").pathComponents) && array.count == 3 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    [[array objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo// */
	    (array = C(@"foo//").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    C(@"").pathComponents.count == 0)
# else
	TEST(@"-[pathComponents]",
	    /* /tmp */
	    (array = C(@"/tmp").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"/"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* /tmp/ */
	    (array = C(@"/tmp/").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"/"] &&
	    [[array objectAtIndex: 1] isEqual: @"tmp"] &&
	    /* / */
	    (array = C(@"/").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"/"] &&
	    /* foo/bar */
	    (array = C(@"foo/bar").pathComponents) && array.count == 2 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    /* foo/bar/baz/ */
	    (array = C(@"foo/bar/baz/").pathComponents) && array.count == 3 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    [[array objectAtIndex: 1] isEqual: @"bar"] &&
	    [[array objectAtIndex: 2] isEqual: @"baz"] &&
	    /* foo// */
	    (array = C(@"foo//").pathComponents) && array.count == 1 &&
	    [[array objectAtIndex: 0] isEqual: @"foo"] &&
	    C(@"").pathComponents.count == 0)
# endif

# if defined(OF_WINDOWS)
	TEST(@"-[lastPathComponent]",
	    [C(@"c:/tmp").lastPathComponent isEqual: @"tmp"] &&
	    [C(@"c:\\tmp\\").lastPathComponent isEqual: @"tmp"] &&
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
	TEST(@"-[longLongValue]",
	    C(@"1234").longLongValue == 1234 &&
	    C(@"\r\n+123  ").longLongValue == 123 &&
	    C(@"-500\t").longLongValue == -500 &&
	    [C(@"-0x10\t") longLongValueWithBase: 0] == -0x10 &&
	    C(@"\t\t\r\n").longLongValue == 0 &&
	    [C(@"123f") longLongValueWithBase: 16] == 0x123f &&

	    [C(@"\t\n0xABcd\r") longLongValueWithBase: 0] == 0xABCD &&
	    [C(@"1234567") longLongValueWithBase: 8] == 01234567 &&
	    [C(@"\r\n0123") longLongValueWithBase: 0] == 0123 &&
	    [C(@"765\t") longLongValueWithBase: 8] == 0765 &&
	    [C(@"\t\t\r\n") longLongValueWithBase: 8] == 0)

	TEST(@"-[unsignedLongLongValue]",
	    C(@"1234").unsignedLongLongValue == 1234 &&
	    C(@"\r\n+123  ").unsignedLongLongValue == 123 &&
	    C(@"\t\t\r\n").unsignedLongLongValue == 0 &&
	    [C(@"123f") unsignedLongLongValueWithBase: 16] == 0x123f &&

	    [C(@"\t\n0xABcd\r") unsignedLongLongValueWithBase: 0] == 0xABCD &&
	    [C(@"1234567") unsignedLongLongValueWithBase: 8] == 01234567 &&
	    [C(@"\r\n0123") unsignedLongLongValueWithBase: 0] == 0123 &&
	    [C(@"765\t") unsignedLongLongValueWithBase: 8] == 0765 &&
	    [C(@"\t\t\r\n") unsignedLongLongValueWithBase: 8] == 0)

	/*
	 * These test numbers can be generated without rounding if we have IEEE
	 * floating point numbers, thus we can use == on them.
	 */
	TEST(@"-[floatValue]",
	    C(@"\t-0.25 ").floatValue == -0.25 &&
	    C(@"\r\n\tINF\t\n").floatValue == INFINITY &&
	    C(@"\r -INFINITY\n").floatValue == -INFINITY &&
	    isnan(C(@"   NAN\t\t").floatValue))


#if !defined(OF_ANDROID) && !defined(OF_SOLARIS) && !defined(OF_DJGPP) && \
    !defined(OF_AMIGAOS_M68K)
# define INPUT @"\t-0x1.FFFFFFFFFFFFFP-1020 "
# define EXPECTED -0x1.FFFFFFFFFFFFFP-1020
#else
/* Android, Solaris, DJGPP and AmigaOS3 do not accept 0x for strtod() */
# if (!defined(OF_SOLARIS) || !defined(OF_X86)) && !defined(OF_AMIGAOS_M68K)
#  define INPUT @"\t-0.123456789 "
#  define EXPECTED -0.123456789
# else
/*
 * Solaris' strtod() has weird rounding on x86, but not on x86_64.
 * AmigaOS 3 with libnix has weird rounding as well.







>











>














|
>

|
|



|







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
	TEST(@"-[longLongValue]",
	    C(@"1234").longLongValue == 1234 &&
	    C(@"\r\n+123  ").longLongValue == 123 &&
	    C(@"-500\t").longLongValue == -500 &&
	    [C(@"-0x10\t") longLongValueWithBase: 0] == -0x10 &&
	    C(@"\t\t\r\n").longLongValue == 0 &&
	    [C(@"123f") longLongValueWithBase: 16] == 0x123f &&
	    [C(@"-1234") longLongValueWithBase: 0] == -1234 &&
	    [C(@"\t\n0xABcd\r") longLongValueWithBase: 0] == 0xABCD &&
	    [C(@"1234567") longLongValueWithBase: 8] == 01234567 &&
	    [C(@"\r\n0123") longLongValueWithBase: 0] == 0123 &&
	    [C(@"765\t") longLongValueWithBase: 8] == 0765 &&
	    [C(@"\t\t\r\n") longLongValueWithBase: 8] == 0)

	TEST(@"-[unsignedLongLongValue]",
	    C(@"1234").unsignedLongLongValue == 1234 &&
	    C(@"\r\n+123  ").unsignedLongLongValue == 123 &&
	    C(@"\t\t\r\n").unsignedLongLongValue == 0 &&
	    [C(@"123f") unsignedLongLongValueWithBase: 16] == 0x123f &&
	    [C(@"1234") unsignedLongLongValueWithBase: 0] == 1234 &&
	    [C(@"\t\n0xABcd\r") unsignedLongLongValueWithBase: 0] == 0xABCD &&
	    [C(@"1234567") unsignedLongLongValueWithBase: 8] == 01234567 &&
	    [C(@"\r\n0123") unsignedLongLongValueWithBase: 0] == 0123 &&
	    [C(@"765\t") unsignedLongLongValueWithBase: 8] == 0765 &&
	    [C(@"\t\t\r\n") unsignedLongLongValueWithBase: 8] == 0)

	/*
	 * These test numbers can be generated without rounding if we have IEEE
	 * floating point numbers, thus we can use == on them.
	 */
	TEST(@"-[floatValue]",
	    C(@"\t-0.25 ").floatValue == -0.25 &&
	    C(@"\r\n\tINF\t\n").floatValue == INFINITY &&
	    C(@"\r -INFINITY\n").floatValue == -INFINITY &&
	    isnan(C(@"   NAN\t\t").floatValue) &&
	    isnan(C(@"   -NaN\t\t").floatValue))

#if !defined(OF_ANDROID) && !defined(OF_SOLARIS) && !defined(OF_HPUX) && \
    !defined(OF_DJGPP) && !defined(OF_AMIGAOS_M68K)
# define INPUT @"\t-0x1.FFFFFFFFFFFFFP-1020 "
# define EXPECTED -0x1.FFFFFFFFFFFFFP-1020
#else
/* Android, Solaris, HP-UX, DJGPP and AmigaOS 3 do not accept 0x for strtod() */
# if (!defined(OF_SOLARIS) || !defined(OF_X86)) && !defined(OF_AMIGAOS_M68K)
#  define INPUT @"\t-0.123456789 "
#  define EXPECTED -0.123456789
# else
/*
 * Solaris' strtod() has weird rounding on x86, but not on x86_64.
 * AmigaOS 3 with libnix has weird rounding as well.
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190

1191
1192
1193
1194

1195
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
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

	EXPECT_EXCEPTION(@"Detect out of range in -[unsignedLongLongValue]",
	    OFOutOfRangeException,
	    [C(@"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
	       @"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")
	    unsignedLongLongValueWithBase: 16])

	TEST(@"-[characters]", (ua = C(@"fööbär🀺").characters) &&
	    !memcmp(ua, ucstr + 1, sizeof(ucstr) - 8))

#ifdef OF_BIG_ENDIAN
# define SWAPPED_BYTE_ORDER OF_BYTE_ORDER_LITTLE_ENDIAN
#else
# define SWAPPED_BYTE_ORDER OF_BYTE_ORDER_BIG_ENDIAN
#endif
	TEST(@"-[UTF16String]", (u16a = C(@"fööbär🀺").UTF16String) &&

	    !memcmp(u16a, utf16str + 1, of_string_utf16_length(utf16str) * 2) &&
	    (u16a = [C(@"fööbär🀺")
	    UTF16StringWithByteOrder: SWAPPED_BYTE_ORDER]) &&
	    !memcmp(u16a, sutf16str + 1, of_string_utf16_length(sutf16str) * 2))


	TEST(@"-[UTF16StringLength]", C(@"fööbär🀺").UTF16StringLength == 8)

	TEST(@"-[UTF32String]", (ua = C(@"fööbär🀺").UTF32String) &&

	    !memcmp(ua, ucstr + 1, of_string_utf32_length(ucstr) * 4) &&
	    (ua = [C(@"fööbär🀺") UTF32StringWithByteOrder:
	    SWAPPED_BYTE_ORDER]) &&
	    !memcmp(ua, sucstr + 1, of_string_utf32_length(sucstr) * 4))

#undef SWAPPED_BYTE_ORDER

	TEST(@"-[MD5Hash]", [C(@"asdfoobar").MD5Hash
	    isEqual: @"184dce2ec49b5422c7cfd8728864db4c"])


	TEST(@"-[RIPEMD160Hash]", [C(@"asdfoobar").RIPEMD160Hash
	    isEqual: @"021d773b0fac06eb6755ca6aa58a580c980f7f13"])

	TEST(@"-[SHA1Hash]", [C(@"asdfoobar").SHA1Hash
	    isEqual: @"f5f81ac0a8b5cbfdc4585ec1ad32e7b3a12b9b49"])

	TEST(@"-[SHA224Hash]", [C(@"asdfoobar").SHA224Hash
	    isEqual:
	    @"5a06822dcbd5a874f67d062b80b9d8a9cb9b5b303960b9da9290c192"])


	TEST(@"-[SHA256Hash]", [C(@"asdfoobar").SHA256Hash isEqual:
	    @"28e65b1dcd7f6ce2ea6277b15f87b913"
	    @"628b5500bf7913a2bbf4cedcfa1215f6"])

	TEST(@"-[SHA384Hash]", [C(@"asdfoobar").SHA384Hash isEqual:
	    @"73286da882ffddca2f45e005cfa6b44f3fc65bfb26db1d08"
	    @"7ded2f9c279e5addf8be854044bca0cece073fce28eec7d9"])

	TEST(@"-[SHA512Hash]", [C(@"asdfoobar").SHA512Hash isEqual:
	    @"0464c427da158b02161bb44a3090bbfc594611ef6a53603640454b56412a9247c"
	    @"3579a329e53a5dc74676b106755e3394f9454a2d42273242615d32f80437d61"])



	cs = [OFCharacterSet characterSetWithCharactersInString: @"abfo'_~$🍏"];
	TEST(@"-[stringByURLEncodingWithAllowedCharacters:]",
	    [[C(@"foo\"ba'_~$]🍏🍌") stringByURLEncodingWithAllowedCharacters:
	    cs] isEqual: @"foo%22ba'_~$%5D🍏%F0%9F%8D%8C"])

	TEST(@"-[stringByURLDecoding]",
	    [C(@"foo%20bar%22+%24%F0%9F%8D%8C").stringByURLDecoding
	    isEqual: @"foo bar\"+$🍌"])

	TEST(@"-[insertString:atIndex:]",

	    (s[0] = [mutableStringClass stringWithString: @"𝄞öööbä€"]) &&
	    R([s[0] insertString: @"äöü"
			 atIndex: 3]) &&
	    [s[0] isEqual: @"𝄞ööäöüöbä€"])

	EXPECT_EXCEPTION(@"Detect invalid format in -[stringByURLDecoding] "
	    @"#1", OFInvalidFormatException,
	    [C(@"foo%xbar") stringByURLDecoding])
	EXPECT_EXCEPTION(@"Detect invalid encoding in -[stringByURLDecoding] "
	    @"#2", OFInvalidEncodingException,
	    [C(@"foo%FFbar") stringByURLDecoding])

	TEST(@"-[setCharacter:atIndex:]",

	    (s[0] = [mutableStringClass stringWithString: @"abäde"]) &&
	    R([s[0] setCharacter: 0xF6
			 atIndex: 2]) &&
	    [s[0] isEqual: @"aböde"] &&
	    R([s[0] setCharacter: 'c'
			 atIndex: 2]) &&
	    [s[0] isEqual: @"abcde"] &&
	    R([s[0] setCharacter: 0x20AC
			 atIndex: 3]) &&
	    [s[0] isEqual: @"abc€e"] &&
	    R([s[0] setCharacter: 'x'
			 atIndex: 1]) &&
	    [s[0] isEqual: @"axc€e"])

	TEST(@"-[deleteCharactersInRange:]",

	    (s[0] = [mutableStringClass stringWithString: @"𝄞öööbä€"]) &&
	    R([s[0] deleteCharactersInRange: of_range(1, 3)]) &&
	    [s[0] isEqual: @"𝄞bä€"] &&
	    R([s[0] deleteCharactersInRange: of_range(0, 4)]) &&
	    [s[0] isEqual: @""])

	TEST(@"-[replaceCharactersInRange:withString:]",

	    (s[0] = [mutableStringClass stringWithString: @"𝄞öööbä€"]) &&
	    R([s[0] replaceCharactersInRange: of_range(1, 3)
				  withString: @"äöüß"]) &&
	    [s[0] isEqual: @"𝄞äöüßbä€"] &&
	    R([s[0] replaceCharactersInRange: of_range(4, 2)
				  withString: @"b"]) &&
	    [s[0] isEqual: @"𝄞äöübä€"] &&
	    R([s[0] replaceCharactersInRange: of_range(0, 7)
				  withString: @""]) &&
	    [s[0] isEqual: @""])

	EXPECT_EXCEPTION(@"Detect OoR in -[deleteCharactersInRange:] #1",
	    OFOutOfRangeException,
	    {
		s[0] = [mutableStringClass stringWithString: @"𝄞öö"];
		[s[0] deleteCharactersInRange: of_range(2, 2)];
	    })

	EXPECT_EXCEPTION(@"Detect OoR in -[deleteCharactersInRange:] #2",
	    OFOutOfRangeException,
	    [s[0] deleteCharactersInRange: of_range(4, 0)])

	EXPECT_EXCEPTION(@"Detect OoR in "
	    @"-[replaceCharactersInRange:withString:] #1",
	    OFOutOfRangeException,
	    [s[0] replaceCharactersInRange: of_range(2, 2)
				withString: @""])

	EXPECT_EXCEPTION(@"Detect OoR in "
	    @"-[replaceCharactersInRange:withString:] #2",
	    OFOutOfRangeException,
	    [s[0] replaceCharactersInRange: of_range(4, 0)
				withString: @""])

	TEST(@"-[replaceOccurrencesOfString:withString:]",
	    (s[0] = [mutableStringClass stringWithString:
	    @"asd fo asd fofo asd"]) &&
	    R([s[0] replaceOccurrencesOfString: @"fo"
				    withString: @"foo"]) &&
	    [s[0] isEqual: @"asd foo asd foofoo asd"] &&
	    (s[0] = [mutableStringClass stringWithString: @"XX"]) &&
	    R([s[0] replaceOccurrencesOfString: @"X"
				    withString: @"XX"]) &&
	    [s[0] isEqual: @"XXXX"])

	TEST(@"-[replaceOccurrencesOfString:withString:options:range:]",
	    (s[0] = [mutableStringClass stringWithString:
	    @"foofoobarfoobarfoo"]) &&
	    R([s[0] replaceOccurrencesOfString: @"oo"
				    withString: @"óò"
				       options: 0
					 range: of_range(2, 15)]) &&
	    [s[0] isEqual: @"foofóòbarfóòbarfoo"])

	TEST(@"-[deleteLeadingWhitespaces]",

	    (s[0] = [mutableStringClass stringWithString: whitespace[0]]) &&
	    R([s[0] deleteLeadingWhitespaces]) &&
	    [s[0] isEqual: @"asd  \t \t\t\r\n"] &&

	    (s[0] = [mutableStringClass stringWithString: whitespace[1]]) &&
	    R([s[0] deleteLeadingWhitespaces]) && [s[0] isEqual: @""])


	TEST(@"-[deleteTrailingWhitespaces]",

	    (s[0] = [mutableStringClass stringWithString: whitespace[0]]) &&
	    R([s[0] deleteTrailingWhitespaces]) &&
	    [s[0] isEqual: @" \r \t\n\t \tasd"] &&

	    (s[0] = [mutableStringClass stringWithString: whitespace[1]]) &&
	    R([s[0] deleteTrailingWhitespaces]) && [s[0] isEqual: @""])


	TEST(@"-[deleteEnclosingWhitespaces]",

	    (s[0] = [mutableStringClass stringWithString: whitespace[0]]) &&
	    R([s[0] deleteEnclosingWhitespaces]) && [s[0] isEqual: @"asd"] &&


	    (s[0] = [mutableStringClass stringWithString: whitespace[1]]) &&
	    R([s[0] deleteEnclosingWhitespaces]) && [s[0] isEqual: @""])


#ifdef OF_HAVE_UNICODE_TABLES
	TEST(@"-[decomposedStringWithCanonicalMapping]",
	    [C(@"H\xC3\xA4llj\xC3\xB6").decomposedStringWithCanonicalMapping
	    isEqual: @"H\x61\xCC\x88llj\x6F\xCC\x88"]);

	TEST(@"-[decomposedStringWithCompatibilityMapping]",
	    [C(@"H\xC3\xA4llj\xC3\xB6").decomposedStringWithCompatibilityMapping
	    isEqual: @"H\x61\xCC\x88llj\x6F\xCC\x88"]);
#endif

	TEST(@"-[stringByXMLEscaping]",
	    (is = C(@"<hello> &world'\"!&").stringByXMLEscaping) &&
	    [is isEqual: @"&lt;hello&gt; &amp;world&apos;&quot;!&amp;"])

	TEST(@"-[stringByXMLUnescaping]",
	    [is.stringByXMLUnescaping isEqual: @"<hello> &world'\"!&"] &&
	    [C(@"&#x79;").stringByXMLUnescaping isEqual: @"y"] &&
	    [C(@"&#xe4;").stringByXMLUnescaping isEqual: @"ä"] &&
	    [C(@"&#8364;").stringByXMLUnescaping isEqual: @"€"] &&
	    [C(@"&#x1D11E;").stringByXMLUnescaping isEqual: @"𝄞"])

	EXPECT_EXCEPTION(@"Detect unknown entities in -[stringByXMLUnescaping]",
	    OFUnknownXMLEntityException, [C(@"&foo;") stringByXMLUnescaping])







|
|


|

|

|
>
|
|
|
|
>



|
>
|
|
|
|
>
|

|


>
|


|


|
<
|
>

|
|
|

|
|
|

|
|
|
>

>
|


|






>
|
|
<
|









>
|
|
<
|
|
<
|
|
<
|
|
<
|


>
|
|
|
|
|


>
|
|
|
|
|
|
|
|
|
|




|
|




|




|
|




|
|


|

|
|
|
|
|
|
|


|
|
|
|
|
|
|


>
|
|
|
>
|
|
>


>
|
|
|
>
|
|
>


>
|
|
>
>
|
|
>












|
|


|







1193
1194
1195
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
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

	EXPECT_EXCEPTION(@"Detect out of range in -[unsignedLongLongValue]",
	    OFOutOfRangeException,
	    [C(@"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
	       @"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")
	    unsignedLongLongValueWithBase: 16])

	TEST(@"-[characters]", (characters = C(@"fööbär🀺").characters) &&
	    !memcmp(characters, unicharString + 1, sizeof(unicharString) - 8))

#ifdef OF_BIG_ENDIAN
# define swappedByteOrder OFByteOrderLittleEndian
#else
# define swappedByteOrder OFByteOrderBigEndian
#endif
	TEST(@"-[UTF16String]", (UTF16Characters = C(@"fööbär🀺").UTF16String) &&
	    !memcmp(UTF16Characters, char16String + 1,
	    OFUTF16StringLength(char16String) * 2) &&
	    (UTF16Characters = [C(@"fööbär🀺")
	    UTF16StringWithByteOrder: swappedByteOrder]) &&
	    !memcmp(UTF16Characters, swappedChar16String + 1,
	    OFUTF16StringLength(swappedChar16String) * 2))

	TEST(@"-[UTF16StringLength]", C(@"fööbär🀺").UTF16StringLength == 8)

	TEST(@"-[UTF32String]", (characters = C(@"fööbär🀺").UTF32String) &&
	    !memcmp(characters, unicharString + 1,
	    OFUTF32StringLength(unicharString) * 4) &&
	    (characters = [C(@"fööbär🀺") UTF32StringWithByteOrder:
	    swappedByteOrder]) &&
	    !memcmp(characters, swappedUnicharString + 1,
	    OFUTF32StringLength(swappedUnicharString) * 4))
#undef swappedByteOrder

	TEST(@"-[stringByMD5Hashing]", [C(@"asdfoobar").stringByMD5Hashing
	    isEqual: @"184dce2ec49b5422c7cfd8728864db4c"])

	TEST(@"-[stringByRIPEMD160Hashing]",
	    [C(@"asdfoobar").stringByRIPEMD160Hashing
	    isEqual: @"021d773b0fac06eb6755ca6aa58a580c980f7f13"])

	TEST(@"-[stringBySHA1Hashing]", [C(@"asdfoobar").stringBySHA1Hashing
	    isEqual: @"f5f81ac0a8b5cbfdc4585ec1ad32e7b3a12b9b49"])

	TEST(@"-[stringBySHA224Hashing]", [C(@"asdfoobar").stringBySHA224Hashing

	    isEqual: @"5a06822dcbd5a874f67d062b80b9d8a9cb9b5b303960b9da9290c192"
	    ])

	TEST(@"-[stringBySHA256Hashing]", [C(@"asdfoobar").stringBySHA256Hashing
	    isEqual: @"28e65b1dcd7f6ce2ea6277b15f87b913628b5500bf7913a2bbf4cedc"
		     @"fa1215f6"])

	TEST(@"-[stringBySHA384Hashing]", [C(@"asdfoobar").stringBySHA384Hashing
	    isEqual: @"73286da882ffddca2f45e005cfa6b44f3fc65bfb26db1d087ded2f9c"
		     @"279e5addf8be854044bca0cece073fce28eec7d9"])

	TEST(@"-[stringBySHA512Hashing]", [C(@"asdfoobar").stringBySHA512Hashing
	    isEqual: @"0464c427da158b02161bb44a3090bbfc594611ef6a53603640454b56"
		     @"412a9247c3579a329e53a5dc74676b106755e3394f9454a2d4227324"
		     @"2615d32f80437d61"])

	characterSet =
	    [OFCharacterSet characterSetWithCharactersInString: @"abfo'_~$🍏"];
	TEST(@"-[stringByURLEncodingWithAllowedCharacters:]",
	    [[C(@"foo\"ba'_~$]🍏🍌") stringByURLEncodingWithAllowedCharacters:
	    characterSet] isEqual: @"foo%22ba'_~$%5D🍏%F0%9F%8D%8C"])

	TEST(@"-[stringByURLDecoding]",
	    [C(@"foo%20bar%22+%24%F0%9F%8D%8C").stringByURLDecoding
	    isEqual: @"foo bar\"+$🍌"])

	TEST(@"-[insertString:atIndex:]",
	    (mutableString1 = [mutableStringClass
	    stringWithString: @"𝄞öööbä€"]) &&
	    R([mutableString1 insertString: @"äöü" atIndex: 3]) &&

	    [mutableString1 isEqual: @"𝄞ööäöüöbä€"])

	EXPECT_EXCEPTION(@"Detect invalid format in -[stringByURLDecoding] "
	    @"#1", OFInvalidFormatException,
	    [C(@"foo%xbar") stringByURLDecoding])
	EXPECT_EXCEPTION(@"Detect invalid encoding in -[stringByURLDecoding] "
	    @"#2", OFInvalidEncodingException,
	    [C(@"foo%FFbar") stringByURLDecoding])

	TEST(@"-[setCharacter:atIndex:]",
	    (mutableString1 = [mutableStringClass
	    stringWithString: @"abäde"]) &&
	    R([mutableString1 setCharacter: 0xF6 atIndex: 2]) &&

	    [mutableString1 isEqual: @"aböde"] &&
	    R([mutableString1 setCharacter: 'c' atIndex: 2]) &&

	    [mutableString1 isEqual: @"abcde"] &&
	    R([mutableString1 setCharacter: 0x20AC atIndex: 3]) &&

	    [mutableString1 isEqual: @"abc€e"] &&
	    R([mutableString1 setCharacter: 'x' atIndex: 1]) &&

	    [mutableString1 isEqual: @"axc€e"])

	TEST(@"-[deleteCharactersInRange:]",
	    (mutableString1 = [mutableStringClass
	    stringWithString: @"𝄞öööbä€"]) &&
	    R([mutableString1 deleteCharactersInRange: OFRangeMake(1, 3)]) &&
	    [mutableString1 isEqual: @"𝄞bä€"] &&
	    R([mutableString1 deleteCharactersInRange: OFRangeMake(0, 4)]) &&
	    [mutableString1 isEqual: @""])

	TEST(@"-[replaceCharactersInRange:withString:]",
	    (mutableString1 = [mutableStringClass
	    stringWithString: @"𝄞öööbä€"]) &&
	    R([mutableString1 replaceCharactersInRange: OFRangeMake(1, 3)
					    withString: @"äöüß"]) &&
	    [mutableString1 isEqual: @"𝄞äöüßbä€"] &&
	    R([mutableString1 replaceCharactersInRange: OFRangeMake(4, 2)
					    withString: @"b"]) &&
	    [mutableString1 isEqual: @"𝄞äöübä€"] &&
	    R([mutableString1 replaceCharactersInRange: OFRangeMake(0, 7)
					    withString: @""]) &&
	    [mutableString1 isEqual: @""])

	EXPECT_EXCEPTION(@"Detect OoR in -[deleteCharactersInRange:] #1",
	    OFOutOfRangeException,
	    {
		mutableString1 = [mutableStringClass stringWithString: @"𝄞öö"];
		[mutableString1 deleteCharactersInRange: OFRangeMake(2, 2)];
	    })

	EXPECT_EXCEPTION(@"Detect OoR in -[deleteCharactersInRange:] #2",
	    OFOutOfRangeException,
	    [mutableString1 deleteCharactersInRange: OFRangeMake(4, 0)])

	EXPECT_EXCEPTION(@"Detect OoR in "
	    @"-[replaceCharactersInRange:withString:] #1",
	    OFOutOfRangeException,
	    [mutableString1 replaceCharactersInRange: OFRangeMake(2, 2)
					  withString: @""])

	EXPECT_EXCEPTION(@"Detect OoR in "
	    @"-[replaceCharactersInRange:withString:] #2",
	    OFOutOfRangeException,
	    [mutableString1 replaceCharactersInRange: OFRangeMake(4, 0)
					  withString: @""])

	TEST(@"-[replaceOccurrencesOfString:withString:]",
	    (mutableString1 = [mutableStringClass stringWithString:
	    @"asd fo asd fofo asd"]) &&
	    R([mutableString1 replaceOccurrencesOfString: @"fo"
					      withString: @"foo"]) &&
	    [mutableString1 isEqual: @"asd foo asd foofoo asd"] &&
	    (mutableString1 = [mutableStringClass stringWithString: @"XX"]) &&
	    R([mutableString1 replaceOccurrencesOfString: @"X"
					      withString: @"XX"]) &&
	    [mutableString1 isEqual: @"XXXX"])

	TEST(@"-[replaceOccurrencesOfString:withString:options:range:]",
	    (mutableString1 = [mutableStringClass stringWithString:
	    @"foofoobarfoobarfoo"]) && R([mutableString1
	    replaceOccurrencesOfString: @"oo"
			    withString: @"óò"
			       options: 0
				 range: OFRangeMake(2, 15)]) &&
	    [mutableString1 isEqual: @"foofóòbarfóòbarfoo"])

	TEST(@"-[deleteLeadingWhitespaces]",
	    (mutableString1 = [mutableStringClass
	    stringWithString: whitespace[0]]) &&
	    R([mutableString1 deleteLeadingWhitespaces]) &&
	    [mutableString1 isEqual: @"asd  \t \t\t\r\n"] &&
	    (mutableString1 = [mutableStringClass
	    stringWithString: whitespace[1]]) &&
	    R([mutableString1 deleteLeadingWhitespaces]) &&
	    [mutableString1 isEqual: @""])

	TEST(@"-[deleteTrailingWhitespaces]",
	    (mutableString1 = [mutableStringClass
	    stringWithString: whitespace[0]]) &&
	    R([mutableString1 deleteTrailingWhitespaces]) &&
	    [mutableString1 isEqual: @" \r \t\n\t \tasd"] &&
	    (mutableString1 = [mutableStringClass
	    stringWithString: whitespace[1]]) &&
	    R([mutableString1 deleteTrailingWhitespaces]) &&
	    [mutableString1 isEqual: @""])

	TEST(@"-[deleteEnclosingWhitespaces]",
	    (mutableString1 = [mutableStringClass
	    stringWithString: whitespace[0]]) &&
	    R([mutableString1 deleteEnclosingWhitespaces]) &&
	    [mutableString1 isEqual: @"asd"] &&
	    (mutableString1 = [mutableStringClass
	    stringWithString: whitespace[1]]) &&
	    R([mutableString1 deleteEnclosingWhitespaces]) &&
	    [mutableString1 isEqual: @""])

#ifdef OF_HAVE_UNICODE_TABLES
	TEST(@"-[decomposedStringWithCanonicalMapping]",
	    [C(@"H\xC3\xA4llj\xC3\xB6").decomposedStringWithCanonicalMapping
	    isEqual: @"H\x61\xCC\x88llj\x6F\xCC\x88"]);

	TEST(@"-[decomposedStringWithCompatibilityMapping]",
	    [C(@"H\xC3\xA4llj\xC3\xB6").decomposedStringWithCompatibilityMapping
	    isEqual: @"H\x61\xCC\x88llj\x6F\xCC\x88"]);
#endif

	TEST(@"-[stringByXMLEscaping]",
	    (string = C(@"<hello> &world'\"!&").stringByXMLEscaping) &&
	    [string isEqual: @"&lt;hello&gt; &amp;world&apos;&quot;!&amp;"])

	TEST(@"-[stringByXMLUnescaping]",
	    [string.stringByXMLUnescaping isEqual: @"<hello> &world'\"!&"] &&
	    [C(@"&#x79;").stringByXMLUnescaping isEqual: @"y"] &&
	    [C(@"&#xe4;").stringByXMLUnescaping isEqual: @"ä"] &&
	    [C(@"&#8364;").stringByXMLUnescaping isEqual: @"€"] &&
	    [C(@"&#x1D11E;").stringByXMLUnescaping isEqual: @"𝄞"])

	EXPECT_EXCEPTION(@"Detect unknown entities in -[stringByXMLUnescaping]",
	    OFUnknownXMLEntityException, [C(@"&foo;") stringByXMLUnescaping])
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
	EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] "
	    @"#4", OFInvalidFormatException, [C(@"&#g;") stringByXMLUnescaping])
	EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] "
	    @"#5", OFInvalidFormatException,
	    [C(@"&#xg;") stringByXMLUnescaping])

	TEST(@"-[stringByXMLUnescapingWithDelegate:]",
	    (h = [[[EntityHandler alloc] init] autorelease]) &&
	    [[C(@"x&foo;y") stringByXMLUnescapingWithDelegate: h]
	    isEqual: @"xbary"])

#ifdef OF_HAVE_BLOCKS
	TEST(@"-[stringByXMLUnescapingWithBlock:]",
	    [[C(@"x&foo;y") stringByXMLUnescapingWithBlock:
	        ^ OFString *(OFString *str, OFString *entity) {
		    if ([entity isEqual: @"foo"])
			    return @"bar";

		    return nil;
	    }] isEqual: @"xbary"])

	j = 0;







|
|





|







1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
	EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] "
	    @"#4", OFInvalidFormatException, [C(@"&#g;") stringByXMLUnescaping])
	EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] "
	    @"#5", OFInvalidFormatException,
	    [C(@"&#xg;") stringByXMLUnescaping])

	TEST(@"-[stringByXMLUnescapingWithDelegate:]",
	    (entityHandler = [[[EntityHandler alloc] init] autorelease]) &&
	    [[C(@"x&foo;y") stringByXMLUnescapingWithDelegate: entityHandler]
	    isEqual: @"xbary"])

#ifdef OF_HAVE_BLOCKS
	TEST(@"-[stringByXMLUnescapingWithBlock:]",
	    [[C(@"x&foo;y") stringByXMLUnescapingWithBlock:
		^ OFString *(OFString *str, OFString *entity) {
		    if ([entity isEqual: @"foo"])
			    return @"bar";

		    return nil;
	    }] isEqual: @"xbary"])

	j = 0;

Modified tests/OFSystemInfoTests.m from [62ec592705] to [9c2137f367].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
- (void)systemInfoTests
{
	void *pool = objc_autoreleasePoolPush();
#ifdef OF_HAVE_FILES
	OFString *userConfigPath, *userDataPath;
#endif

	[of_stdout setForegroundColor: [OFColor lime]];

	[of_stdout writeFormat: @"[OFSystemInfo] Page size: %zd\n",
	    [OFSystemInfo pageSize]];

	[of_stdout writeFormat: @"[OFSystemInfo] Number of CPUs: %zd\n",
	    [OFSystemInfo numberOfCPUs]];

	[of_stdout writeFormat: @"[OFSystemInfo] ObjFW version: %@\n",
	    [OFSystemInfo ObjFWVersion]];

	[of_stdout writeFormat: @"[OFSystemInfo] ObjFW version major: %u\n",
	    [OFSystemInfo ObjFWVersionMajor]];

	[of_stdout writeFormat: @"[OFSystemInfo] ObjFW version minor: %u\n",
	    [OFSystemInfo ObjFWVersionMinor]];

	[of_stdout writeFormat: @"[OFSystemInfo] Operating system name: %@\n",
	    [OFSystemInfo operatingSystemName]];

	[of_stdout writeFormat:
	    @"[OFSystemInfo] Operating system version: %@\n",
	    [OFSystemInfo operatingSystemVersion]];

#ifdef OF_HAVE_FILES
	@try {
		userConfigPath = [OFSystemInfo userConfigPath];
	} @catch (OFNotImplementedException *e) {
		userConfigPath = @"Not implemented";
	}
	[of_stdout writeFormat: @"[OFSystemInfo] User config path: %@\n",
	    userConfigPath];

	@try {
		userDataPath = [OFSystemInfo userDataPath];
	} @catch (OFNotImplementedException *e) {
		userDataPath = @"Not implemented";
	}
	[of_stdout writeFormat: @"[OFSystemInfo] User data path: %@\n",
	    userDataPath];
#endif

	[of_stdout writeFormat: @"[OFSystemInfo] CPU vendor: %@\n",
	    [OFSystemInfo CPUVendor]];

	[of_stdout writeFormat: @"[OFSystemInfo] CPU model: %@\n",
	    [OFSystemInfo CPUModel]];

#if defined(OF_X86_64) || defined(OF_X86)
	[of_stdout writeFormat: @"[OFSystemInfo] Supports MMX: %d\n",
	    [OFSystemInfo supportsMMX]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE: %d\n",
	    [OFSystemInfo supportsSSE]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE2: %d\n",
	    [OFSystemInfo supportsSSE2]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE3: %d\n",
	    [OFSystemInfo supportsSSE3]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSSE3: %d\n",
	    [OFSystemInfo supportsSSSE3]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE4.1: %d\n",
	    [OFSystemInfo supportsSSE41]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE4.2: %d\n",
	    [OFSystemInfo supportsSSE42]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports AVX: %d\n",
	    [OFSystemInfo supportsAVX]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports AVX2: %d\n",
	    [OFSystemInfo supportsAVX2]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports AES-NI: %d\n",
	    [OFSystemInfo supportsAESNI]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SHA extensions: %d\n",
	    [OFSystemInfo supportsSHAExtensions]];
#endif

#ifdef OF_POWERPC
	[of_stdout writeFormat: @"[OFSystemInfo] Supports AltiVec: %d\n",
	    [OFSystemInfo supportsAltiVec]];
#endif

	objc_autoreleasePoolPop(pool);
}
@end







|

|


|


|


|


|


|


|









|







|



|


|



|


|


|


|


|


|


|


|


|


|


|




|






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
- (void)systemInfoTests
{
	void *pool = objc_autoreleasePoolPush();
#ifdef OF_HAVE_FILES
	OFString *userConfigPath, *userDataPath;
#endif

	[OFStdOut setForegroundColor: [OFColor lime]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Page size: %zd\n",
	    [OFSystemInfo pageSize]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Number of CPUs: %zd\n",
	    [OFSystemInfo numberOfCPUs]];

	[OFStdOut writeFormat: @"[OFSystemInfo] ObjFW version: %@\n",
	    [OFSystemInfo ObjFWVersion]];

	[OFStdOut writeFormat: @"[OFSystemInfo] ObjFW version major: %u\n",
	    [OFSystemInfo ObjFWVersionMajor]];

	[OFStdOut writeFormat: @"[OFSystemInfo] ObjFW version minor: %u\n",
	    [OFSystemInfo ObjFWVersionMinor]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Operating system name: %@\n",
	    [OFSystemInfo operatingSystemName]];

	[OFStdOut writeFormat:
	    @"[OFSystemInfo] Operating system version: %@\n",
	    [OFSystemInfo operatingSystemVersion]];

#ifdef OF_HAVE_FILES
	@try {
		userConfigPath = [OFSystemInfo userConfigPath];
	} @catch (OFNotImplementedException *e) {
		userConfigPath = @"Not implemented";
	}
	[OFStdOut writeFormat: @"[OFSystemInfo] User config path: %@\n",
	    userConfigPath];

	@try {
		userDataPath = [OFSystemInfo userDataPath];
	} @catch (OFNotImplementedException *e) {
		userDataPath = @"Not implemented";
	}
	[OFStdOut writeFormat: @"[OFSystemInfo] User data path: %@\n",
	    userDataPath];
#endif

	[OFStdOut writeFormat: @"[OFSystemInfo] CPU vendor: %@\n",
	    [OFSystemInfo CPUVendor]];

	[OFStdOut writeFormat: @"[OFSystemInfo] CPU model: %@\n",
	    [OFSystemInfo CPUModel]];

#if defined(OF_X86_64) || defined(OF_X86)
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports MMX: %d\n",
	    [OFSystemInfo supportsMMX]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE: %d\n",
	    [OFSystemInfo supportsSSE]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE2: %d\n",
	    [OFSystemInfo supportsSSE2]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE3: %d\n",
	    [OFSystemInfo supportsSSE3]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSSE3: %d\n",
	    [OFSystemInfo supportsSSSE3]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE4.1: %d\n",
	    [OFSystemInfo supportsSSE41]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SSE4.2: %d\n",
	    [OFSystemInfo supportsSSE42]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports AVX: %d\n",
	    [OFSystemInfo supportsAVX]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports AVX2: %d\n",
	    [OFSystemInfo supportsAVX2]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports AES-NI: %d\n",
	    [OFSystemInfo supportsAESNI]];

	[OFStdOut writeFormat: @"[OFSystemInfo] Supports SHA extensions: %d\n",
	    [OFSystemInfo supportsSHAExtensions]];
#endif

#ifdef OF_POWERPC
	[OFStdOut writeFormat: @"[OFSystemInfo] Supports AltiVec: %d\n",
	    [OFSystemInfo supportsAltiVec]];
#endif

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFTCPSocketTests.m from [549b791525] to [e205251e21].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFTCPSocket";

@implementation TestsAppDelegate (OFTCPSocketTests)
- (void)TCPSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFTCPSocket *server, *client = nil, *accepted;
	uint16_t port;
	char buf[6];

	TEST(@"+[socket]", (server = [OFTCPSocket socket]) &&
	    (client = [OFTCPSocket socket]))

	TEST(@"-[bindToHost:port:]",
	    (port = [server bindToHost: @"127.0.0.1"
				  port: 0]))

	TEST(@"-[listen]", R([server listen]))

	TEST(@"-[connectToHost:port:]",
	    R([client connectToHost: @"127.0.0.1"
			       port: port]))

	TEST(@"-[accept]", (accepted = [server accept]))

	TEST(@"-[remoteAddress]",
	    [of_socket_address_ip_string(accepted.remoteAddress, NULL)
	    isEqual: @"127.0.0.1"])

	TEST(@"-[writeString:]", [client writeString: @"Hello!"])

	TEST(@"-[readIntoBuffer:length:]", [accepted readIntoBuffer: buf
							     length: 6] &&
	    !memcmp(buf, "Hello!", 6))

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|







|





|
<




|
<




|


|

|
|
|




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFTCPSocket";

@implementation TestsAppDelegate (OFTCPSocketTests)
- (void)TCPSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFTCPSocket *server, *client = nil, *accepted;
	uint16_t port;
	char buffer[6];

	TEST(@"+[socket]", (server = [OFTCPSocket socket]) &&
	    (client = [OFTCPSocket socket]))

	TEST(@"-[bindToHost:port:]",
	    (port = [server bindToHost: @"127.0.0.1" port: 0]))


	TEST(@"-[listen]", R([server listen]))

	TEST(@"-[connectToHost:port:]",
	    R([client connectToHost: @"127.0.0.1" port: port]))


	TEST(@"-[accept]", (accepted = [server accept]))

	TEST(@"-[remoteAddress]",
	    [OFSocketAddressString(accepted.remoteAddress)
	    isEqual: @"127.0.0.1"])

	TEST(@"-[writeString:]", R([client writeString: @"Hello!"]))

	TEST(@"-[readIntoBuffer:length:]",
	    [accepted readIntoBuffer: buffer length: 6] &&
	    !memcmp(buffer, "Hello!", 6))

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFThreadTests.m from [86faf24c0e] to [3abe7860ab].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFThread";

@interface TestThread: OFThread
@end

@implementation TestThread
- (id)main
{
	[[OFThread threadDictionary] setObject: @"bar"

					forKey: @"foo"];

	return @"success";
}
@end

@implementation TestsAppDelegate (OFThreadTests)
- (void)threadTests
{
	void *pool = objc_autoreleasePoolPush();
	TestThread *t;
	OFMutableDictionary *d;

	TEST(@"+[thread]", (t = [TestThread thread]))

	TEST(@"-[start]", R([t start]))

	TEST(@"-[join]", [[t join] isEqual: @"success"])

	TEST(@"-[threadDictionary]", (d = [OFThread threadDictionary]) &&
	    [d objectForKey: @"foo"] == nil)

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|







|
>
|









|
<

|

|

|

|
|




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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFThread";

@interface TestThread: OFThread
@end

@implementation TestThread
- (id)main
{
	[[OFThread threadDictionary] setObject: @"bar" forKey: @"foo"];
	OFEnsure([[[OFThread threadDictionary]
	    objectForKey: @"foo"] isEqual: @"bar"]);

	return @"success";
}
@end

@implementation TestsAppDelegate (OFThreadTests)
- (void)threadTests
{
	void *pool = objc_autoreleasePoolPush();
	TestThread *thread;


	TEST(@"+[thread]", (thread = [TestThread thread]))

	TEST(@"-[start]", R([thread start]))

	TEST(@"-[join]", [[thread join] isEqual: @"success"])

	TEST(@"-[threadDictionary]",
	    [[OFThread threadDictionary] objectForKey: @"foo"] == nil)

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFUDPSocketTests.m from [555c13caae] to [b882de1802].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFUDPSocket";

@implementation TestsAppDelegate (OFUDPSocketTests)
- (void)UDPSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFUDPSocket *sock;
	uint16_t port1, port2;
	of_socket_address_t addr1, addr2, addr3;
	char buf[6];
	OFString *host;

	TEST(@"+[socket]", (sock = [OFUDPSocket socket]))

	TEST(@"-[bindToHost:port:]",
	    (port1 = [sock bindToHost: @"127.0.0.1"
				 port: 0]))

	addr1 = of_socket_address_parse_ip(@"127.0.0.1", port1);

	TEST(@"-[sendBuffer:length:receiver:]",
	    R([sock sendBuffer: "Hello"
			length: 6
		      receiver: &addr1]))

	TEST(@"-[receiveIntoBuffer:length:sender:]",
	    [sock receiveIntoBuffer: buf
			     length: 6
			     sender: &addr2] == 6 &&
	    !memcmp(buf, "Hello", 6) &&
	    (host = of_socket_address_ip_string(&addr2, &port2)) &&
	    [host isEqual: @"127.0.0.1"] && port2 == port1)


	addr3 = of_socket_address_parse_ip(@"127.0.0.1", port1 + 1);

	/*
	 * TODO: Move those tests elsewhere as soon as the DNS resolving part
	 *	 is no longer in OFUDPSocket.
	 */
	TEST(@"of_socket_address_equal()",
	    of_socket_address_equal(&addr1, &addr2) &&
	    !of_socket_address_equal(&addr1, &addr3))

	TEST(@"of_socket_address_hash()",
	    of_socket_address_hash(&addr1) == of_socket_address_hash(&addr2) &&
	    of_socket_address_hash(&addr1) != of_socket_address_hash(&addr3))

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|



















|






|
|

<




|
<

|


|
<
<


|
<
<

<
|
>

|





|
|
|

|
|
|




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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFUDPSocket";

@implementation TestsAppDelegate (OFUDPSocketTests)
- (void)UDPSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFUDPSocket *sock;
	uint16_t port1;
	OFSocketAddress addr1, addr2, addr3;
	char buf[6];


	TEST(@"+[socket]", (sock = [OFUDPSocket socket]))

	TEST(@"-[bindToHost:port:]",
	    (port1 = [sock bindToHost: @"127.0.0.1" port: 0]))


	addr1 = OFSocketAddressParseIP(@"127.0.0.1", port1);

	TEST(@"-[sendBuffer:length:receiver:]",
	    R([sock sendBuffer: "Hello" length: 6 receiver: &addr1]))



	TEST(@"-[receiveIntoBuffer:length:sender:]",
	    [sock receiveIntoBuffer: buf length: 6 sender: &addr2] == 6 &&


	    !memcmp(buf, "Hello", 6) &&

	    [OFSocketAddressString(&addr2) isEqual: @"127.0.0.1"] &&
	    OFSocketAddressPort(&addr2) == port1)

	addr3 = OFSocketAddressParseIP(@"127.0.0.1", port1 + 1);

	/*
	 * TODO: Move those tests elsewhere as soon as the DNS resolving part
	 *	 is no longer in OFUDPSocket.
	 */
	TEST(@"OFSocketAddressEqual()",
	    OFSocketAddressEqual(&addr1, &addr2) &&
	    !OFSocketAddressEqual(&addr1, &addr3))

	TEST(@"OFSocketAddressHash()",
	    OFSocketAddressHash(&addr1) == OFSocketAddressHash(&addr2) &&
	    OFSocketAddressHash(&addr1) != OFSocketAddressHash(&addr3))

	objc_autoreleasePoolPop(pool);
}
@end

Added tests/OFUNIXDatagramSocketTests.m version [26b71a4581].



















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "TestsAppDelegate.h"

static OFString *const module = @"OFUNIXDatagramSocket";

@implementation TestsAppDelegate (OFUNIXDatagramSocketTests)
- (void)UNIXDatagramSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFString *path;
	OFUNIXDatagramSocket *sock;
	OFSocketAddress address1, address2;
	char buffer[5];

#if defined(OF_HAVE_FILES) && !defined(OF_IOS)
	path = [[OFSystemInfo temporaryDirectoryPath]
	    stringByAppendingPathComponent: [[OFUUID UUID] UUIDString]];
#else
	/*
	 * We can have sockets, including UNIX sockets, while file support is
	 * disabled.
	 *
	 * We also use this code path for iOS, as the temporaryDirectoryPath is
	 * too long on the iOS simulator.
	 */
	path = [OFString stringWithFormat: @"/tmp/%@",
					   [[OFUUID UUID] UUIDString]];
#endif

	TEST(@"+[socket]", (sock = [OFUNIXDatagramSocket socket]))

	@try {
		TEST(@"-[bindToPath:]", R(address1 = [sock bindToPath: path]))
	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EAFNOSUPPORT:
		case EPERM:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFUNIXDatagramSocket] -[bindToPath:]: "
			    @"UNIX datagram sockets unsupported, skipping "
			    @"tests"];

			objc_autoreleasePoolPop(pool);
			return;
		default:
			@throw e;
		}
	}

	@try {
		TEST(@"-[sendBuffer:length:receiver:]",
		    R([sock sendBuffer: "Hello" length: 5 receiver: &address1]))

		TEST(@"-[receiveIntoBuffer:length:sender:]",
		    [sock receiveIntoBuffer: buffer
				     length: 5
				     sender: &address2] == 5 &&
		    memcmp(buffer, "Hello", 5) == 0 &&
		    OFSocketAddressEqual(&address1, &address2) &&
		    OFSocketAddressHash(&address1) ==
		    OFSocketAddressHash(&address2))
	} @finally {
#ifdef OF_HAVE_FILES
		[[OFFileManager defaultManager] removeItemAtPath: path];
#endif
	}

	objc_autoreleasePoolPop(pool);
}
@end

Added tests/OFUNIXStreamSocketTests.m version [5aa361cd34].



























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * 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 "TestsAppDelegate.h"

static OFString *const module = @"OFUNIXStreamSocket";

@implementation TestsAppDelegate (OFUNIXStreamSocketTests)
- (void)UNIXStreamSocketTests
{
	void *pool = objc_autoreleasePoolPush();
	OFString *path;
	OFUNIXStreamSocket *sockClient, *sockServer, *sockAccepted;
	char buffer[5];

#if defined(OF_HAVE_FILES) && !defined(OF_IOS)
	path = [[OFSystemInfo temporaryDirectoryPath]
	    stringByAppendingPathComponent: [[OFUUID UUID] UUIDString]];
#else
	/*
	 * We can have sockets, including UNIX sockets, while file support is
	 * disabled.
	 *
	 * We also use this code path for iOS, as the temporaryDirectoryPath is
	 * too long on the iOS simulator.
	 */
	path = [OFString stringWithFormat: @"/tmp/%@",
					   [[OFUUID UUID] UUIDString]];
#endif

	TEST(@"+[socket]", (sockClient = [OFUNIXStreamSocket socket]) &&
	    (sockServer = [OFUNIXStreamSocket socket]))

	@try {
		TEST(@"-[bindToPath:]", R([sockServer bindToPath: path]))
	} @catch (OFBindFailedException *e) {
		switch (e.errNo) {
		case EAFNOSUPPORT:
		case EPERM:
			[OFStdOut setForegroundColor: [OFColor lime]];
			[OFStdOut writeLine:
			    @"\r[OFUNIXStreamSocket] -[bindToPath:]: "
			    @"UNIX stream sockets unsupported, skipping tests"];

			objc_autoreleasePoolPop(pool);
			return;
		default:
			@throw e;
		}
	}

	@try {
		TEST(@"-[listen]", R([sockServer listen]))

		TEST(@"-[connectToPath:]",
		    R([sockClient connectToPath: path]))

		TEST(@"-[accept]", (sockAccepted = [sockServer accept]))

		TEST(@"-[writeBuffer:length:]",
		    R([sockAccepted writeBuffer: "Hello" length: 5]))

		TEST(@"-[readIntoBuffer:length:]",
		    [sockClient readIntoBuffer: buffer length: 5] == 5 &&
		    memcmp(buffer, "Hello", 5) == 0)

		TEST(@"-[remoteAddress]",
		    OFSocketAddressUNIXPath(sockAccepted.remoteAddress) == nil)
	} @finally {
#ifdef OF_HAVE_FILES
		[[OFFileManager defaultManager] removeItemAtPath: path];
#endif
	}

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFURLTests.m from [307ed6832b] to [e63dcbf6b8].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFURL";
static OFString *url_str = @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/"
    @"pa%3Fth?que%23ry=1&f%26oo=b%3dar#frag%23ment";

@implementation TestsAppDelegate (OFURLTests)
- (void)URLTests
{
	void *pool = objc_autoreleasePoolPush();
	OFURL *u1, *u2, *u3, *u4, *u5, *u6, *u7;
	OFMutableURL *mu;

	TEST(@"+[URLWithString:]",
	    R(u1 = [OFURL URLWithString: url_str]) &&
	    R(u2 = [OFURL URLWithString: @"http://foo:80"]) &&
	    R(u3 = [OFURL URLWithString: @"http://bar/"]) &&
	    R(u4 = [OFURL URLWithString: @"file:///etc/passwd"]) &&
	    R(u5 = [OFURL URLWithString: @"http://foo/bar/qux/foo%2fbar"]) &&
	    R(u6 = [OFURL URLWithString: @"https://[12:34::56:abcd]/"]) &&
	    R(u7 = [OFURL URLWithString: @"https://[12:34::56:abcd]:234/"]))

	EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #1",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"ht,tp://foo"])

	EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #2",
	    OFInvalidFormatException,

<
<
|

















|
|






|
|


|
|
|
|
|
|
|







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFURL";
static OFString *URLString = @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/"
    @"pa%3Fth?que%23ry=1&f%26oo=b%3dar#frag%23ment";

@implementation TestsAppDelegate (OFURLTests)
- (void)URLTests
{
	void *pool = objc_autoreleasePoolPush();
	OFURL *URL1, *URL2, *URL3, *URL4, *URL5, *URL6, *URL7;
	OFMutableURL *mutableURL;

	TEST(@"+[URLWithString:]",
	    R(URL1 = [OFURL URLWithString: URLString]) &&
	    R(URL2 = [OFURL URLWithString: @"http://foo:80"]) &&
	    R(URL3 = [OFURL URLWithString: @"http://bar/"]) &&
	    R(URL4 = [OFURL URLWithString: @"file:///etc/passwd"]) &&
	    R(URL5 = [OFURL URLWithString: @"http://foo/bar/qux/foo%2fbar"]) &&
	    R(URL6 = [OFURL URLWithString: @"https://[12:34::56:abcd]/"]) &&
	    R(URL7 = [OFURL URLWithString: @"https://[12:34::56:abcd]:234/"]))

	EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #1",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"ht,tp://foo"])

	EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #2",
	    OFInvalidFormatException,
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
	    [OFURL URLWithString: @"https://[f]:/"])

	EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #8",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"https://[f]:f/"])

	TEST(@"+[URLWithString:relativeToURL:]",
	    [[[OFURL URLWithString: @"/foo"
		     relativeToURL: u1] string] isEqual:
	    @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/foo"] &&
	    [[[OFURL URLWithString: @"foo/bar?q"
		     relativeToURL: [OFURL URLWithString: @"http://h/qux/quux"]]
	    string] isEqual: @"http://h/qux/foo/bar?q"] &&
	    [[[OFURL URLWithString: @"foo/bar"
		     relativeToURL: [OFURL URLWithString: @"http://h/qux/?x"]]
	    string] isEqual: @"http://h/qux/foo/bar"] &&
	    [[[OFURL URLWithString: @"http://foo/?q"
		     relativeToURL: u1] string] isEqual: @"http://foo/?q"] &&
	    [[[OFURL URLWithString: @"foo"
		     relativeToURL: [OFURL URLWithString: @"http://foo/bar"]]
	    string] isEqual: @"http://foo/foo"] &&
	    [[[OFURL URLWithString: @"foo"
		     relativeToURL: [OFURL URLWithString: @"http://foo"]]
	    string] isEqual: @"http://foo/foo"])

	EXPECT_EXCEPTION(
	    @"+[URLWithString:relativeToURL:] fails with invalid characters #1",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"`"
		   relativeToURL: u1])

	EXPECT_EXCEPTION(
	    @"+[URLWithString:relativeToURL:] fails with invalid characters #2",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"/`"
		   relativeToURL: u1])

	EXPECT_EXCEPTION(
	    @"+[URLWithString:relativeToURL:] fails with invalid characters #3",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"?`"
		   relativeToURL: u1])

	EXPECT_EXCEPTION(
	    @"+[URLWithString:relativeToURL:] fails with invalid characters #4",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"#`"
		   relativeToURL: u1])

#ifdef OF_HAVE_FILES
	TEST(@"+[fileURLWithPath:]",
	    [[[OFURL fileURLWithPath: @"testfile.txt"] fileSystemRepresentation]
	    isEqual: [[OFFileManager defaultManager].currentDirectoryPath
	    stringByAppendingPathComponent: @"testfile.txt"]])








|
<
|







|










|
<




|
<




|
<




|
<







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
	    [OFURL URLWithString: @"https://[f]:/"])

	EXPECT_EXCEPTION(@"+[URLWithString:] fails with invalid characters #8",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"https://[f]:f/"])

	TEST(@"+[URLWithString:relativeToURL:]",
	    [[[OFURL URLWithString: @"/foo" relativeToURL: URL1] string]

	    isEqual: @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/foo"] &&
	    [[[OFURL URLWithString: @"foo/bar?q"
		     relativeToURL: [OFURL URLWithString: @"http://h/qux/quux"]]
	    string] isEqual: @"http://h/qux/foo/bar?q"] &&
	    [[[OFURL URLWithString: @"foo/bar"
		     relativeToURL: [OFURL URLWithString: @"http://h/qux/?x"]]
	    string] isEqual: @"http://h/qux/foo/bar"] &&
	    [[[OFURL URLWithString: @"http://foo/?q"
		     relativeToURL: URL1] string] isEqual: @"http://foo/?q"] &&
	    [[[OFURL URLWithString: @"foo"
		     relativeToURL: [OFURL URLWithString: @"http://foo/bar"]]
	    string] isEqual: @"http://foo/foo"] &&
	    [[[OFURL URLWithString: @"foo"
		     relativeToURL: [OFURL URLWithString: @"http://foo"]]
	    string] isEqual: @"http://foo/foo"])

	EXPECT_EXCEPTION(
	    @"+[URLWithString:relativeToURL:] fails with invalid characters #1",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"`" relativeToURL: URL1])


	EXPECT_EXCEPTION(
	    @"+[URLWithString:relativeToURL:] fails with invalid characters #2",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"/`" relativeToURL: URL1])


	EXPECT_EXCEPTION(
	    @"+[URLWithString:relativeToURL:] fails with invalid characters #3",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"?`" relativeToURL: URL1])


	EXPECT_EXCEPTION(
	    @"+[URLWithString:relativeToURL:] fails with invalid characters #4",
	    OFInvalidFormatException,
	    [OFURL URLWithString: @"#`" relativeToURL: URL1])


#ifdef OF_HAVE_FILES
	TEST(@"+[fileURLWithPath:]",
	    [[[OFURL fileURLWithPath: @"testfile.txt"] fileSystemRepresentation]
	    isEqual: [[OFFileManager defaultManager].currentDirectoryPath
	    stringByAppendingPathComponent: @"testfile.txt"]])

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
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
	    [tmp.host isEqual: @"test"] && [tmp.path isEqual: @"/"] &&
	    [tmp.string isEqual: @"file://test/"] &&
	    [tmp.fileSystemRepresentation isEqual: @"\\\\test"])
# endif
#endif

	TEST(@"-[string]",
	    [u1.string isEqual: url_str] &&
	    [u2.string isEqual: @"http://foo:80"] &&
	    [u3.string isEqual: @"http://bar/"] &&
	    [u4.string isEqual: @"file:///etc/passwd"])

	TEST(@"-[scheme]",
	    [u1.scheme isEqual: @"ht:tp"] && [u4.scheme isEqual: @"file"])

	TEST(@"-[user]", [u1.user isEqual: @"us:er"] && u4.user == nil)
	TEST(@"-[password]",
	    [u1.password isEqual: @"p@w"] && u4.password == nil)
	TEST(@"-[host]", [u1.host isEqual: @"ho:st"] &&
	    [u6.host isEqual: @"12:34::56:abcd"] &&
	    [u7.host isEqual: @"12:34::56:abcd"])
	TEST(@"-[port]", u1.port.unsignedShortValue == 1234 &&
	    [u4 port] == nil && u7.port.unsignedShortValue == 234)
	TEST(@"-[path]",

	    [u1.path isEqual: @"/pa?th"] && [u4.path isEqual: @"/etc/passwd"])
	TEST(@"-[pathComponents]",
	    [u1.pathComponents isEqual:
	    [OFArray arrayWithObjects: @"/", @"pa?th", nil]] &&
	    [u4.pathComponents isEqual:
	    [OFArray arrayWithObjects: @"/", @"etc", @"passwd", nil]] &&
	    [u5.pathComponents isEqual:
	    [OFArray arrayWithObjects: @"/", @"bar", @"qux", @"foo/bar", nil]])
	TEST(@"-[lastPathComponent]",
	    [[[OFURL URLWithString: @"http://host/foo//bar/baz"]
	    lastPathComponent] isEqual: @"baz"] &&
	    [[[OFURL URLWithString: @"http://host/foo//bar/baz/"]
	    lastPathComponent] isEqual: @"baz"] &&
	    [[[OFURL URLWithString: @"http://host/foo/"]
	    lastPathComponent] isEqual: @"foo"] &&
	    [[[OFURL URLWithString: @"http://host/"]
	    lastPathComponent] isEqual: @"/"] &&
	    [u5.lastPathComponent isEqual: @"foo/bar"])
	TEST(@"-[query]",
	    [u1.query isEqual: @"que#ry=1&f&oo=b=ar"] && u4.query == nil)
	TEST(@"-[queryDictionary]",
	    [u1.queryDictionary isEqual:
	    [OFDictionary dictionaryWithKeysAndObjects:
	    @"que#ry", @"1", @"f&oo", @"b=ar", nil]]);
	TEST(@"-[fragment]",
	    [u1.fragment isEqual: @"frag#ment"] && u4.fragment == nil)

	TEST(@"-[copy]", R(u4 = [[u1 copy] autorelease]))

	TEST(@"-[isEqual:]", [u1 isEqual: u4] && ![u2 isEqual: u3] &&
	    [[OFURL URLWithString: @"HTTP://bar/"] isEqual: u3])

	TEST(@"-[hash:]", u1.hash == u4.hash && u2.hash != u3.hash)

	EXPECT_EXCEPTION(@"Detection of invalid format",
	    OFInvalidFormatException, [OFURL URLWithString: @"http"])

	mu = [OFMutableURL URL];

	TEST(@"-[setScheme:]",

	    (mu.scheme = @"ht:tp") && [mu.URLEncodedScheme isEqual: @"ht%3Atp"])

	TEST(@"-[setURLEncodedScheme:]",
	    (mu.URLEncodedScheme = @"ht%3Atp") && [mu.scheme isEqual: @"ht:tp"])


	EXPECT_EXCEPTION(
	    @"-[setURLEncodedScheme:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedScheme = @"~")

	TEST(@"-[setHost:]",

	    (mu.host = @"ho:st") && [mu.URLEncodedHost isEqual: @"ho%3Ast"] &&
	    (mu.host = @"12:34:ab") &&
	    [mu.URLEncodedHost isEqual: @"[12:34:ab]"] &&
	    (mu.host = @"12:34:aB") &&
	    [mu.URLEncodedHost isEqual: @"[12:34:aB]"] &&
	    (mu.host = @"12:34:g") &&
	    [mu.URLEncodedHost isEqual: @"12%3A34%3Ag"])

	TEST(@"-[setURLEncodedHost:]",
	    (mu.URLEncodedHost = @"ho%3Ast") && [mu.host isEqual: @"ho:st"] &&

	    (mu.URLEncodedHost = @"[12:34]") && [mu.host isEqual: @"12:34"] &&

	    (mu.URLEncodedHost = @"[12::ab]") && [mu.host isEqual: @"12::ab"])


	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #1", OFInvalidFormatException, mu.URLEncodedHost = @"/")

	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #2", OFInvalidFormatException, mu.URLEncodedHost = @"[12:34")


	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #3", OFInvalidFormatException, mu.URLEncodedHost = @"[a::g]")


	TEST(@"-[setUser:]",

	    (mu.user = @"us:er") && [mu.URLEncodedUser isEqual: @"us%3Aer"])

	TEST(@"-[setURLEncodedUser:]",
	    (mu.URLEncodedUser = @"us%3Aer") && [mu.user isEqual: @"us:er"])


	EXPECT_EXCEPTION(@"-[setURLEncodedUser:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedHost = @"/")

	TEST(@"-[setPassword:]",
	    (mu.password = @"pass:word") &&
	    [mu.URLEncodedPassword isEqual: @"pass%3Aword"])

	TEST(@"-[setURLEncodedPassword:]",
	    (mu.URLEncodedPassword = @"pass%3Aword") &&
	    [mu.password isEqual: @"pass:word"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedPassword:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedPassword = @"/")

	TEST(@"-[setPath:]",

	    (mu.path = @"pa/th@?") && [mu.URLEncodedPath isEqual: @"pa/th@%3F"])

	TEST(@"-[setURLEncodedPath:]",
	    (mu.URLEncodedPath = @"pa/th@%3F") && [mu.path isEqual: @"pa/th@?"])


	EXPECT_EXCEPTION(@"-[setURLEncodedPath:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedPath = @"?")

	TEST(@"-[setQuery:]",
	    (mu.query = @"que/ry?#") &&
	    [mu.URLEncodedQuery isEqual: @"que/ry?%23"])

	TEST(@"-[setURLEncodedQuery:]",
	    (mu.URLEncodedQuery = @"que/ry?%23") &&
	    [mu.query isEqual: @"que/ry?#"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedQuery:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedQuery = @"`")

	TEST(@"-[setQueryDictionary:]",

	    (mu.queryDictionary = [OFDictionary dictionaryWithKeysAndObjects:
	    @"foo&bar", @"baz=qux", @"f=oobar", @"b&azqux", nil]) &&
	    [mu.URLEncodedQuery isEqual:
	    @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"])

	TEST(@"-[setFragment:]",
	    (mu.fragment = @"frag/ment?#") &&
	    [mu.URLEncodedFragment isEqual: @"frag/ment?%23"])

	TEST(@"-[setURLEncodedFragment:]",
	    (mu.URLEncodedFragment = @"frag/ment?%23") &&
	    [mu.fragment isEqual: @"frag/ment?#"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedFragment:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedFragment = @"`")

	TEST(@"-[URLByAppendingPathComponent:isDirectory:]",
	    [[[OFURL URLWithString: @"file:///foo/bar"]
	    URLByAppendingPathComponent: @"qux"
			    isDirectory: false] isEqual:
	    [OFURL URLWithString: @"file:///foo/bar/qux"]] &&
	    [[[OFURL URLWithString: @"file:///foo/bar/"]
	    URLByAppendingPathComponent: @"qux"
			    isDirectory: false] isEqual:
	    [OFURL URLWithString: @"file:///foo/bar/qux"]] &&
	    [[[OFURL URLWithString: @"file:///foo/bar/"]
	    URLByAppendingPathComponent: @"qu?x"
			    isDirectory: false] isEqual:
	    [OFURL URLWithString: @"file:///foo/bar/qu%3Fx"]] &&
	    [[[OFURL URLWithString: @"file:///foo/bar/"]
	    URLByAppendingPathComponent: @"qu?x"
			    isDirectory: true] isEqual:
	    [OFURL URLWithString: @"file:///foo/bar/qu%3Fx/"]])

	TEST(@"-[URLByStandardizingPath]",
	    [[[OFURL URLWithString: @"http://foo/bar/.."]
	    URLByStandardizingPath] isEqual:
	    [OFURL URLWithString: @"http://foo/"]] &&
	    [[[OFURL URLWithString: @"http://foo/bar/%2E%2E/../qux/"]







|
|
|
|


|

|

|
|
|
|
|
|

>
|

|

|

|










|

|

|



|

|

|
|

|




|


>
|


|
>



|


>
|
|
|
|
|
|
|


|
>
|
>
|
>


|


|
>


|
>


>
|


|
>


|


|
|


|
|



|


>
|


|
>


|


|
|


|
|



|


>
|

|



|
|


|
|



|



|
<


|
<


|
<


|
<







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
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
	    [tmp.host isEqual: @"test"] && [tmp.path isEqual: @"/"] &&
	    [tmp.string isEqual: @"file://test/"] &&
	    [tmp.fileSystemRepresentation isEqual: @"\\\\test"])
# endif
#endif

	TEST(@"-[string]",
	    [URL1.string isEqual: URLString] &&
	    [URL2.string isEqual: @"http://foo:80"] &&
	    [URL3.string isEqual: @"http://bar/"] &&
	    [URL4.string isEqual: @"file:///etc/passwd"])

	TEST(@"-[scheme]",
	    [URL1.scheme isEqual: @"ht:tp"] && [URL4.scheme isEqual: @"file"])

	TEST(@"-[user]", [URL1.user isEqual: @"us:er"] && URL4.user == nil)
	TEST(@"-[password]",
	    [URL1.password isEqual: @"p@w"] && URL4.password == nil)
	TEST(@"-[host]", [URL1.host isEqual: @"ho:st"] &&
	    [URL6.host isEqual: @"12:34::56:abcd"] &&
	    [URL7.host isEqual: @"12:34::56:abcd"])
	TEST(@"-[port]", URL1.port.unsignedShortValue == 1234 &&
	    [URL4 port] == nil && URL7.port.unsignedShortValue == 234)
	TEST(@"-[path]",
	    [URL1.path isEqual: @"/pa?th"] &&
	    [URL4.path isEqual: @"/etc/passwd"])
	TEST(@"-[pathComponents]",
	    [URL1.pathComponents isEqual:
	    [OFArray arrayWithObjects: @"/", @"pa?th", nil]] &&
	    [URL4.pathComponents isEqual:
	    [OFArray arrayWithObjects: @"/", @"etc", @"passwd", nil]] &&
	    [URL5.pathComponents isEqual:
	    [OFArray arrayWithObjects: @"/", @"bar", @"qux", @"foo/bar", nil]])
	TEST(@"-[lastPathComponent]",
	    [[[OFURL URLWithString: @"http://host/foo//bar/baz"]
	    lastPathComponent] isEqual: @"baz"] &&
	    [[[OFURL URLWithString: @"http://host/foo//bar/baz/"]
	    lastPathComponent] isEqual: @"baz"] &&
	    [[[OFURL URLWithString: @"http://host/foo/"]
	    lastPathComponent] isEqual: @"foo"] &&
	    [[[OFURL URLWithString: @"http://host/"]
	    lastPathComponent] isEqual: @"/"] &&
	    [URL5.lastPathComponent isEqual: @"foo/bar"])
	TEST(@"-[query]",
	    [URL1.query isEqual: @"que#ry=1&f&oo=b=ar"] && URL4.query == nil)
	TEST(@"-[queryDictionary]",
	    [URL1.queryDictionary isEqual:
	    [OFDictionary dictionaryWithKeysAndObjects:
	    @"que#ry", @"1", @"f&oo", @"b=ar", nil]]);
	TEST(@"-[fragment]",
	    [URL1.fragment isEqual: @"frag#ment"] && URL4.fragment == nil)

	TEST(@"-[copy]", R(URL4 = [[URL1 copy] autorelease]))

	TEST(@"-[isEqual:]", [URL1 isEqual: URL4] && ![URL2 isEqual: URL3] &&
	    [[OFURL URLWithString: @"HTTP://bar/"] isEqual: URL3])

	TEST(@"-[hash:]", URL1.hash == URL4.hash && URL2.hash != URL3.hash)

	EXPECT_EXCEPTION(@"Detection of invalid format",
	    OFInvalidFormatException, [OFURL URLWithString: @"http"])

	mutableURL = [OFMutableURL URL];

	TEST(@"-[setScheme:]",
	    (mutableURL.scheme = @"ht:tp") &&
	    [mutableURL.URLEncodedScheme isEqual: @"ht%3Atp"])

	TEST(@"-[setURLEncodedScheme:]",
	    (mutableURL.URLEncodedScheme = @"ht%3Atp") &&
	    [mutableURL.scheme isEqual: @"ht:tp"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedScheme:] with invalid characters fails",
	    OFInvalidFormatException, mutableURL.URLEncodedScheme = @"~")

	TEST(@"-[setHost:]",
	    (mutableURL.host = @"ho:st") &&
	    [mutableURL.URLEncodedHost isEqual: @"ho%3Ast"] &&
	    (mutableURL.host = @"12:34:ab") &&
	    [mutableURL.URLEncodedHost isEqual: @"[12:34:ab]"] &&
	    (mutableURL.host = @"12:34:aB") &&
	    [mutableURL.URLEncodedHost isEqual: @"[12:34:aB]"] &&
	    (mutableURL.host = @"12:34:g") &&
	    [mutableURL.URLEncodedHost isEqual: @"12%3A34%3Ag"])

	TEST(@"-[setURLEncodedHost:]",
	    (mutableURL.URLEncodedHost = @"ho%3Ast") &&
	    [mutableURL.host isEqual: @"ho:st"] &&
	    (mutableURL.URLEncodedHost = @"[12:34]") &&
	    [mutableURL.host isEqual: @"12:34"] &&
	    (mutableURL.URLEncodedHost = @"[12::ab]") &&
	    [mutableURL.host isEqual: @"12::ab"])

	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #1", OFInvalidFormatException, mutableURL.URLEncodedHost = @"/")

	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #2", OFInvalidFormatException,
	    mutableURL.URLEncodedHost = @"[12:34")

	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #3", OFInvalidFormatException,
	    mutableURL.URLEncodedHost = @"[a::g]")

	TEST(@"-[setUser:]",
	    (mutableURL.user = @"us:er") &&
	    [mutableURL.URLEncodedUser isEqual: @"us%3Aer"])

	TEST(@"-[setURLEncodedUser:]",
	    (mutableURL.URLEncodedUser = @"us%3Aer") &&
	    [mutableURL.user isEqual: @"us:er"])

	EXPECT_EXCEPTION(@"-[setURLEncodedUser:] with invalid characters fails",
	    OFInvalidFormatException, mutableURL.URLEncodedHost = @"/")

	TEST(@"-[setPassword:]",
	    (mutableURL.password = @"pass:word") &&
	    [mutableURL.URLEncodedPassword isEqual: @"pass%3Aword"])

	TEST(@"-[setURLEncodedPassword:]",
	    (mutableURL.URLEncodedPassword = @"pass%3Aword") &&
	    [mutableURL.password isEqual: @"pass:word"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedPassword:] with invalid characters fails",
	    OFInvalidFormatException, mutableURL.URLEncodedPassword = @"/")

	TEST(@"-[setPath:]",
	    (mutableURL.path = @"pa/th@?") &&
	    [mutableURL.URLEncodedPath isEqual: @"pa/th@%3F"])

	TEST(@"-[setURLEncodedPath:]",
	    (mutableURL.URLEncodedPath = @"pa/th@%3F") &&
	    [mutableURL.path isEqual: @"pa/th@?"])

	EXPECT_EXCEPTION(@"-[setURLEncodedPath:] with invalid characters fails",
	    OFInvalidFormatException, mutableURL.URLEncodedPath = @"?")

	TEST(@"-[setQuery:]",
	    (mutableURL.query = @"que/ry?#") &&
	    [mutableURL.URLEncodedQuery isEqual: @"que/ry?%23"])

	TEST(@"-[setURLEncodedQuery:]",
	    (mutableURL.URLEncodedQuery = @"que/ry?%23") &&
	    [mutableURL.query isEqual: @"que/ry?#"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedQuery:] with invalid characters fails",
	    OFInvalidFormatException, mutableURL.URLEncodedQuery = @"`")

	TEST(@"-[setQueryDictionary:]",
	    (mutableURL.queryDictionary =
	    [OFDictionary dictionaryWithKeysAndObjects:
	    @"foo&bar", @"baz=qux", @"f=oobar", @"b&azqux", nil]) &&
	    [mutableURL.URLEncodedQuery isEqual:
	    @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"])

	TEST(@"-[setFragment:]",
	    (mutableURL.fragment = @"frag/ment?#") &&
	    [mutableURL.URLEncodedFragment isEqual: @"frag/ment?%23"])

	TEST(@"-[setURLEncodedFragment:]",
	    (mutableURL.URLEncodedFragment = @"frag/ment?%23") &&
	    [mutableURL.fragment isEqual: @"frag/ment?#"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedFragment:] with invalid characters fails",
	    OFInvalidFormatException, mutableURL.URLEncodedFragment = @"`")

	TEST(@"-[URLByAppendingPathComponent:isDirectory:]",
	    [[[OFURL URLWithString: @"file:///foo/bar"]
	    URLByAppendingPathComponent: @"qux" isDirectory: false] isEqual:

	    [OFURL URLWithString: @"file:///foo/bar/qux"]] &&
	    [[[OFURL URLWithString: @"file:///foo/bar/"]
	    URLByAppendingPathComponent: @"qux" isDirectory: false] isEqual:

	    [OFURL URLWithString: @"file:///foo/bar/qux"]] &&
	    [[[OFURL URLWithString: @"file:///foo/bar/"]
	    URLByAppendingPathComponent: @"qu?x" isDirectory: false] isEqual:

	    [OFURL URLWithString: @"file:///foo/bar/qu%3Fx"]] &&
	    [[[OFURL URLWithString: @"file:///foo/bar/"]
	    URLByAppendingPathComponent: @"qu?x" isDirectory: true] isEqual:

	    [OFURL URLWithString: @"file:///foo/bar/qu%3Fx/"]])

	TEST(@"-[URLByStandardizingPath]",
	    [[[OFURL URLWithString: @"http://foo/bar/.."]
	    URLByStandardizingPath] isEqual:
	    [OFURL URLWithString: @"http://foo/"]] &&
	    [[[OFURL URLWithString: @"http://foo/bar/%2E%2E/../qux/"]

Modified tests/OFValueTests.m from [d8e22a71e7] to [545d0b4ed0].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFValue";

@implementation TestsAppDelegate (OFValueTests)
- (void)valueTests
{
	void *pool = objc_autoreleasePoolPush();
	of_range_t range = of_range(1, 64), range2;
	of_point_t point = of_point(1.5f, 3.0f), point2;
	of_dimension_t dimension = of_dimension(4.5f, 5.0f), dimension2;
	of_rectangle_t rectangle = of_rectangle(1.5f, 3.0f, 4.5f, 6.0f);
	of_rectangle_t rectangle2;
	OFValue *value;
	void *pointer = &value;

	TEST(@"+[valueWithBytes:objCType:]",
	    (value = [OFValue valueWithBytes: &range
				    objCType: @encode(of_range_t)]))

	TEST(@"-[objCType]", strcmp(value.objCType, @encode(of_range_t)) == 0)

	TEST(@"-[getValue:size:]",
	    R([value getValue: &range2
			 size: sizeof(of_range_t)]) &&
	    of_range_equal(range2, range))

	EXPECT_EXCEPTION(@"-[getValue:size:] with wrong size throws",
	    OFOutOfRangeException,
	    [value getValue: &range
		       size: sizeof(of_range_t) - 1])

	TEST(@"+[valueWithPointer:]",
	    (value = [OFValue valueWithPointer: pointer]))

	TEST(@"-[pointerValue]",
	    value.pointerValue == pointer &&
	    [[OFValue valueWithBytes: &pointer

<
<
|



















|





|
|
|
|
<





|

|


|
<
|



|
<







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
/*


 * 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 <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFValue";

@implementation TestsAppDelegate (OFValueTests)
- (void)valueTests
{
	void *pool = objc_autoreleasePoolPush();
	OFRange range = OFRangeMake(1, 64), range2;
	OFPoint point = OFPointMake(1.5f, 3.0f), point2;
	OFSize size = OFSizeMake(4.5f, 5.0f), size2;
	OFRect rect = OFRectMake(1.5f, 3.0f, 4.5f, 6.0f), rect2;

	OFValue *value;
	void *pointer = &value;

	TEST(@"+[valueWithBytes:objCType:]",
	    (value = [OFValue valueWithBytes: &range
				    objCType: @encode(OFRange)]))

	TEST(@"-[objCType]", strcmp(value.objCType, @encode(OFRange)) == 0)

	TEST(@"-[getValue:size:]",
	    R([value getValue: &range2 size: sizeof(OFRange)]) &&

	    OFRangeEqual(range2, range))

	EXPECT_EXCEPTION(@"-[getValue:size:] with wrong size throws",
	    OFOutOfRangeException,
	    [value getValue: &range size: sizeof(OFRange) - 1])


	TEST(@"+[valueWithPointer:]",
	    (value = [OFValue valueWithPointer: pointer]))

	TEST(@"-[pointerValue]",
	    value.pointerValue == pointer &&
	    [[OFValue valueWithBytes: &pointer
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
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] nonretainedObjectValue])

	TEST(@"+[valueWithRange:]",
	    (value = [OFValue valueWithRange: range]))

	TEST(@"-[rangeValue]",
	    of_range_equal(value.rangeValue, range) &&
	    (value = [OFValue valueWithBytes: &range
				    objCType: @encode(of_range_t)]) &&
	    of_range_equal(value.rangeValue, range))

	TEST(@"-[getValue:size:] for OFRangeValue",
	    (value = [OFValue valueWithRange: range]) &&
	    R([value getValue: &range2
			 size: sizeof(range2)]) &&
	    of_range_equal(range2, range))

	EXPECT_EXCEPTION(@"-[rangeValue] with wrong size throws",
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] rangeValue])

	TEST(@"+[valueWithPoint:]",
	    (value = [OFValue valueWithPoint: point]))

	TEST(@"-[pointValue]",
	    of_point_equal(value.pointValue, point) &&
	    (value = [OFValue valueWithBytes: &point
				    objCType: @encode(of_point_t)]) &&
	    of_point_equal(value.pointValue, point))

	TEST(@"-[getValue:size:] for OFPointValue",
	    (value = [OFValue valueWithPoint: point]) &&
	    R([value getValue: &point2
			 size: sizeof(point2)]) &&
	    of_point_equal(point2, point))

	EXPECT_EXCEPTION(@"-[pointValue] with wrong size throws",
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] pointValue])

	TEST(@"+[valueWithDimension:]",
	    (value = [OFValue valueWithDimension: dimension]))

	TEST(@"-[dimensionValue]",
	    of_dimension_equal(value.dimensionValue, dimension) &&
	    (value = [OFValue valueWithBytes: &dimension
				    objCType: @encode(of_dimension_t)]) &&
	    of_dimension_equal(value.dimensionValue, dimension))

	TEST(@"-[getValue:size:] for OFDimensionValue",
	    (value = [OFValue valueWithDimension: dimension]) &&
	    R([value getValue: &dimension2
			 size: sizeof(dimension2)]) &&
	    of_dimension_equal(dimension2, dimension))

	EXPECT_EXCEPTION(@"-[dimensionValue] with wrong size throws",
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] dimensionValue])

	TEST(@"+[valueWithRectangle:]",
	    (value = [OFValue valueWithRectangle: rectangle]))

	TEST(@"-[rectangleValue]",
	    of_rectangle_equal(value.rectangleValue, rectangle) &&
	    (value = [OFValue valueWithBytes: &rectangle
				    objCType: @encode(of_rectangle_t)]) &&
	    of_rectangle_equal(value.rectangleValue, rectangle))

	TEST(@"-[getValue:size:] for OFRectangleValue",
	    (value = [OFValue valueWithRectangle: rectangle]) &&
	    R([value getValue: &rectangle2
			 size: sizeof(rectangle2)]) &&
	    of_rectangle_equal(rectangle2, rectangle))

	EXPECT_EXCEPTION(@"-[rectangleValue] with wrong size throws",
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] rectangleValue])

	TEST(@"-[isEqual:]",
	    [[OFValue valueWithRectangle: rectangle]
	    isEqual: [OFValue valueWithBytes: &rectangle
				    objCType: @encode(of_rectangle_t)]] &&
	    ![[OFValue valueWithBytes: "a"
			     objCType: @encode(signed char)]
	    isEqual: [OFValue valueWithBytes: "a"
				    objCType: @encode(unsigned char)]] &&
	    ![[OFValue valueWithBytes: "a"
			     objCType: @encode(char)]
	    isEqual: [OFValue valueWithBytes: "b"
				    objCType: @encode(char)]])

	objc_autoreleasePoolPop(pool);
}
@end







|

|
|



|
<
|










|

|
|



|
<
|






|
|

|
|
|
|
|

|
|
|
|
<

|


|

|
|

|
|
|
|
|

|
|
|
<
|

|

|
<


|
|
|
|
<


|
<
|
<




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
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] nonretainedObjectValue])

	TEST(@"+[valueWithRange:]",
	    (value = [OFValue valueWithRange: range]))

	TEST(@"-[rangeValue]",
	    OFRangeEqual(value.rangeValue, range) &&
	    (value = [OFValue valueWithBytes: &range
				    objCType: @encode(OFRange)]) &&
	    OFRangeEqual(value.rangeValue, range))

	TEST(@"-[getValue:size:] for OFRangeValue",
	    (value = [OFValue valueWithRange: range]) &&
	    R([value getValue: &range2 size: sizeof(range2)]) &&

	    OFRangeEqual(range2, range))

	EXPECT_EXCEPTION(@"-[rangeValue] with wrong size throws",
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] rangeValue])

	TEST(@"+[valueWithPoint:]",
	    (value = [OFValue valueWithPoint: point]))

	TEST(@"-[pointValue]",
	    OFPointEqual(value.pointValue, point) &&
	    (value = [OFValue valueWithBytes: &point
				    objCType: @encode(OFPoint)]) &&
	    OFPointEqual(value.pointValue, point))

	TEST(@"-[getValue:size:] for OFPointValue",
	    (value = [OFValue valueWithPoint: point]) &&
	    R([value getValue: &point2 size: sizeof(point2)]) &&

	    OFPointEqual(point2, point))

	EXPECT_EXCEPTION(@"-[pointValue] with wrong size throws",
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] pointValue])

	TEST(@"+[valueWithSize:]",
	    (value = [OFValue valueWithSize: size]))

	TEST(@"-[sizeValue]",
	    OFSizeEqual(value.sizeValue, size) &&
	    (value = [OFValue valueWithBytes: &size
				    objCType: @encode(OFSize)]) &&
	    OFSizeEqual(value.sizeValue, size))

	TEST(@"-[getValue:size:] for OFSizeValue",
	    (value = [OFValue valueWithSize: size]) &&
	    R([value getValue: &size2 size: sizeof(size2)]) &&
	    OFSizeEqual(size2, size))


	EXPECT_EXCEPTION(@"-[sizeValue] with wrong size throws",
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a"
			    objCType: @encode(char)] sizeValue])

	TEST(@"+[valueWithRect:]",
	    (value = [OFValue valueWithRect: rect]))

	TEST(@"-[rectValue]",
	    OFRectEqual(value.rectValue, rect) &&
	    (value = [OFValue valueWithBytes: &rect
				    objCType: @encode(OFRect)]) &&
	    OFRectEqual(value.rectValue, rect))

	TEST(@"-[getValue:size:] for OFRectValue",
	    (value = [OFValue valueWithRect: rect]) &&
	    R([value getValue: &rect2 size: sizeof(rect2)]) &&

	    OFRectEqual(rect2, rect))

	EXPECT_EXCEPTION(@"-[rectValue] with wrong size throws",
	    OFOutOfRangeException,
	    [[OFValue valueWithBytes: "a" objCType: @encode(char)] rectValue])


	TEST(@"-[isEqual:]",
	    [[OFValue valueWithRect: rect]
	    isEqual: [OFValue valueWithBytes: &rect
				    objCType: @encode(OFRect)]] &&
	    ![[OFValue valueWithBytes: "a" objCType: @encode(signed char)]

	    isEqual: [OFValue valueWithBytes: "a"
				    objCType: @encode(unsigned char)]] &&
	    ![[OFValue valueWithBytes: "a" objCType: @encode(char)]

	    isEqual: [OFValue valueWithBytes: "b" objCType: @encode(char)]])


	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFWindowsRegistryKeyTests.m from [814a5cca52] to [60470d17f4].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFWindowsRegistryKey";

@implementation TestsAppDelegate (OFWindowsRegistryKeyTests)
- (void)windowsRegistryKeyTests
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data = [OFData dataWithItems: "abcdef"
				       count: 6];
	OFWindowsRegistryKey *softwareKey, *ObjFWKey;
	DWORD type;

	TEST(@"+[OFWindowsRegistryKey classesRootKey]",
	    [OFWindowsRegistryKey classesRootKey])

	TEST(@"+[OFWindowsRegistryKey currentConfigKey]",
	    [OFWindowsRegistryKey currentConfigKey])

	TEST(@"+[OFWindowsRegistryKey currentUserKey]",
	    [OFWindowsRegistryKey currentUserKey])

	TEST(@"+[OFWindowsRegistryKey localMachineKey]",
	    [OFWindowsRegistryKey localMachineKey])

	TEST(@"+[OFWindowsRegistryKey usersKey]",
	    [OFWindowsRegistryKey usersKey])

	TEST(@"-[openSubkeyAtPath:securityAndAccessRights:]",
	    (softwareKey = [[OFWindowsRegistryKey currentUserKey]
		   openSubkeyAtPath: @"Software"
	    securityAndAccessRights: KEY_ALL_ACCESS]) &&



	    [[OFWindowsRegistryKey currentUserKey]
		   openSubkeyAtPath: @"nonexistent"
	    securityAndAccessRights: KEY_ALL_ACCESS] == nil)

	TEST(@"-[createSubkeyAtPath:securityAndAccessRights:]",
	    (ObjFWKey = [softwareKey createSubkeyAtPath: @"ObjFW"
				securityAndAccessRights: KEY_ALL_ACCESS]))

	TEST(@"-[setData:forValue:type:]",
	    R([ObjFWKey setData: data
		       forValue: @"data"
			   type: REG_BINARY]))

	TEST(@"-[dataForValue:subkeyPath:flags:type:]",
	    [[ObjFWKey dataForValue: @"data"
			       type: &type] isEqual: data] &&
	    type == REG_BINARY)

	TEST(@"-[setString:forValue:type:]",
	    R([ObjFWKey setString: @"foobar"
			 forValue: @"string"]) &&
	    R([ObjFWKey setString: @"%PATH%;foo"
			 forValue: @"expand"
			     type: REG_EXPAND_SZ]))

	TEST(@"-[stringForValue:subkeyPath:]",
	    [[ObjFWKey stringForValue: @"string"] isEqual: @"foobar"] &&
	    [[ObjFWKey stringForValue: @"expand"
				 type: &type] isEqual: @"%PATH%;foo"] &&
	    type == REG_EXPAND_SZ)

	TEST(@"-[deleteValue:]", R([ObjFWKey deleteValue: @"data"]))

	TEST(@"-[deleteSubkeyAtPath:]",
	    R([softwareKey deleteSubkeyAtPath: @"ObjFW"]))

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|





|
<
|

















|


|
>
>
>


|


|


|
|
<
<

|
<
|


|
|
<
|
|



|
|
|


|







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFWindowsRegistryKey";

@implementation TestsAppDelegate (OFWindowsRegistryKeyTests)
- (void)windowsRegistryKeyTests
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data = [OFData dataWithItems: "abcdef" count: 6];

	OFWindowsRegistryKey *softwareKey, *objFWKey;
	DWORD type;

	TEST(@"+[OFWindowsRegistryKey classesRootKey]",
	    [OFWindowsRegistryKey classesRootKey])

	TEST(@"+[OFWindowsRegistryKey currentConfigKey]",
	    [OFWindowsRegistryKey currentConfigKey])

	TEST(@"+[OFWindowsRegistryKey currentUserKey]",
	    [OFWindowsRegistryKey currentUserKey])

	TEST(@"+[OFWindowsRegistryKey localMachineKey]",
	    [OFWindowsRegistryKey localMachineKey])

	TEST(@"+[OFWindowsRegistryKey usersKey]",
	    [OFWindowsRegistryKey usersKey])

	TEST(@"-[openSubkeyAtPath:securityAndAccessRights:] #1",
	    (softwareKey = [[OFWindowsRegistryKey currentUserKey]
		   openSubkeyAtPath: @"Software"
	    securityAndAccessRights: KEY_ALL_ACCESS]))

	EXPECT_EXCEPTION(@"-[openSubkeyAtPath:securityAndAccessRights:] #2",
	    OFOpenWindowsRegistryKeyFailedException,
	    [[OFWindowsRegistryKey currentUserKey]
		   openSubkeyAtPath: @"nonexistent"
	    securityAndAccessRights: KEY_ALL_ACCESS])

	TEST(@"-[createSubkeyAtPath:securityAndAccessRights:]",
	    (objFWKey = [softwareKey createSubkeyAtPath: @"ObjFW"
				securityAndAccessRights: KEY_ALL_ACCESS]))

	TEST(@"-[setData:forValueNamed:type:]",
	    R([objFWKey setData: data forValueNamed: @"data" type: REG_BINARY]))



	TEST(@"-[dataForValueNamed:subkeyPath:flags:type:]",

	    [[objFWKey dataForValueNamed: @"data" type: &type] isEqual: data] &&
	    type == REG_BINARY)

	TEST(@"-[setString:forValueNamed:type:]",
	    R([objFWKey setString: @"foobar" forValueNamed: @"string"]) &&

	    R([objFWKey setString: @"%PATH%;foo"
		    forValueNamed: @"expand"
			     type: REG_EXPAND_SZ]))

	TEST(@"-[stringForValue:subkeyPath:]",
	    [[objFWKey stringForValueNamed: @"string"] isEqual: @"foobar"] &&
	    [[objFWKey stringForValueNamed: @"expand" type: &type]
	    isEqual: @"%PATH%;foo"] &&
	    type == REG_EXPAND_SZ)

	TEST(@"-[deleteValueNamed:]", R([objFWKey deleteValueNamed: @"data"]))

	TEST(@"-[deleteSubkeyAtPath:]",
	    R([softwareKey deleteSubkeyAtPath: @"ObjFW"]))

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFXMLElementBuilderTests.m from [5c6f3fc1cf] to [3226c89147].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFXMLElementBuilder";
static OFXMLNode *nodes[2];
static size_t i = 0;

@implementation TestsAppDelegate (OFXMLElementBuilderTests)
- (void)elementBuilder: (OFXMLElementBuilder *)builder
       didBuildElement: (OFXMLElement *)element
{
	OF_ENSURE(i == 0);
	nodes[i++] = [element retain];
}

-   (void)elementBuilder: (OFXMLElementBuilder *)builder
  didBuildParentlessNode: (OFXMLNode *)node
{
	OF_ENSURE(i == 1);
	nodes[i++] = [node retain];
}

- (void)XMLElementBuilderTests
{
	void *pool = objc_autoreleasePoolPush();
	OFXMLParser *p = [OFXMLParser parser];
	OFXMLElementBuilder *builder = [OFXMLElementBuilder elementBuilder];
	OFString *str = @"<foo>bar<![CDATA[f<oo]]>baz<qux/>"
	    " <qux xmlns:qux='urn:qux'><?asd?><qux:bar/><x qux:y='z'/></qux>"
	    "</foo>";

	p.delegate = builder;
	builder.delegate = self;

	TEST(@"Building elements from parsed XML",
	    R([p parseString: str]) &&
	    nodes[0] != nil && [nodes[0].XMLString isEqual: str] &&
	    R([p parseString: @"<!--foo-->"]) &&
	    nodes[1] != nil && [nodes[1].XMLString isEqual: @"<!--foo-->"] &&
	    i == 2)

	[nodes[0] release];
	[nodes[1] release];
	objc_autoreleasePoolPop(pool);
}
@end

<
<
|

















|







|






|






|
|
|



|



|
|
|








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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"OFXMLElementBuilder";
static OFXMLNode *nodes[2];
static size_t i = 0;

@implementation TestsAppDelegate (OFXMLElementBuilderTests)
- (void)elementBuilder: (OFXMLElementBuilder *)builder
       didBuildElement: (OFXMLElement *)element
{
	OFEnsure(i == 0);
	nodes[i++] = [element retain];
}

-   (void)elementBuilder: (OFXMLElementBuilder *)builder
  didBuildParentlessNode: (OFXMLNode *)node
{
	OFEnsure(i == 1);
	nodes[i++] = [node retain];
}

- (void)XMLElementBuilderTests
{
	void *pool = objc_autoreleasePoolPush();
	OFXMLParser *parser = [OFXMLParser parser];
	OFXMLElementBuilder *builder = [OFXMLElementBuilder builder];
	OFString *string = @"<foo>bar<![CDATA[f<oo]]>baz<qux/>"
	    " <qux xmlns:qux='urn:qux'><?asd?><qux:bar/><x qux:y='z'/></qux>"
	    "</foo>";

	parser.delegate = builder;
	builder.delegate = self;

	TEST(@"Building elements from parsed XML",
	    R([parser parseString: string]) &&
	    nodes[0] != nil && [nodes[0].XMLString isEqual: string] &&
	    R([parser parseString: @"<!--foo-->"]) &&
	    nodes[1] != nil && [nodes[1].XMLString isEqual: @"<!--foo-->"] &&
	    i == 2)

	[nodes[0] release];
	[nodes[1] release];
	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFXMLNodeTests.m from [ec444519ac] to [29eb2048b3].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"OFXMLNode";

@implementation TestsAppDelegate (OFXMLNodeTests)
- (void)XMLNodeTests
{
	void *pool = objc_autoreleasePoolPush();
	id nodes[4];
	OFArray *a;



	TEST(@"+[elementWithName:]",
	    (nodes[0] = [OFXMLElement elementWithName: @"foo"]) &&
	    [[nodes[0] XMLString] isEqual: @"<foo/>"])

	TEST(@"+[elementWithName:stringValue:]",
	    (nodes[1] = [OFXMLElement elementWithName: @"foo"
					  stringValue: @"b&ar"]) &&
	    [[nodes[1] XMLString] isEqual: @"<foo>b&amp;ar</foo>"])

	TEST(@"+[elementWithName:namespace:]",
	    (nodes[2] = [OFXMLElement elementWithName: @"foo"
					    namespace: @"urn:objfw:test"]) &&
	    R([nodes[2] addAttributeWithName: @"test"
				 stringValue: @"test"]) &&
	    R([nodes[2] setPrefix: @"objfw-test"
		     forNamespace: @"urn:objfw:test"]) &&
	    [[nodes[2] XMLString] isEqual: @"<objfw-test:foo test='test'/>"] &&
	    (nodes[3] = [OFXMLElement elementWithName: @"foo"
					    namespace: @"urn:objfw:test"]) &&
	    R([nodes[3] addAttributeWithName: @"test"
				 stringValue: @"test"]) &&
	    [[nodes[3] XMLString] isEqual:
	    @"<foo xmlns='urn:objfw:test' test='test'/>"])

	TEST(@"+[elementWithName:namespace:stringValue:]",
	    (nodes[3] = [OFXMLElement elementWithName: @"foo"
					    namespace: @"urn:objfw:test"
					  stringValue: @"x"]) &&
	    R([nodes[3] setPrefix: @"objfw-test"
		     forNamespace: @"urn:objfw:test"]) &&
	    [[nodes[3] XMLString] isEqual:
	    @"<objfw-test:foo>x</objfw-test:foo>"])

	TEST(@"+[charactersWithString:]",
	    (nodes[3] = [OFXMLCharacters charactersWithString: @"<foo>"]) &&
	    [[nodes[3] XMLString] isEqual: @"&lt;foo&gt;"])

	TEST(@"+[CDATAWithString:]",
	    (nodes[3] = [OFXMLCDATA CDATAWithString: @"<foo>"]) &&
	    [[nodes[3] XMLString] isEqual: @"<![CDATA[<foo>]]>"]);

	TEST(@"+[commentWithString:]",
	    (nodes[3] = [OFXMLComment commentWithString: @" comment "]) &&
	    [[nodes[3] XMLString] isEqual: @"<!-- comment -->"])

	module = @"OFXMLElement";

	TEST(@"-[addAttributeWithName:stringValue:]",
	    R([nodes[0] addAttributeWithName: @"foo"
				 stringValue: @"b&ar"]) &&
	    [[nodes[0] XMLString] isEqual: @"<foo foo='b&amp;ar'/>"] &&
	    R([nodes[1] addAttributeWithName: @"foo"
				 stringValue: @"b&ar"]) &&
	    [[nodes[1] XMLString] isEqual:
	    @"<foo foo='b&amp;ar'>b&amp;ar</foo>"])

	TEST(@"-[setPrefix:forNamespace:]",
	    R([nodes[1] setPrefix: @"objfw-test"
		     forNamespace: @"urn:objfw:test"]))

	TEST(@"-[addAttributeWithName:namespace:stringValue:]",
	    R([nodes[1] addAttributeWithName: @"foo"
				   namespace: @"urn:objfw:test"
				 stringValue: @"bar"]) &&
	    R([nodes[1] addAttributeWithName: @"foo"
				   namespace: @"urn:objfw:test"
				 stringValue: @"ignored"]) &&
	    [[nodes[1] XMLString] isEqual:
	    @"<foo foo='b&amp;ar' objfw-test:foo='bar'>b&amp;ar</foo>"])

	TEST(@"-[removeAttributeForName:namespace:]",
	    R([nodes[1] removeAttributeForName: @"foo"]) &&
	    [[nodes[1] XMLString] isEqual:
	    @"<foo objfw-test:foo='bar'>b&amp;ar</foo>"] &&
	    R([nodes[1] removeAttributeForName: @"foo"
				     namespace: @"urn:objfw:test"]) &&
	    [[nodes[1] XMLString] isEqual: @"<foo>b&amp;ar</foo>"])

	TEST(@"-[addChild:]",
	    R([nodes[0] addChild: [OFXMLElement elementWithName: @"bar"]]) &&
	    [[nodes[0] XMLString] isEqual:
	    @"<foo foo='b&amp;ar'><bar/></foo>"] &&
	    R([nodes[2] addChild: [OFXMLElement elementWithName: @"bar"
		       namespace: @"urn:objfw:test"]]) &&
	    [[nodes[2] XMLString] isEqual:
	    @"<objfw-test:foo test='test'><objfw-test:bar/></objfw-test:foo>"])

	TEST(@"+[elementWithXMLString:] and -[stringValue]",
	    [[[OFXMLElement elementWithXMLString:
	    @"<?xml version='1.0' encoding='UTF-8'?>\r\n<x>foo<![CDATA[bar]]>"
	    @"<y>b<!-- fooo -->az</y>qux</x>"] stringValue]
	    isEqual: @"foobarbazqux"])

	TEST(@"-[elementsForName:namespace:]",
	    (a = [nodes[2] elementsForName: @"bar"
				 namespace: @"urn:objfw:test"]) &&
	    a.count == 1 && [[[a firstObject] XMLString] isEqual:
	    @"<bar xmlns='urn:objfw:test'/>"])

	TEST(@"-[isEqual:]",
	    [[OFXMLElement elementWithXMLString: @"<foo bar='asd'/>"] isEqual:
	    [OFXMLElement elementWithXMLString: @"<foo bar='asd'></foo>"]] &&
	    [[OFXMLElement elementWithXMLString: @"<x><y/></x>"] isEqual:
	    [OFXMLElement elementWithXMLString: @"<x><y></y></x>"]])

<
<
|

















|





|
|

>
>

|
|


|
|
|


|
|
|
<
|
|
|
|
|
|
<
|



|
|
|
|
|
<
|


|
|


|
|

|
|
|




|
<
|
|
<
<
|


|
|


|
|
|
|
|
|
|



|
|

|
|
|


|
|

|
|
|









|
|
|







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *module;

@implementation TestsAppDelegate (OFXMLNodeTests)
- (void)XMLNodeTests
{
	void *pool = objc_autoreleasePoolPush();
	id node1, node2, node3, node4;
	OFArray *array;

	module = @"OFXMLNode";

	TEST(@"+[elementWithName:]",
	    (node1 = [OFXMLElement elementWithName: @"foo"]) &&
	    [[node1 XMLString] isEqual: @"<foo/>"])

	TEST(@"+[elementWithName:stringValue:]",
	    (node2 = [OFXMLElement elementWithName: @"foo"
				       stringValue: @"b&ar"]) &&
	    [[node2 XMLString] isEqual: @"<foo>b&amp;ar</foo>"])

	TEST(@"+[elementWithName:namespace:]",
	    (node3 = [OFXMLElement elementWithName: @"foo"
					 namespace: @"urn:objfw:test"]) &&
	    R([node3 addAttributeWithName: @"test" stringValue: @"test"]) &&

	    R([node3 setPrefix: @"objfw-test"
		  forNamespace: @"urn:objfw:test"]) &&
	    [[node3 XMLString] isEqual: @"<objfw-test:foo test='test'/>"] &&
	    (node4 = [OFXMLElement elementWithName: @"foo"
					 namespace: @"urn:objfw:test"]) &&
	    R([node4 addAttributeWithName: @"test" stringValue: @"test"]) &&

	    [[node4 XMLString] isEqual:
	    @"<foo xmlns='urn:objfw:test' test='test'/>"])

	TEST(@"+[elementWithName:namespace:stringValue:]",
	    (node4 = [OFXMLElement elementWithName: @"foo"
					 namespace: @"urn:objfw:test"
				       stringValue: @"x"]) &&
	    R([node4 setPrefix: @"objfw-test"
		  forNamespace: @"urn:objfw:test"]) &&

	    [[node4 XMLString] isEqual: @"<objfw-test:foo>x</objfw-test:foo>"])

	TEST(@"+[charactersWithString:]",
	    (node4 = [OFXMLCharacters charactersWithString: @"<foo>"]) &&
	    [[node4 XMLString] isEqual: @"&lt;foo&gt;"])

	TEST(@"+[CDATAWithString:]",
	    (node4 = [OFXMLCDATA CDATAWithString: @"<foo>"]) &&
	    [[node4 XMLString] isEqual: @"<![CDATA[<foo>]]>"]);

	TEST(@"+[commentWithText:]",
	    (node4 = [OFXMLComment commentWithText: @" comment "]) &&
	    [[node4 XMLString] isEqual: @"<!-- comment -->"])

	module = @"OFXMLElement";

	TEST(@"-[addAttributeWithName:stringValue:]",
	    R([node1 addAttributeWithName: @"foo" stringValue: @"b&ar"]) &&

	    [[node1 XMLString] isEqual: @"<foo foo='b&amp;ar'/>"] &&
	    R([node2 addAttributeWithName: @"foo" stringValue: @"b&ar"]) &&


	    [[node2 XMLString] isEqual: @"<foo foo='b&amp;ar'>b&amp;ar</foo>"])

	TEST(@"-[setPrefix:forNamespace:]",
	    R([node2 setPrefix: @"objfw-test"
		  forNamespace: @"urn:objfw:test"]))

	TEST(@"-[addAttributeWithName:namespace:stringValue:]",
	    R([node2 addAttributeWithName: @"foo"
				namespace: @"urn:objfw:test"
			      stringValue: @"bar"]) &&
	    R([node2 addAttributeWithName: @"foo"
				namespace: @"urn:objfw:test"
			      stringValue: @"ignored"]) &&
	    [[node2 XMLString] isEqual:
	    @"<foo foo='b&amp;ar' objfw-test:foo='bar'>b&amp;ar</foo>"])

	TEST(@"-[removeAttributeForName:namespace:]",
	    R([node2 removeAttributeForName: @"foo"]) &&
	    [[node2 XMLString] isEqual:
	    @"<foo objfw-test:foo='bar'>b&amp;ar</foo>"] &&
	    R([node2 removeAttributeForName: @"foo"
				  namespace: @"urn:objfw:test"]) &&
	    [[node2 XMLString] isEqual: @"<foo>b&amp;ar</foo>"])

	TEST(@"-[addChild:]",
	    R([node1 addChild: [OFXMLElement elementWithName: @"bar"]]) &&
	    [[node1 XMLString] isEqual:
	    @"<foo foo='b&amp;ar'><bar/></foo>"] &&
	    R([node3 addChild: [OFXMLElement elementWithName: @"bar"
		    namespace: @"urn:objfw:test"]]) &&
	    [[node3 XMLString] isEqual:
	    @"<objfw-test:foo test='test'><objfw-test:bar/></objfw-test:foo>"])

	TEST(@"+[elementWithXMLString:] and -[stringValue]",
	    [[[OFXMLElement elementWithXMLString:
	    @"<?xml version='1.0' encoding='UTF-8'?>\r\n<x>foo<![CDATA[bar]]>"
	    @"<y>b<!-- fooo -->az</y>qux</x>"] stringValue]
	    isEqual: @"foobarbazqux"])

	TEST(@"-[elementsForName:namespace:]",
	    (array = [node3 elementsForName: @"bar"
				  namespace: @"urn:objfw:test"]) &&
	    array.count == 1 && [[array.firstObject XMLString] isEqual:
	    @"<bar xmlns='urn:objfw:test'/>"])

	TEST(@"-[isEqual:]",
	    [[OFXMLElement elementWithXMLString: @"<foo bar='asd'/>"] isEqual:
	    [OFXMLElement elementWithXMLString: @"<foo bar='asd'></foo>"]] &&
	    [[OFXMLElement elementWithXMLString: @"<x><y/></x>"] isEqual:
	    [OFXMLElement elementWithXMLString: @"<x><y></y></x>"]])

Modified tests/OFXMLParserTests.m from [032c647d7b] to [c00f2c66ff].

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
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 <stdlib.h>
#include <string.h>

#import "TestsAppDelegate.h"

static OFString *module = @"OFXMLParser";
static int i = 0;

enum event_type {
	PROCESSING_INSTRUCTIONS,
	TAG_OPEN,
	TAG_CLOSE,
	STRING,
	CDATA,
	COMMENT
};

@implementation TestsAppDelegate (OFXMLParser)
-   (void)parser: (OFXMLParser *)parser
  didCreateEvent: (enum event_type)type
	    name: (OFString *)name
	  prefix: (OFString *)prefix
       namespace: (OFString *)ns
      attributes: (OFArray *)attrs
	  string: (OFString *)string
{
	OFString *msg;

	i++;
	msg = [OFString stringWithFormat: @"Parsing part #%d", i];

	switch (i) {
	case 1:
		TEST(msg, type == PROCESSING_INSTRUCTIONS &&


		    [string isEqual: @"xml version='1.0'"])
		break;
	case 2:
		TEST(msg, type == PROCESSING_INSTRUCTIONS &&

		    [string isEqual: @"p?i"])
		break;
	case 3:

		TEST(msg, type == TAG_OPEN && [name isEqual: @"root"] &&
		    prefix == nil && ns == nil && attrs.count == 0)
		break;
	case 4:

		TEST(msg, type == STRING && [string isEqual: @"\n\n "])
		break;
	case 5:

		TEST(msg, type == CDATA && [string isEqual: @"f<]]]oo]"] &&
		    parser.lineNumber == 3)
		break;
	case 6:

		TEST(msg, type == TAG_OPEN && [name isEqual: @"bar"] &&
		    prefix == nil && ns == nil && attrs == nil)
		break;
	case 7:

		TEST(msg, type == TAG_CLOSE && [name isEqual: @"bar"] &&
		    prefix == nil && ns == nil && attrs == nil)
		break;
	case 8:

		TEST(msg, type == STRING && [string isEqual: @"\n "])
		break;
	case 9:

		TEST(msg, type == TAG_OPEN && [name isEqual: @"foobar"] &&

		    prefix == nil && [ns isEqual: @"urn:objfw:test:foobar"] &&
		    attrs.count == 1 &&
		    /* xmlns attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"xmlns"] &&
		    [[attrs objectAtIndex: 0] namespace] == nil &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:foobar"])
		break;
	case 10:

		TEST(msg, type == STRING && [string isEqual: @"\n  "])
		break;
	case 11:

		TEST(msg, type == TAG_OPEN && [name isEqual: @"qux"] &&

		    prefix == nil && [ns isEqual: @"urn:objfw:test:foobar"] &&
		    attrs.count == 1 &&
		    /* xmlns:foo attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"foo"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"http://www.w3.org/2000/xmlns/"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:foo"])
		break;
	case 12:

		TEST(msg, type == STRING && [string isEqual: @"\n   "])
		break;
	case 13:

		TEST(msg, type == TAG_OPEN && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"foo"] &&
		    [ns isEqual: @"urn:objfw:test:foo"] &&
		    attrs.count == 2 &&
		    /* foo:bla attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"bla"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"urn:objfw:test:foo"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual: @"bla"] &&
		    /* blafoo attr */
		    [[[attrs objectAtIndex: 1] name] isEqual: @"blafoo"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"foo"])
		break;
	case 14:

		TEST(msg, type == STRING && [string isEqual: @"\n    "])
		break;
	case 15:

		TEST(msg, type == TAG_OPEN && [name isEqual: @"blup"] &&

		    prefix == nil && [ns isEqual: @"urn:objfw:test:foobar"] &&
		    attrs.count == 2 &&
		    /* foo:qux attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"qux"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"urn:objfw:test:foo"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual: @"asd"] &&
		    /* quxqux attr */
		    [[[attrs objectAtIndex: 1] name] isEqual: @"quxqux"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"test"])
		break;
	case 16:

		TEST(msg, type == TAG_CLOSE && [name isEqual: @"blup"] &&

		    prefix == nil && [ns isEqual: @"urn:objfw:test:foobar"])
		break;
	case 17:

		TEST(msg, type == STRING && [string isEqual: @"\n    "])
		break;
	case 18:

		TEST(msg, type == TAG_OPEN && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"bla"] &&
		    [ns isEqual: @"urn:objfw:test:bla"] && attrs.count == 3 &&

		    /* xmlns:bla attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"bla"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"http://www.w3.org/2000/xmlns/"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:bla"] &&
		    /* qux attr */
		    [[[attrs objectAtIndex: 1] name] isEqual: @"qux"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"qux"] &&
		    /* bla:foo attr */
		    [[[attrs objectAtIndex: 2] name] isEqual: @"foo"] &&
		    [[[attrs objectAtIndex: 2] namespace] isEqual:
		    @"urn:objfw:test:bla"] &&
		    [[[attrs objectAtIndex: 2] stringValue] isEqual: @"blafoo"])
		break;
	case 19:

		TEST(msg, type == TAG_CLOSE && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"bla"] &&
		    [ns isEqual: @"urn:objfw:test:bla"])
		break;
	case 20:

		TEST(msg, type == STRING && [string isEqual: @"\n    "])
		break;
	case 21:

		TEST(msg, type == TAG_OPEN && [name isEqual: @"abc"] &&

		    prefix == nil && [ns isEqual: @"urn:objfw:test:abc"] &&
		    attrs.count == 3 &&
		    /* xmlns attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"xmlns"] &&
		    [[attrs objectAtIndex: 0] namespace] == nil &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:abc"] &&
		    /* abc attr */
		    [[[attrs objectAtIndex: 1] name] isEqual: @"abc"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"abc"] &&
		    /* foo:abc attr */
		    [[[attrs objectAtIndex: 2] name] isEqual: @"abc"] &&
		    [[[attrs objectAtIndex: 2] namespace] isEqual:
		    @"urn:objfw:test:foo"] &&
		    [[[attrs objectAtIndex: 2] stringValue] isEqual: @"abc"])
		break;
	case 22:

		TEST(msg, type == TAG_CLOSE && [name isEqual: @"abc"] &&
		    prefix == nil && [ns isEqual: @"urn:objfw:test:abc"])
		break;
	case 23:

		TEST(msg, type == STRING && [string isEqual: @"\n   "])
		break;
	case 24:

		TEST(msg, type == TAG_CLOSE && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"foo"] &&
		    [ns isEqual: @"urn:objfw:test:foo"])
		break;
	case 25:

		TEST(msg, type == STRING && [string isEqual: @"\n   "])
		break;
	case 26:

		TEST(msg, type == COMMENT && [string isEqual: @" commänt "])
		break;
	case 27:

		TEST(msg, type == STRING && [string isEqual: @"\n  "])
		break;
	case 28:

		TEST(msg, type == TAG_CLOSE && [name isEqual: @"qux"] &&

		    prefix == nil && [ns isEqual: @"urn:objfw:test:foobar"])
		break;
	case 29:

		TEST(msg, type == STRING && [string isEqual: @"\n "])
		break;
	case 30:

		TEST(msg, type == TAG_CLOSE && [name isEqual: @"foobar"] &&

		    prefix == nil && [ns isEqual: @"urn:objfw:test:foobar"])
		break;
	case 31:

		TEST(msg, type == STRING && [string isEqual: @"\n"])
		break;
	case 32:

		TEST(msg, type == TAG_CLOSE && [name isEqual: @"root"] &&
		    prefix == nil && ns == nil);
		break;
	}
}

-		 (void)parser: (OFXMLParser *)parser
  foundProcessingInstructions: (OFString *)pi

{
	[self	    parser: parser
	    didCreateEvent: PROCESSING_INSTRUCTIONS
		      name: nil
		    prefix: nil
		 namespace: nil
		attributes: nil
		    string: pi];
}

-    (void)parser: (OFXMLParser *)parser
  didStartElement: (OFString *)name
	   prefix: (OFString *)prefix
	namespace: (OFString *)ns
       attributes: (OFArray *)attrs
{
	[self	    parser: parser
	    didCreateEvent: TAG_OPEN
		      name: name
		    prefix: prefix
		 namespace: ns
		attributes: attrs
		    string: nil];
}

-  (void)parser: (OFXMLParser *)parser
  didEndElement: (OFString *)name
	 prefix: (OFString *)prefix
      namespace: (OFString *)ns
{
	[self	    parser: parser
	    didCreateEvent: TAG_CLOSE
		      name: name
		    prefix: prefix
		 namespace: ns
		attributes: nil
		    string: nil];
}

-    (void)parser: (OFXMLParser *)parser
  foundCharacters: (OFString *)string
{
	[self	    parser: parser
	    didCreateEvent: STRING
		      name: nil
		    prefix: nil
		 namespace: nil
		attributes: nil
		    string: string];
}

- (void)parser: (OFXMLParser *)parser
    foundCDATA: (OFString *)cdata
{
	[self	    parser: parser
	    didCreateEvent: CDATA
		      name: nil
		    prefix: nil
		 namespace: nil
		attributes: nil
		    string: cdata];
}

- (void)parser: (OFXMLParser *)parser
  foundComment: (OFString *)comment
{
	[self	    parser: parser
	    didCreateEvent: COMMENT
		      name: nil
		    prefix: nil
		 namespace: nil
		attributes: nil
		    string: comment];
}

-      (OFString *)parser: (OFXMLParser *)parser
  foundUnknownEntityNamed: (OFString *)entity
{
	if ([entity isEqual: @"foo"])
		return @"foobar";

	return nil;
}

- (void)XMLParserTests
{
	void *pool = objc_autoreleasePoolPush();
	const char *str = "\xEF\xBB\xBF<?xml version='1.0'?><?p?i?>"
	    "<!DOCTYPE foo><root>\r\r"
	    " <![CDATA[f<]]]oo]]]><bar/>\n"
	    " <foobar xmlns='urn:objfw:test:foobar'>\r\n"
	    "  <qux xmlns:foo='urn:objfw:test:foo'>\n"
	    "   <foo:bla foo:bla = '&#x62;&#x6c;&#x61;' blafoo='foo'>\n"
	    "    <blup foo:qux='asd' quxqux='test'/>\n"
	    "    <bla:bla\r\rxmlns:bla\r=\t\"urn:objfw:test:bla\" qux='qux'\r\n"
	    "     bla:foo='blafoo'/>\n"
	    "    <abc xmlns='urn:objfw:test:abc' abc='abc' foo:abc='abc'/>\n"
	    "   </foo:bla>\n"
	    "   <!-- commänt -->\n"
	    "  </qux>\n"
	    " </foobar>\n"
	    "</root>";
	OFXMLParser *parser;
	size_t j, len;

	TEST(@"+[parser]", (parser = [OFXMLParser parser]))

	TEST(@"-[setDelegate:]", (parser.delegate = self))

	/* Simulate a stream where we only get chunks */
	len = strlen(str);

	for (j = 0; j < len; j+= 2) {
		if (parser.hasFinishedParsing)
			abort();

		if (j + 2 > len)
			[parser parseBuffer: str + j
				     length: 1];
		else
			[parser parseBuffer: str + j
				     length: 2];
	}

	TEST(@"Checking if everything was parsed",
	    i == 32 && parser.lineNumber == 18)

	TEST(@"-[hasFinishedParsing]", parser.hasFinishedParsing)

	TEST(@"Parsing whitespaces after the document",
	    R([parser parseString: @" \t\r\n "]))

	TEST(@"Parsing comments after the document",
	    R([parser parseString: @" \t<!-- foo -->\r<!--bar-->\n "]))

	EXPECT_EXCEPTION(@"Detection of junk after the document #1",
	    OFMalformedXMLException, [parser parseString: @"a"])

	EXPECT_EXCEPTION(@"Detection of junk after the document #2",
	    OFMalformedXMLException, [parser parseString: @"<!["])

	parser = [OFXMLParser parser];
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instructions #1",
	    OFMalformedXMLException,
	    [parser parseString: @"<?xml version='2.0'?>"])

	parser = [OFXMLParser parser];
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instructions #2",
	    OFInvalidEncodingException,
	    [parser parseString: @"<?xml encoding='UTF-7'?>"])

	parser = [OFXMLParser parser];
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instructions #3",
	    OFMalformedXMLException,
	    [parser parseString: @"<x><?xml?></x>"])

	objc_autoreleasePoolPop(pool);
}
@end

<
<
|




















|


|
|
|
|
|
|
|




|


|



|


|



|
>
>
|


|
>
|


>
|
|


>
|


>
|



>
|
|


>
|
|


>
|


>
|
>
|








>
|


>
|
>
|









>
|


>
|

|












>
|


>
|
>
|












>
|
>
|


>
|


>
|

|
>

















>
|

|


>
|


>
|
>
|

















>
|
|


>
|


>
|

|


>
|


>
|


>
|


>
|
>
|


>
|


>
|
>
|


>
|


>
|
|




|
|
>


|
|



|





|



|


|







|


|


|




|
<


|







|
<


|




|


|
<


|



















|















|






|

|



|
|
<

|
<




















|




|




|






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
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
/*


 * 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 <stdlib.h>
#include <string.h>

#import "TestsAppDelegate.h"

static OFString *const module = @"OFXMLParser";
static int i = 0;

enum EventType {
	eventTypeProcessingInstruction,
	eventTypeTagOpen,
	eventTypeTagClose,
	eventTypeString,
	eventTypeCDATA,
	eventTypeComment
};

@implementation TestsAppDelegate (OFXMLParser)
-   (void)parser: (OFXMLParser *)parser
  didCreateEvent: (enum EventType)type
	    name: (OFString *)name
	  prefix: (OFString *)prefix
       namespace: (OFString *)namespace
      attributes: (OFArray *)attrs
	  string: (OFString *)string
{
	OFString *message;

	i++;
	message = [OFString stringWithFormat: @"Parsing part #%d", i];

	switch (i) {
	case 1:
		TEST(message,
		    type == eventTypeProcessingInstruction &&
		    [name isEqual: @"xml"] &&
		    [string isEqual: @"version='1.0'"])
		break;
	case 2:
		TEST(message,
		    type == eventTypeProcessingInstruction &&
		    [name isEqual: @"p?i"] && string == nil)
		break;
	case 3:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"root"] &&
		    prefix == nil && namespace == nil && attrs.count == 0)
		break;
	case 4:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n\n "])
		break;
	case 5:
		TEST(message,
		    type == eventTypeCDATA && [string isEqual: @"f<]]]oo]"] &&
		    parser.lineNumber == 3)
		break;
	case 6:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"bar"] &&
		    prefix == nil && namespace == nil && attrs == nil)
		break;
	case 7:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"bar"] &&
		    prefix == nil && namespace == nil && attrs == nil)
		break;
	case 8:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n "])
		break;
	case 9:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"foobar"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"] &&
		    attrs.count == 1 &&
		    /* xmlns attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"xmlns"] &&
		    [[attrs objectAtIndex: 0] namespace] == nil &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:foobar"])
		break;
	case 10:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n  "])
		break;
	case 11:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"qux"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"] &&
		    attrs.count == 1 &&
		    /* xmlns:foo attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"foo"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"http://www.w3.org/2000/xmlns/"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:foo"])
		break;
	case 12:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n   "])
		break;
	case 13:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"foo"] &&
		    [namespace isEqual: @"urn:objfw:test:foo"] &&
		    attrs.count == 2 &&
		    /* foo:bla attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"bla"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"urn:objfw:test:foo"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual: @"bla"] &&
		    /* blafoo attr */
		    [[[attrs objectAtIndex: 1] name] isEqual: @"blafoo"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"foo"])
		break;
	case 14:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n    "])
		break;
	case 15:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"blup"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"] &&
		    attrs.count == 2 &&
		    /* foo:qux attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"qux"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"urn:objfw:test:foo"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual: @"asd"] &&
		    /* quxqux attr */
		    [[[attrs objectAtIndex: 1] name] isEqual: @"quxqux"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"test"])
		break;
	case 16:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"blup"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"])
		break;
	case 17:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n    "])
		break;
	case 18:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"bla"] &&
		    [namespace isEqual: @"urn:objfw:test:bla"] &&
		    attrs.count == 3 &&
		    /* xmlns:bla attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"bla"] &&
		    [[[attrs objectAtIndex: 0] namespace] isEqual:
		    @"http://www.w3.org/2000/xmlns/"] &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:bla"] &&
		    /* qux attr */
		    [[[attrs objectAtIndex: 1] name] isEqual: @"qux"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"qux"] &&
		    /* bla:foo attr */
		    [[[attrs objectAtIndex: 2] name] isEqual: @"foo"] &&
		    [[[attrs objectAtIndex: 2] namespace] isEqual:
		    @"urn:objfw:test:bla"] &&
		    [[[attrs objectAtIndex: 2] stringValue] isEqual: @"blafoo"])
		break;
	case 19:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"bla"] &&
		    [namespace isEqual: @"urn:objfw:test:bla"])
		break;
	case 20:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n    "])
		break;
	case 21:
		TEST(message,
		    type == eventTypeTagOpen && [name isEqual: @"abc"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:abc"] &&
		    attrs.count == 3 &&
		    /* xmlns attr */
		    [[[attrs objectAtIndex: 0] name] isEqual: @"xmlns"] &&
		    [[attrs objectAtIndex: 0] namespace] == nil &&
		    [[[attrs objectAtIndex: 0] stringValue] isEqual:
		    @"urn:objfw:test:abc"] &&
		    /* abc attr */
		    [[[attrs objectAtIndex: 1] name] isEqual: @"abc"] &&
		    [[attrs objectAtIndex: 1] namespace] == nil &&
		    [[[attrs objectAtIndex: 1] stringValue] isEqual: @"abc"] &&
		    /* foo:abc attr */
		    [[[attrs objectAtIndex: 2] name] isEqual: @"abc"] &&
		    [[[attrs objectAtIndex: 2] namespace] isEqual:
		    @"urn:objfw:test:foo"] &&
		    [[[attrs objectAtIndex: 2] stringValue] isEqual: @"abc"])
		break;
	case 22:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"abc"] &&
		    prefix == nil && [namespace isEqual: @"urn:objfw:test:abc"])
		break;
	case 23:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n   "])
		break;
	case 24:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"bla"] &&
		    [prefix isEqual: @"foo"] &&
		    [namespace isEqual: @"urn:objfw:test:foo"])
		break;
	case 25:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n   "])
		break;
	case 26:
		TEST(message,
		    type == eventTypeComment && [string isEqual: @" commänt "])
		break;
	case 27:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n  "])
		break;
	case 28:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"qux"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"])
		break;
	case 29:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n "])
		break;
	case 30:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"foobar"] &&
		    prefix == nil &&
		    [namespace isEqual: @"urn:objfw:test:foobar"])
		break;
	case 31:
		TEST(message,
		    type == eventTypeString && [string isEqual: @"\n"])
		break;
	case 32:
		TEST(message,
		    type == eventTypeTagClose && [name isEqual: @"root"] &&
		    prefix == nil && namespace == nil);
		break;
	}
}

-			  (void)parser: (OFXMLParser *)parser
  foundProcessingInstructionWithTarget: (OFString *)target
				  data: (OFString *)data
{
	[self	    parser: parser
	    didCreateEvent: eventTypeProcessingInstruction
		      name: target
		    prefix: nil
		 namespace: nil
		attributes: nil
		    string: data];
}

-    (void)parser: (OFXMLParser *)parser
  didStartElement: (OFString *)name
	   prefix: (OFString *)prefix
	namespace: (OFString *)namespace
       attributes: (OFArray *)attrs
{
	[self	    parser: parser
	    didCreateEvent: eventTypeTagOpen
		      name: name
		    prefix: prefix
		 namespace: namespace
		attributes: attrs
		    string: nil];
}

-  (void)parser: (OFXMLParser *)parser
  didEndElement: (OFString *)name
	 prefix: (OFString *)prefix
      namespace: (OFString *)namespace
{
	[self	    parser: parser
	    didCreateEvent: eventTypeTagClose
		      name: name
		    prefix: prefix
		 namespace: namespace
		attributes: nil
		    string: nil];
}

- (void)parser: (OFXMLParser *)parser foundCharacters: (OFString *)string

{
	[self	    parser: parser
	    didCreateEvent: eventTypeString
		      name: nil
		    prefix: nil
		 namespace: nil
		attributes: nil
		    string: string];
}

- (void)parser: (OFXMLParser *)parser foundCDATA: (OFString *)CDATA

{
	[self	    parser: parser
	    didCreateEvent: eventTypeCDATA
		      name: nil
		    prefix: nil
		 namespace: nil
		attributes: nil
		    string: CDATA];
}

- (void)parser: (OFXMLParser *)parser foundComment: (OFString *)comment

{
	[self	    parser: parser
	    didCreateEvent: eventTypeComment
		      name: nil
		    prefix: nil
		 namespace: nil
		attributes: nil
		    string: comment];
}

-      (OFString *)parser: (OFXMLParser *)parser
  foundUnknownEntityNamed: (OFString *)entity
{
	if ([entity isEqual: @"foo"])
		return @"foobar";

	return nil;
}

- (void)XMLParserTests
{
	void *pool = objc_autoreleasePoolPush();
	const char *string = "\xEF\xBB\xBF<?xml version='1.0'?><?p?i?>"
	    "<!DOCTYPE foo><root>\r\r"
	    " <![CDATA[f<]]]oo]]]><bar/>\n"
	    " <foobar xmlns='urn:objfw:test:foobar'>\r\n"
	    "  <qux xmlns:foo='urn:objfw:test:foo'>\n"
	    "   <foo:bla foo:bla = '&#x62;&#x6c;&#x61;' blafoo='foo'>\n"
	    "    <blup foo:qux='asd' quxqux='test'/>\n"
	    "    <bla:bla\r\rxmlns:bla\r=\t\"urn:objfw:test:bla\" qux='qux'\r\n"
	    "     bla:foo='blafoo'/>\n"
	    "    <abc xmlns='urn:objfw:test:abc' abc='abc' foo:abc='abc'/>\n"
	    "   </foo:bla>\n"
	    "   <!-- commänt -->\n"
	    "  </qux>\n"
	    " </foobar>\n"
	    "</root>";
	OFXMLParser *parser;
	size_t j, length;

	TEST(@"+[parser]", (parser = [OFXMLParser parser]))

	TEST(@"-[setDelegate:]", (parser.delegate = self))

	/* Simulate a stream where we only get chunks */
	length = strlen(string);

	for (j = 0; j < length; j+= 2) {
		if (parser.hasFinishedParsing)
			abort();

		if (j + 2 > length)
			[parser parseBuffer: string + j length: 1];

		else
			[parser parseBuffer: string + j length: 2];

	}

	TEST(@"Checking if everything was parsed",
	    i == 32 && parser.lineNumber == 18)

	TEST(@"-[hasFinishedParsing]", parser.hasFinishedParsing)

	TEST(@"Parsing whitespaces after the document",
	    R([parser parseString: @" \t\r\n "]))

	TEST(@"Parsing comments after the document",
	    R([parser parseString: @" \t<!-- foo -->\r<!--bar-->\n "]))

	EXPECT_EXCEPTION(@"Detection of junk after the document #1",
	    OFMalformedXMLException, [parser parseString: @"a"])

	EXPECT_EXCEPTION(@"Detection of junk after the document #2",
	    OFMalformedXMLException, [parser parseString: @"<!["])

	parser = [OFXMLParser parser];
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instruction #1",
	    OFMalformedXMLException,
	    [parser parseString: @"<?xml version='2.0'?>"])

	parser = [OFXMLParser parser];
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instruction #2",
	    OFInvalidEncodingException,
	    [parser parseString: @"<?xml encoding='UTF-7'?>"])

	parser = [OFXMLParser parser];
	EXPECT_EXCEPTION(@"Detection of invalid XML processing instruction #3",
	    OFMalformedXMLException,
	    [parser parseString: @"<x><?xml?></x>"])

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/RuntimeARCTests.m from [951f2e2edf] to [3a41eba7d4].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"Runtime (ARC)";

@interface RuntimeARCTest: OFObject
@end

@implementation RuntimeARCTest
- (instancetype)init
{
	self = [super init];









	@throw [OFException exception];


	return self;
}
@end

@implementation TestsAppDelegate (RuntimeARCTests)
- (void)runtimeARCTests
{
	id object;


	EXPECT_EXCEPTION(@"Exceptions in init", OFException,
	    object = [[RuntimeARCTest alloc] init])
}







@end

<
<
|

















|









>
>
>
>
>
>
>
>

>









>



|
>
>
>
>
>
>
>

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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"Runtime (ARC)";

@interface RuntimeARCTest: OFObject
@end

@implementation RuntimeARCTest
- (instancetype)init
{
	self = [super init];

#ifdef OF_WINDOWS
	/*
	 * Clang has a bug on Windows where it creates an invalid call into
	 * objc_retainAutoreleasedReturnValue(). Work around it by not using an
	 * autoreleased exception.
	 */
	@throw [[OFException alloc] init];
#else
	@throw [OFException exception];
#endif

	return self;
}
@end

@implementation TestsAppDelegate (RuntimeARCTests)
- (void)runtimeARCTests
{
	id object;
	__weak id weak;

	EXPECT_EXCEPTION(@"Exceptions in init", OFException,
	    object = [[RuntimeARCTest alloc] init])

	object = [[OFObject alloc] init];
	weak = object;
	TEST(@"weakly referencing an object", weak == object)

	object = nil;
	TEST(@"weak references becoming nil", weak == nil)
}
@end

Modified tests/RuntimeTests.m from [eff11f992f] to [9aa59cf983].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "TestsAppDelegate.h"

static OFString *module = @"Runtime";

@interface OFObject (SuperTest)
- (id)superTest;
@end

@interface RuntimeTest: OFObject
{

<
<
|

















|







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
/*


 * 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"

#import "TestsAppDelegate.h"

static OFString *const module = @"Runtime";

@interface OFObject (SuperTest)
- (id)superTest;
@end

@interface RuntimeTest: OFObject
{
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
}
@end

@implementation TestsAppDelegate (RuntimeTests)
- (void)runtimeTests
{
	void *pool = objc_autoreleasePoolPush();
	RuntimeTest *rt = [[[RuntimeTest alloc] init] autorelease];
	OFString *t, *foo;
#ifdef OF_OBJFW_RUNTIME
	int cid1, cid2;
	uintmax_t value;
	id object;
#endif

	EXPECT_EXCEPTION(@"Calling a non-existent method via super",
	    OFNotImplementedException, [rt superTest])

	TEST(@"Calling a method via a super with self == nil",
	    [rt nilSuperTest] == nil)

	t = [OFMutableString stringWithString: @"foo"];
	foo = @"foo";

	[rt setFoo: t];
	TEST(@"copy, nonatomic properties", [rt.foo isEqual: foo] &&
	    rt.foo != foo && rt.foo.retainCount == 1)

	rt.bar = t;
	TEST(@"retain, atomic properties", rt.bar == t && t.retainCount == 3)


#ifdef OF_OBJFW_RUNTIME
	if (sizeof(uintptr_t) == 8)
		value = 0xDEADBEEFDEADBEF;
	else if (sizeof(uintptr_t) == 4)
		value = 0xDEADBEF;
	else
		abort();

	TEST(@"Tagged pointers",
	    (cid1 = objc_registerTaggedPointerClass([OFString class])) != -1 &&
	    (cid2 = objc_registerTaggedPointerClass([OFNumber class])) != -1 &&

	    (object = objc_createTaggedPointer(cid2, (uintptr_t)value)) &&
	    object_getClass(object) == [OFNumber class] &&
	    [object class] == [OFNumber class] &&
	    object_getTaggedPointerValue(object) == value &&
	    objc_createTaggedPointer(cid2, UINTPTR_MAX >> 4) != nil &&
	    objc_createTaggedPointer(cid2, (UINTPTR_MAX >> 4) + 1) == nil)
#endif

	objc_autoreleasePoolPop(pool);
}
@end







|
|

|





|


|

|


|
|
|

|
|
>










|
|
>
|



|
|





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
}
@end

@implementation TestsAppDelegate (RuntimeTests)
- (void)runtimeTests
{
	void *pool = objc_autoreleasePoolPush();
	RuntimeTest *test = [[[RuntimeTest alloc] init] autorelease];
	OFString *string, *foo;
#ifdef OF_OBJFW_RUNTIME
	int classID;
	uintmax_t value;
	id object;
#endif

	EXPECT_EXCEPTION(@"Calling a non-existent method via super",
	    OFNotImplementedException, [test superTest])

	TEST(@"Calling a method via a super with self == nil",
	    [test nilSuperTest] == nil)

	string = [OFMutableString stringWithString: @"foo"];
	foo = @"foo";

	test.foo = string;
	TEST(@"copy, nonatomic properties", [test.foo isEqual: foo] &&
	    test.foo != foo && test.foo.retainCount == 1)

	test.bar = string;
	TEST(@"retain, atomic properties",
	    test.bar == string && string.retainCount == 3)

#ifdef OF_OBJFW_RUNTIME
	if (sizeof(uintptr_t) == 8)
		value = 0xDEADBEEFDEADBEF;
	else if (sizeof(uintptr_t) == 4)
		value = 0xDEADBEF;
	else
		abort();

	TEST(@"Tagged pointers",
	    objc_registerTaggedPointerClass([OFString class]) != -1 &&
	    (classID = objc_registerTaggedPointerClass([OFNumber class])) !=
	    -1 &&
	    (object = objc_createTaggedPointer(classID, (uintptr_t)value)) &&
	    object_getClass(object) == [OFNumber class] &&
	    [object class] == [OFNumber class] &&
	    object_getTaggedPointerValue(object) == value &&
	    objc_createTaggedPointer(classID, UINTPTR_MAX >> 4) != nil &&
	    objc_createTaggedPointer(classID, (UINTPTR_MAX >> 4) + 1) == nil)
#endif

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/TestsAppDelegate.h from [474ebe8ce9] to [453017b72e].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "ObjFW.h"

#define TEST(test, ...)					\
	{						\
		[self outputTesting: test		\
			   inModule: module];		\
							\
		if (__VA_ARGS__)			\
			[self outputSuccess: test	\
				   inModule: module];	\
		else {					\
			[self outputFailure: test	\
				   inModule: module];	\
			_fails++;			\
		}					\
	}
#define EXPECT_EXCEPTION(test, exception, code)		\
	{						\
		bool caught = false;			\
							\
		[self outputTesting: test		\
			   inModule: module];		\
							\
		@try {					\
			code;				\
		} @catch (exception *e) {		\
			caught = true;			\
		}					\
							\
		if (caught)				\
			[self outputSuccess: test	\
				   inModule: module];	\
		else {					\
			[self outputFailure: test	\
				   inModule: module];	\
			_fails++;			\
		}					\
	}
#define R(...) (__VA_ARGS__, 1)

@class OFString;

@interface TestsAppDelegate: OFObject <OFApplicationDelegate>
{
	int _fails;
}

- (void)outputTesting: (OFString *)test
	     inModule: (OFString *)module;
- (void)outputSuccess: (OFString *)test
	     inModule: (OFString *)module;
- (void)outputFailure: (OFString *)test
	     inModule: (OFString *)module;
@end

@interface TestsAppDelegate (OFASN1DERParsingTests)
- (void)ASN1DERParsingTests;
@end

@interface TestsAppDelegate (OFASN1DERRepresentationTests)
- (void)ASN1DERRepresentationTests;
@end

@interface TestsAppDelegate (OFArrayTests)
- (void)arrayTests;
@end

@interface TestsAppDelegate (OFBlockTests)

<
<
|















|
|
|
<
|
|
|
<
|
|
<
|
|

|
|
|
|
|
<
|
|
|
|
|
|
|
|
|
<
|
|
<
|
|










|
<
|
<
|
<
<
<
<
<
<
<
<
<







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
/*


 * 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 "ObjFW.h"

#define TEST(test, ...)							\
	{								\
		[self outputTesting: test inModule: module];		\

									\
		if (__VA_ARGS__)					\
			[self outputSuccess: test inModule: module];	\

		else {							\
			[self outputFailure: test inModule: module];	\

			_fails++;					\
		}							\
	}
#define EXPECT_EXCEPTION(test, exception, code)				\
	{								\
		bool caught = false;					\
									\
		[self outputTesting: test inModule: module];		\

									\
		@try {							\
			code;						\
		} @catch (exception *e) {				\
			caught = true;					\
		}							\
									\
		if (caught)						\
			[self outputSuccess: test inModule: module];	\

		else {							\
			[self outputFailure: test inModule: module];	\

			_fails++;					\
		}							\
	}
#define R(...) (__VA_ARGS__, 1)

@class OFString;

@interface TestsAppDelegate: OFObject <OFApplicationDelegate>
{
	int _fails;
}

- (void)outputTesting: (OFString *)test inModule: (OFString *)module;

- (void)outputSuccess: (OFString *)test inModule: (OFString *)module;

- (void)outputFailure: (OFString *)test inModule: (OFString *)module;









@end

@interface TestsAppDelegate (OFArrayTests)
- (void)arrayTests;
@end

@interface TestsAppDelegate (OFBlockTests)
153
154
155
156
157
158
159




160
161
162
163
164
165
166
167




168
169
170
171
172
173
174
@interface TestsAppDelegate (OFMD5HashTests)
- (void)MD5HashTests;
@end

@interface TestsAppDelegate (OFMethodSignatureTests)
- (void)methodSignatureTests;
@end





@interface TestsAppDelegate (OFNumberTests)
- (void)numberTests;
@end

@interface TestsAppDelegate (OFObjectTests)
- (void)objectTests;
@end





@interface TestsAppDelegate (OFPropertyListTests)
- (void)propertyListTests;
@end

@interface TestsAppDelegate (OFPluginTests)
- (void)pluginTests;







>
>
>
>








>
>
>
>







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
@interface TestsAppDelegate (OFMD5HashTests)
- (void)MD5HashTests;
@end

@interface TestsAppDelegate (OFMethodSignatureTests)
- (void)methodSignatureTests;
@end

@interface TestsAppDelegate (OFNotificationCenterTests)
- (void)notificationCenterTests;
@end

@interface TestsAppDelegate (OFNumberTests)
- (void)numberTests;
@end

@interface TestsAppDelegate (OFObjectTests)
- (void)objectTests;
@end

@interface TestsAppDelegate (OFPBKDF2Tests)
- (void)PBKDF2Tests;
@end

@interface TestsAppDelegate (OFPropertyListTests)
- (void)propertyListTests;
@end

@interface TestsAppDelegate (OFPluginTests)
- (void)pluginTests;
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
- (void)runtimeARCTests;
@end

@interface TestsAppDelegate (OFRIPEMD160HashTests)
- (void)RIPEMD160HashTests;
@end

@interface TestsAppDelegate (ScryptTests)
- (void)scryptTests;
@end

@interface TestsAppDelegate (OFSHA1HashTests)
- (void)SHA1HashTests;
@end








|







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
- (void)runtimeARCTests;
@end

@interface TestsAppDelegate (OFRIPEMD160HashTests)
- (void)RIPEMD160HashTests;
@end

@interface TestsAppDelegate (OFScryptTests)
- (void)scryptTests;
@end

@interface TestsAppDelegate (OFSHA1HashTests)
- (void)SHA1HashTests;
@end

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
- (void)SHA384HashTests;
@end

@interface TestsAppDelegate (OFSHA512HashTests)
- (void)SHA512HashTests;
@end

@interface TestsAppDelegate (OFSCTPSocketTests)
- (void)SCTPSocketTests;
@end

@interface TestsAppDelegate (OFSPXSocketTests)
- (void)SPXSocketTests;
@end

@interface TestsAppDelegate (OFSPXStreamSocketTests)
- (void)SPXStreamSocketTests;
@end







<
<
<
<







195
196
197
198
199
200
201




202
203
204
205
206
207
208
- (void)SHA384HashTests;
@end

@interface TestsAppDelegate (OFSHA512HashTests)
- (void)SHA512HashTests;
@end





@interface TestsAppDelegate (OFSPXSocketTests)
- (void)SPXSocketTests;
@end

@interface TestsAppDelegate (OFSPXStreamSocketTests)
- (void)SPXStreamSocketTests;
@end
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
@interface TestsAppDelegate (OFSystemInfoTests)
- (void)systemInfoTests;
@end

@interface TestsAppDelegate (OFHMACTests)
- (void)HMACTests;
@end





@interface TestsAppDelegate (OFStreamTests)
- (void)streamTests;
@end

@interface TestsAppDelegate (OFStringTests)
- (void)stringTests;
@end

@interface TestsAppDelegate (OFTCPSocketTests)
- (void)TCPSocketTests;
@end

@interface TestsAppDelegate (OFThreadTests)
- (void)threadTests;
@end

@interface TestsAppDelegate (OFUDPSocketTests)
- (void)UDPSocketTests;
@end









@interface TestsAppDelegate (OFURLTests)
- (void)URLTests;
@end

@interface TestsAppDelegate (OFValueTests)
- (void)valueTests;







>
>
>
>




















>
>
>
>
>
>
>
>







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
@interface TestsAppDelegate (OFSystemInfoTests)
- (void)systemInfoTests;
@end

@interface TestsAppDelegate (OFHMACTests)
- (void)HMACTests;
@end

@interface TestsAppDelegate (OFSocketTests)
- (void)socketTests;
@end

@interface TestsAppDelegate (OFStreamTests)
- (void)streamTests;
@end

@interface TestsAppDelegate (OFStringTests)
- (void)stringTests;
@end

@interface TestsAppDelegate (OFTCPSocketTests)
- (void)TCPSocketTests;
@end

@interface TestsAppDelegate (OFThreadTests)
- (void)threadTests;
@end

@interface TestsAppDelegate (OFUDPSocketTests)
- (void)UDPSocketTests;
@end

@interface TestsAppDelegate (OFUNIXDatagramSocketTests)
- (void)UNIXDatagramSocketTests;
@end

@interface TestsAppDelegate (OFUNIXStreamSocketTests)
- (void)UNIXStreamSocketTests;
@end

@interface TestsAppDelegate (OFURLTests)
- (void)URLTests;
@end

@interface TestsAppDelegate (OFValueTests)
- (void)valueTests;
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
- (void)XMLNodeTests;
@end

@interface TestsAppDelegate (OFXMLParserTests)
    <OFXMLParserDelegate, OFXMLElementBuilderDelegate>
- (void)XMLParserTests;
@end

@interface TestsAppDelegate (PBKDF2Tests)
- (void)PBKDF2Tests;
@end

@interface TestsAppDelegate (SocketTests)
- (void)socketTests;
@end







<
<
<
<
<
<
<
<
275
276
277
278
279
280
281








- (void)XMLNodeTests;
@end

@interface TestsAppDelegate (OFXMLParserTests)
    <OFXMLParserDelegate, OFXMLElementBuilderDelegate>
- (void)XMLParserTests;
@end








Modified tests/TestsAppDelegate.m from [7de17ddfab] to [e90fb7f104].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#ifdef OF_NINTENDO_3DS
/* Newer versions of libctru started using id as a parameter name. */
# define id id_3ds
# include <3ds.h>
# undef id
#endif



#ifdef OF_PSP
static int
exit_cb(int arg1, int arg2, void *arg)
{
	sceKernelExitGame();

	return 0;
}

static int
callback_thread(SceSize args, void *argp)
{
	sceKernelRegisterExitCallback(
	    sceKernelCreateCallback("Exit Callback", exit_cb, NULL));
	sceKernelSleepThreadCB();

	return 0;
}
#endif

int
main(int argc, char *argv[])
{
#ifdef OF_PSP
	int tid;
#endif

#if defined(OF_OBJFW_RUNTIME) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS)
	/*
	 * This does not work on Win32 if ObjFW is built as a DLL.
	 *
	 * On AmigaOS, some destructors need to be able to send messages.
	 * Calling objc_exit() via atexit() would result in the runtime being
	 * destructed before for the destructors ran.
	 */
	atexit(objc_exit);
#endif

	/* We need deterministic hashes for tests */
	of_hash_seed = 0;

#ifdef OF_WII
	GXRModeObj *rmode;
	void *xfb;

	VIDEO_Init();
	WPAD_Init();







>
>


|







|


|


















|


|



|







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
#ifdef OF_NINTENDO_3DS
/* Newer versions of libctru started using id as a parameter name. */
# define id id_3ds
# include <3ds.h>
# undef id
#endif

extern unsigned long OFHashSeed;

#ifdef OF_PSP
static int
exitCallback(int arg1, int arg2, void *arg)
{
	sceKernelExitGame();

	return 0;
}

static int
threadCallback(SceSize args, void *argp)
{
	sceKernelRegisterExitCallback(
	    sceKernelCreateCallback("Exit Callback", exitCallback, NULL));
	sceKernelSleepThreadCB();

	return 0;
}
#endif

int
main(int argc, char *argv[])
{
#ifdef OF_PSP
	int tid;
#endif

#if defined(OF_OBJFW_RUNTIME) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS)
	/*
	 * This does not work on Win32 if ObjFW is built as a DLL.
	 *
	 * On AmigaOS, some destructors need to be able to send messages.
	 * Calling objc_deinit() via atexit() would result in the runtime being
	 * destructed before for the destructors ran.
	 */
	atexit(objc_deinit);
#endif

	/* We need deterministic hashes for tests */
	OFHashSeed = 0;

#ifdef OF_WII
	GXRModeObj *rmode;
	void *xfb;

	VIDEO_Init();
	WPAD_Init();
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
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

#ifdef OF_PSP
	pspDebugScreenInit();

	sceCtrlSetSamplingCycle(0);
	sceCtrlSetSamplingMode(PSP_CTRL_MODE_DIGITAL);

	if ((tid = sceKernelCreateThread("update_thread", callback_thread,
	    0x11, 0xFA0, 0, 0)) >= 0)
		sceKernelStartThread(tid, 0, 0);
#endif

#ifdef OF_NINTENDO_DS
	consoleDemoInit();
#endif

#ifdef OF_NINTENDO_3DS
	gfxInitDefault();
	atexit(gfxExit);

	consoleInit(GFX_TOP, NULL);
#endif

#if defined(OF_WII) || defined(OF_PSP) || defined(OF_NINTENDO_DS) || \
	defined(OF_NINTENDO_3DS)
	@try {
		return of_application_main(&argc, &argv,
		    [[TestsAppDelegate alloc] init]);
	} @catch (id e) {
		OFString *string = [OFString stringWithFormat:
		    @"\nRuntime error: Unhandled exception:\n%@\n", e];
		OFString *backtrace = [OFString stringWithFormat:
		    @"\nBacktrace:\n  %@\n\n",
		    [[e backtrace] componentsJoinedByString: @"\n  "]];

		[of_stdout setForegroundColor: [OFColor red]];
		[of_stdout writeString: string];
		[of_stdout writeString: backtrace];

# if defined(OF_WII)
		[of_stdout reset];
		[of_stdout writeString: @"Press home button to exit!"];

		for (;;) {
			WPAD_ScanPads();

			if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
				[OFApplication terminateWithStatus: 1];

			VIDEO_WaitVSync();
		}
# elif defined(OF_PSP)
		sceKernelSleepThreadCB();
# elif defined(OF_NINTENDO_DS)
		[of_stdout reset];
		[of_stdout writeString: @"Press start button to exit!"];

		for (;;) {
			swiWaitForVBlank();
			scanKeys();
			if (keysDown() & KEY_START)
				[OFApplication terminateWithStatus: 1];
		}
# elif defined(OF_NINTENDO_3DS)
		[of_stdout reset];
		[of_stdout writeString: @"Press start button to exit!"];

		for (;;) {
			hidScanInput();

			if (hidKeysDown() & KEY_START)
				[OFApplication terminateWithStatus: 1];

			gspWaitForVBlank();
		}
# else
		abort();
# endif
	}
#else
	return of_application_main(&argc, &argv,
	    [[TestsAppDelegate alloc] init]);
#endif
}

@implementation TestsAppDelegate
- (void)outputTesting: (OFString *)test
	     inModule: (OFString *)module
{
	if (of_stdout.hasTerminal) {
		[of_stdout setForegroundColor: [OFColor yellow]];
		[of_stdout writeFormat: @"[%@] %@: testing...", module, test];
	} else
		[of_stdout writeFormat: @"[%@] %@: ", module, test];
}

- (void)outputSuccess: (OFString *)test
	     inModule: (OFString *)module
{
	if (of_stdout.hasTerminal) {
		[of_stdout setForegroundColor: [OFColor lime]];
		[of_stdout eraseLine];
		[of_stdout writeFormat: @"\r[%@] %@: ok\n", module, test];
	} else
		[of_stdout writeLine: @"ok"];
}

- (void)outputFailure: (OFString *)test
	     inModule: (OFString *)module
{
	if (of_stdout.hasTerminal) {
		[of_stdout setForegroundColor: [OFColor red]];
		[of_stdout eraseLine];
		[of_stdout writeFormat: @"\r[%@] %@: failed\n", module, test];

#ifdef OF_WII
		[of_stdout reset];
		[of_stdout writeLine: @"Press A to continue!"];

		for (;;) {
			WPAD_ScanPads();

			if (WPAD_ButtonsDown(0) & WPAD_BUTTON_A)
				return;

			VIDEO_WaitVSync();
		}
#endif
#ifdef OF_PSP
		[of_stdout reset];
		[of_stdout writeLine: @"Press X to continue!"];

		for (;;) {
			SceCtrlData pad;

			sceCtrlReadBufferPositive(&pad, 1);
			if (pad.Buttons & PSP_CTRL_CROSS) {
				for (;;) {
					sceCtrlReadBufferPositive(&pad, 1);
					if (!(pad.Buttons & PSP_CTRL_CROSS))
						return;
				}
			}
		}
#endif
#ifdef OF_NINTENDO_DS
		[of_stdout reset];
		[of_stdout writeString: @"Press A to continue!"];

		for (;;) {
			swiWaitForVBlank();
			scanKeys();
			if (keysDown() & KEY_A)
				break;
		}
#endif
#ifdef OF_NINTENDO_3DS
		[of_stdout reset];
		[of_stdout writeString: @"Press A to continue!"];

		for (;;) {
			hidScanInput();

			if (hidKeysDown() & KEY_A)
				break;

			gspWaitForVBlank();
		}
#endif

		[of_stdout writeString: @"\r"];
		[of_stdout reset];
		[of_stdout eraseLine];
	} else
		[of_stdout writeLine: @"failed"];
}

- (void)applicationDidFinishLaunching
{
#if defined(OF_IOS) && defined(OF_HAVE_FILES)
	CFBundleRef mainBundle = CFBundleGetMainBundle();
	CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
	UInt8 resourcesPath[PATH_MAX];

	if (!CFURLGetFileSystemRepresentation(resourcesURL, true, resourcesPath,
	    PATH_MAX)) {
		[of_stderr writeString: @"Failed to locate resources!\n"];
		[OFApplication terminateWithStatus: 1];
	}

	[[OFFileManager defaultManager] changeCurrentDirectoryPath:
	    [OFString stringWithUTF8String: (const char *)resourcesPath]];
#endif
#if defined(OF_WII) && defined(OF_HAVE_FILES)







|


















|








|
|
|


|
|












|
|








|
|














<
|




|
<

|
|
|

|


|
<

|
|
|
|

|


|
<

|
|
|
|


|
|











|
|















|
|









|
|











|
|
|

|











|







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
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

#ifdef OF_PSP
	pspDebugScreenInit();

	sceCtrlSetSamplingCycle(0);
	sceCtrlSetSamplingMode(PSP_CTRL_MODE_DIGITAL);

	if ((tid = sceKernelCreateThread("update_thread", threadCallback,
	    0x11, 0xFA0, 0, 0)) >= 0)
		sceKernelStartThread(tid, 0, 0);
#endif

#ifdef OF_NINTENDO_DS
	consoleDemoInit();
#endif

#ifdef OF_NINTENDO_3DS
	gfxInitDefault();
	atexit(gfxExit);

	consoleInit(GFX_TOP, NULL);
#endif

#if defined(OF_WII) || defined(OF_PSP) || defined(OF_NINTENDO_DS) || \
	defined(OF_NINTENDO_3DS)
	@try {
		return OFApplicationMain(&argc, &argv,
		    [[TestsAppDelegate alloc] init]);
	} @catch (id e) {
		OFString *string = [OFString stringWithFormat:
		    @"\nRuntime error: Unhandled exception:\n%@\n", e];
		OFString *backtrace = [OFString stringWithFormat:
		    @"\nBacktrace:\n  %@\n\n",
		    [[e backtrace] componentsJoinedByString: @"\n  "]];

		[OFStdOut setForegroundColor: [OFColor red]];
		[OFStdOut writeString: string];
		[OFStdOut writeString: backtrace];

# if defined(OF_WII)
		[OFStdOut reset];
		[OFStdOut writeString: @"Press home button to exit!"];

		for (;;) {
			WPAD_ScanPads();

			if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
				[OFApplication terminateWithStatus: 1];

			VIDEO_WaitVSync();
		}
# elif defined(OF_PSP)
		sceKernelSleepThreadCB();
# elif defined(OF_NINTENDO_DS)
		[OFStdOut reset];
		[OFStdOut writeString: @"Press start button to exit!"];

		for (;;) {
			swiWaitForVBlank();
			scanKeys();
			if (keysDown() & KEY_START)
				[OFApplication terminateWithStatus: 1];
		}
# elif defined(OF_NINTENDO_3DS)
		[OFStdOut reset];
		[OFStdOut writeString: @"Press start button to exit!"];

		for (;;) {
			hidScanInput();

			if (hidKeysDown() & KEY_START)
				[OFApplication terminateWithStatus: 1];

			gspWaitForVBlank();
		}
# else
		abort();
# endif
	}
#else

	return OFApplicationMain(&argc, &argv, [[TestsAppDelegate alloc] init]);
#endif
}

@implementation TestsAppDelegate
- (void)outputTesting: (OFString *)test inModule: (OFString *)module

{
	if (OFStdOut.hasTerminal) {
		[OFStdOut setForegroundColor: [OFColor yellow]];
		[OFStdOut writeFormat: @"[%@] %@: testing...", module, test];
	} else
		[OFStdOut writeFormat: @"[%@] %@: ", module, test];
}

- (void)outputSuccess: (OFString *)test inModule: (OFString *)module

{
	if (OFStdOut.hasTerminal) {
		[OFStdOut setForegroundColor: [OFColor lime]];
		[OFStdOut eraseLine];
		[OFStdOut writeFormat: @"\r[%@] %@: ok\n", module, test];
	} else
		[OFStdOut writeLine: @"ok"];
}

- (void)outputFailure: (OFString *)test inModule: (OFString *)module

{
	if (OFStdOut.hasTerminal) {
		[OFStdOut setForegroundColor: [OFColor red]];
		[OFStdOut eraseLine];
		[OFStdOut writeFormat: @"\r[%@] %@: failed\n", module, test];

#ifdef OF_WII
		[OFStdOut reset];
		[OFStdOut writeLine: @"Press A to continue!"];

		for (;;) {
			WPAD_ScanPads();

			if (WPAD_ButtonsDown(0) & WPAD_BUTTON_A)
				return;

			VIDEO_WaitVSync();
		}
#endif
#ifdef OF_PSP
		[OFStdOut reset];
		[OFStdOut writeLine: @"Press X to continue!"];

		for (;;) {
			SceCtrlData pad;

			sceCtrlReadBufferPositive(&pad, 1);
			if (pad.Buttons & PSP_CTRL_CROSS) {
				for (;;) {
					sceCtrlReadBufferPositive(&pad, 1);
					if (!(pad.Buttons & PSP_CTRL_CROSS))
						return;
				}
			}
		}
#endif
#ifdef OF_NINTENDO_DS
		[OFStdOut reset];
		[OFStdOut writeString: @"Press A to continue!"];

		for (;;) {
			swiWaitForVBlank();
			scanKeys();
			if (keysDown() & KEY_A)
				break;
		}
#endif
#ifdef OF_NINTENDO_3DS
		[OFStdOut reset];
		[OFStdOut writeString: @"Press A to continue!"];

		for (;;) {
			hidScanInput();

			if (hidKeysDown() & KEY_A)
				break;

			gspWaitForVBlank();
		}
#endif

		[OFStdOut writeString: @"\r"];
		[OFStdOut reset];
		[OFStdOut eraseLine];
	} else
		[OFStdOut writeLine: @"failed"];
}

- (void)applicationDidFinishLaunching
{
#if defined(OF_IOS) && defined(OF_HAVE_FILES)
	CFBundleRef mainBundle = CFBundleGetMainBundle();
	CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
	UInt8 resourcesPath[PATH_MAX];

	if (!CFURLGetFileSystemRepresentation(resourcesURL, true, resourcesPath,
	    PATH_MAX)) {
		[OFStdErr writeString: @"Failed to locate resources!\n"];
		[OFApplication terminateWithStatus: 1];
	}

	[[OFFileManager defaultManager] changeCurrentDirectoryPath:
	    [OFString stringWithUTF8String: (const char *)resourcesPath]];
#endif
#if defined(OF_WII) && defined(OF_HAVE_FILES)
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
	[self dictionaryTests];
	[self listTests];
	[self setTests];
	[self dateTests];
	[self valueTests];
	[self numberTests];
	[self streamTests];

#ifdef OF_HAVE_FILES
	[self MD5HashTests];
	[self RIPEMD160HashTests];
	[self SHA1HashTests];
	[self SHA224HashTests];
	[self SHA256HashTests];
	[self SHA384HashTests];
	[self SHA512HashTests];
	[self HMACTests];
#endif
	[self PBKDF2Tests];
	[self scryptTests];
#if defined(OF_HAVE_FILES) && defined(HAVE_CODEPAGE_437)
	[self INIFileTests];
#endif
#ifdef OF_HAVE_SOCKETS
	[self socketTests];
	[self TCPSocketTests];
	[self UDPSocketTests];
# ifdef OF_HAVE_SCTP
	[self SCTPSocketTests];
# endif
# ifdef OF_HAVE_IPX
	[self IPXSocketTests];
	[self SPXSocketTests];
	[self SPXStreamSocketTests];
# endif




	[self kernelEventObserverTests];
#endif
#ifdef OF_HAVE_THREADS
	[self threadTests];
#endif
	[self URLTests];
#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)







>



















<
<
<





>
>
>
>







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
	[self dictionaryTests];
	[self listTests];
	[self setTests];
	[self dateTests];
	[self valueTests];
	[self numberTests];
	[self streamTests];
	[self notificationCenterTests];
#ifdef OF_HAVE_FILES
	[self MD5HashTests];
	[self RIPEMD160HashTests];
	[self SHA1HashTests];
	[self SHA224HashTests];
	[self SHA256HashTests];
	[self SHA384HashTests];
	[self SHA512HashTests];
	[self HMACTests];
#endif
	[self PBKDF2Tests];
	[self scryptTests];
#if defined(OF_HAVE_FILES) && defined(HAVE_CODEPAGE_437)
	[self INIFileTests];
#endif
#ifdef OF_HAVE_SOCKETS
	[self socketTests];
	[self TCPSocketTests];
	[self UDPSocketTests];



# ifdef OF_HAVE_IPX
	[self IPXSocketTests];
	[self SPXSocketTests];
	[self SPXStreamSocketTests];
# endif
# ifdef OF_HAVE_UNIX_SOCKETS
	[self UNIXDatagramSocketTests];
	[self UNIXStreamSocketTests];
# endif
	[self kernelEventObserverTests];
#endif
#ifdef OF_HAVE_THREADS
	[self threadTests];
#endif
	[self URLTests];
#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS)
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
	[self XMLNodeTests];
	[self XMLElementBuilderTests];
#ifdef OF_HAVE_FILES
	[self serializationTests];
#endif
	[self JSONTests];
	[self propertyListTests];
	[self ASN1DERParsingTests];
	[self ASN1DERRepresentationTests];
#if defined(OF_HAVE_PLUGINS)
	[self pluginTests];
#endif
#ifdef OF_WINDOWS
	[self windowsRegistryKeyTests];
#endif

#ifdef OF_HAVE_SOCKETS
	[self DNSResolverTests];
#endif
	[self systemInfoTests];
	[self localeTests];

	[of_stdout reset];

#if defined(OF_IOS)
	[of_stdout writeFormat: @"%d tests failed!", _fails];
	[OFApplication terminateWithStatus: _fails];
#elif defined(OF_WII)
	[of_stdout writeString: @"Press home button to exit!"];

	for (;;) {
		WPAD_ScanPads();

		if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
			[OFApplication terminateWithStatus: _fails];

		VIDEO_WaitVSync();
	}
#elif defined(OF_PSP)
	[of_stdout writeFormat: @"%d tests failed!", _fails];

	sceKernelSleepThreadCB();
#elif defined(OF_NINTENDO_DS)
	[of_stdout writeString: @"Press start button to exit!"];

	for (;;) {
		swiWaitForVBlank();
		scanKeys();
		if (keysDown() & KEY_START)
			[OFApplication terminateWithStatus: _fails];
	}
#elif defined(OF_NINTENDO_3DS)
	[of_stdout writeString: @"Press start button to exit!"];

	for (;;) {
		hidScanInput();

		if (hidKeysDown() & KEY_START)
			[OFApplication terminateWithStatus: _fails];








<
<













|


|


|










|



|








|







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
	[self XMLNodeTests];
	[self XMLElementBuilderTests];
#ifdef OF_HAVE_FILES
	[self serializationTests];
#endif
	[self JSONTests];
	[self propertyListTests];


#if defined(OF_HAVE_PLUGINS)
	[self pluginTests];
#endif
#ifdef OF_WINDOWS
	[self windowsRegistryKeyTests];
#endif

#ifdef OF_HAVE_SOCKETS
	[self DNSResolverTests];
#endif
	[self systemInfoTests];
	[self localeTests];

	[OFStdOut reset];

#if defined(OF_IOS)
	[OFStdOut writeFormat: @"%d tests failed!", _fails];
	[OFApplication terminateWithStatus: _fails];
#elif defined(OF_WII)
	[OFStdOut writeString: @"Press home button to exit!"];

	for (;;) {
		WPAD_ScanPads();

		if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
			[OFApplication terminateWithStatus: _fails];

		VIDEO_WaitVSync();
	}
#elif defined(OF_PSP)
	[OFStdOut writeFormat: @"%d tests failed!", _fails];

	sceKernelSleepThreadCB();
#elif defined(OF_NINTENDO_DS)
	[OFStdOut writeString: @"Press start button to exit!"];

	for (;;) {
		swiWaitForVBlank();
		scanKeys();
		if (keysDown() & KEY_START)
			[OFApplication terminateWithStatus: _fails];
	}
#elif defined(OF_NINTENDO_3DS)
	[OFStdOut writeString: @"Press start button to exit!"];

	for (;;) {
		hidScanInput();

		if (hidKeysDown() & KEY_START)
			[OFApplication terminateWithStatus: _fails];

Modified tests/iOS.xcodeproj/project.pbxproj from [feb9f04764] to [2c6fcc5e96].

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		4BEBFB532013934E002E8710 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastUpgradeCheck = 0920;
				ORGANIZATIONNAME = "Jonathan Schleifer";
				TargetAttributes = {
					4BEBFB5A2013934E002E8710 = {
						CreatedOnToolsVersion = 9.2;
						ProvisioningStyle = Automatic;
					};
				};







|







131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		4BEBFB532013934E002E8710 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastUpgradeCheck = 1330;
				ORGANIZATIONNAME = "Jonathan Schleifer";
				TargetAttributes = {
					4BEBFB5A2013934E002E8710 = {
						CreatedOnToolsVersion = 9.2;
						ProvisioningStyle = Automatic;
					};
				};
198
199
200
201
202
203
204

205
206
207
208
209
210
211

212
213
214
215
216
217
218
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;

				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;

				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;







>







>







198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
253
254
255
256
257
258
259

260
261
262
263
264
265
266

267
268
269
270
271
272
273
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;

				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;

				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;







>







>







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;

Modified tests/objc_sync/Makefile from [050b215f54] to [0df383ed25].

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
include ../../extra.mk

PROG_NOINST = objc_sync${PROG_SUFFIX}
SRCS = test.m

include ../../buildsys.mk

post-all: ${RUN_TESTS}

.PHONY: run
run:
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}

	rm -f objfwrt.dll libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../../src/libobjfw.so; then \
		${LN_S} ../../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/objfw.dll; then \
		${LN_S} ../../src/objfw.dll objfw.dll; \

	fi
	if test -f ../../src/libobjfw.dylib; then \
		${LN_S} ../../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/libobjfwrt.so; then \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/runtime/objfwrt.dll; then \
		${LN_S} ../../src/runtime/objfwrt.dll objfwrt.dll; \

	fi
	if test -f ../../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f objfw.so.${OBJFW_LIB_MAJOR_MINOR} objfw.dll; \

	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f objfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} objfwrt.dll; \

	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

CPPFLAGS += -I../../src -I../../src/runtime -I../..
LIBS := -L../../src -lobjfw -L../../src/runtime ${RUNTIME_LIBS} ${LIBS}


LD = ${OBJC}







<
<




|


>
|









|
|
>













|
|
>














|
>


|
>




|
>
>

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
include ../../extra.mk

PROG_NOINST = objc_sync${PROG_SUFFIX}
SRCS = test.m

include ../../buildsys.mk



.PHONY: run
run:
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw${OBJFW_LIB_MAJOR}.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../../src/libobjfw.so; then \
		${LN_S} ../../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/objfw${OBJFW_LIB_MAJOR}.dll; then \
		${LN_S} ../../src/objfw${OBJFW_LIB_MAJOR}.dll \
			objfw${OBJFW_LIB_MAJOR}.dll; \
	fi
	if test -f ../../src/libobjfw.dylib; then \
		${LN_S} ../../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/libobjfwrt.so; then \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll; then \
		${LN_S} ../../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll \
			objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	fi
	if test -f ../../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	rm -f objfw${OBJFW_LIB_MAJOR}.dll; \
	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

CPPFLAGS += -I../../src -I../../src/runtime -I../..
LIBS := -L../../src -lobjfw						\
	-L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS}	\
	${LIBS}
LD = ${OBJC}

Modified tests/objc_sync/test.m from [c5bc507290] to [63dc58fd99].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *

Modified tests/plugin/TestPlugin.h from [b8f73b745f] to [2f9aa9b796].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFPlugin.h"

@interface TestPlugin: OFPlugin
- (int)test: (int)num;
@end

<
<
|













|

|


1


2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*


 * 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"

@interface TestPlugin: OFObject
- (int)test: (int)num;
@end

Modified tests/plugin/TestPlugin.m from [78de8ec89d] to [5616dbe160].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
{
	Class class = objc_getClass("TestPlugin");

	if (class == Nil)
		/*
		 * musl has broken dlclose(): Instead of calling the destructor
		 * on dlclose(), they call it on exit(). This of course means
		 * that our tests might have already called objc_exit() and the
		 * class is already gone.
		 */
		return;

	objc_unregister_class(class);
}
#endif

@implementation TestPlugin
- (int)test: (int)num
{
	return num * 2;
}
@end

id

init_plugin(void)
{
	return [[[TestPlugin alloc] init] autorelease];
}







|
|



|










<
>
|

|

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
{
	Class class = objc_getClass("TestPlugin");

	if (class == Nil)
		/*
		 * musl has broken dlclose(): Instead of calling the destructor
		 * on dlclose(), they call it on exit(). This of course means
                 * that our tests might have already called objc_deinit() and
                 * the class is already gone.
		 */
		return;

	objc_unregisterClass(class);
}
#endif

@implementation TestPlugin
- (int)test: (int)num
{
	return num * 2;
}
@end


Class
class(void)
{
	return [TestPlugin class];
}

Modified tests/serialization.xml from [c1b84078b7] to [d106910237].

1
2
3






4
5
6
7
8
9
10
<?xml version='1.0' encoding='UTF-8'?>
<serialization xmlns='https://objfw.nil.im/serialization' version='1'>
  <OFMutableDictionary>






    <key>
      <OFString>Blub</OFString>
    </key>
    <object>
      <OFString>B&quot;la</OFString>
    </object>
    <key>



>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version='1.0' encoding='UTF-8'?>
<serialization xmlns='https://objfw.nil.im/serialization' version='1'>
  <OFMutableDictionary>
    <key>
      <OFUUID>01234567-89ab-cdef-fedc-ba9876543210</OFUUID>
    </key>
    <object>
      <OFString>uuid</OFString>
    </object>
    <key>
      <OFString>Blub</OFString>
    </key>
    <object>
      <OFString>B&quot;la</OFString>
    </object>
    <key>

Modified tests/terminal/Makefile from [58c564f0ae] to [e8770b0b9b].

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
include ../../extra.mk

PROG_NOINST = terminal_tests${PROG_SUFFIX}
SRCS = TerminalTests.m

include ../../buildsys.mk

post-all: ${RUN_TESTS}

.PHONY: run
run:
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}

	rm -f objfwrt.dll libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../../src/libobjfw.so; then \
		${LN_S} ../../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/objfw.dll; then \
		${LN_S} ../../src/objfw.dll objfw.dll; \

	fi
	if test -f ../../src/libobjfw.dylib; then \
		${LN_S} ../../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/libobjfwrt.so; then \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/runtime/objfwrt.dll; then \
		${LN_S} ../../src/runtime/objfwrt.dll objfwrt.dll; \

	fi
	if test -f ../../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f objfw.so.${OBJFW_LIB_MAJOR_MINOR} objfw.dll; \

	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f objfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} objfwrt.dll; \

	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

CPPFLAGS += -I../../src -I../../src/exceptions -I../../src/runtime -I../..
LIBS := -L../../src -lobjfw -L../../src/runtime ${RUNTIME_LIBS} ${LIBS}


LD = ${OBJC}







<
<




|


>
|









|
|
>













|
|
>














|
>


|
>




|
>
>

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
include ../../extra.mk

PROG_NOINST = terminal_tests${PROG_SUFFIX}
SRCS = TerminalTests.m

include ../../buildsys.mk



.PHONY: run
run:
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}
	rm -f objfw${OBJFW_LIB_MAJOR}.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	rm -f ${OBJFWRT_AMIGA_LIB}
	if test -f ../../src/libobjfw.so; then \
		${LN_S} ../../src/libobjfw.so libobjfw.so.${OBJFW_LIB_MAJOR}; \
		${LN_S} ../../src/libobjfw.so \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} \
		    libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/objfw${OBJFW_LIB_MAJOR}.dll; then \
		${LN_S} ../../src/objfw${OBJFW_LIB_MAJOR}.dll \
			objfw${OBJFW_LIB_MAJOR}.dll; \
	fi
	if test -f ../../src/libobjfw.dylib; then \
		${LN_S} ../../src/libobjfw.dylib \
		    libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/libobjfwrt.so; then \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
		${LN_S} ../../src/runtime/libobjfwrt.so \
		    libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	elif test -f ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; then \
		${LN_S} ../../src/runtime/libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	fi
	if test -f ../../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll; then \
		${LN_S} ../../src/runtime/objfwrt${OBJFWRT_LIB_MAJOR}.dll \
			objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	fi
	if test -f ../../src/runtime/libobjfwrt.dylib; then \
		${LN_S} ../../src/runtime/libobjfwrt.dylib \
		    libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	fi
	if test -f ../../src/runtime/${OBJFWRT_AMIGA_LIB}; then \
		${LN_S} ../../src/runtime/${OBJFWRT_AMIGA_LIB} \
		    ${OBJFWRT_AMIGA_LIB}; \
	fi
	LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \
	DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \
	LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \
	${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR}; \
	rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR}; \
	rm -f objfw${OBJFW_LIB_MAJOR}.dll; \
	rm -f libobjfw.${OBJFW_LIB_MAJOR}.dylib; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR}; \
	rm -f libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR}; \
	rm -f objfwrt${OBJFWRT_LIB_MAJOR}.dll; \
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

CPPFLAGS += -I../../src -I../../src/exceptions -I../../src/runtime -I../..
LIBS := -L../../src -lobjfw						\
	-L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS}	\
	${LIBS}
LD = ${OBJC}

Modified tests/terminal/TerminalTests.m from [6c2580f32c] to [eb6cacca17].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
	    [OFColor maroon], [OFColor red], [OFColor purple],
	    [OFColor fuchsia], [OFColor green], [OFColor lime], [OFColor olive],
	    [OFColor yellow], [OFColor navy], [OFColor blue], [OFColor teal],
	    [OFColor aqua], nil];
	size_t i;
	OFEnumerator OF_GENERIC(OFColor *) *reverseEnumerator;

	[of_stdout writeFormat: @"%dx%d\n", of_stdout.columns, of_stdout.rows];

	i = 0;
	for (OFColor *color in colors) {
		[of_stdout setForegroundColor: color];
		[of_stdout writeFormat: @"%zx", i++];
	}
	[of_stdout reset];
	[of_stdout writeLine: @"R"];

	i = 0;
	for (OFColor *color in colors) {
		[of_stdout setBackgroundColor: color];
		[of_stdout writeFormat: @"%zx", i++];
	}
	[of_stdout reset];
	[of_stdout writeLine: @"R"];

	i = 0;
	reverseEnumerator = [colors.reversedArray objectEnumerator];
	for (OFColor *color in colors) {
		[of_stdout setForegroundColor: color];
		[of_stdout setBackgroundColor: [reverseEnumerator nextObject]];
		[of_stdout writeFormat: @"%zx", i++];
	}
	[of_stdout reset];
	[of_stdout writeLine: @"R"];

	for (i = 0; i < colors.count * 2; i++) {
		if (i % 2)
			[of_stdout setBackgroundColor: [colors objectAtIndex:
			    ((i / 2) + 2) % colors.count]];
		else
			[of_stdout setForegroundColor:
			    [colors objectAtIndex: i / 2]];

		[of_stdout writeFormat: @"%zx", i / 2];
	}
	[of_stdout reset];
	[of_stdout writeLine: @"R"];

	[of_stdout writeLine: @"Press return"];
	[of_stdin readLine];

	[of_stdout setBackgroundColor: [OFColor green]];
	[of_stdout writeString: @"Hello!"];
	[OFThread sleepForTimeInterval: 2];
	[of_stdout eraseLine];
	[of_stdout writeString: @"World!"];
	[OFThread sleepForTimeInterval: 2];

	[of_stdout clear];
	[OFThread sleepForTimeInterval: 2];

	[of_stdout setCursorPosition: of_point(5, 3)];
	[of_stdout writeString: @"Text at (5, 3)"];
	[OFThread sleepForTimeInterval: 2];

	[of_stdout setRelativeCursorPosition: of_point(-2, 0)];
	[OFThread sleepForTimeInterval: 2];
	[of_stdout setRelativeCursorPosition: of_point(2, 0)];
	[OFThread sleepForTimeInterval: 2];
	[of_stdout setRelativeCursorPosition: of_point(0, -2)];
	[OFThread sleepForTimeInterval: 2];
	[of_stdout setRelativeCursorPosition: of_point(0, 2)];
	[OFThread sleepForTimeInterval: 2];
	[of_stdout setRelativeCursorPosition: of_point(1, 1)];
	[OFThread sleepForTimeInterval: 2];
	[of_stdout setRelativeCursorPosition: of_point(-1, -1)];
	[OFThread sleepForTimeInterval: 2];

	[of_stdout setCursorColumn: 2];
	[OFThread sleepForTimeInterval: 2];

	[of_stdout reset];

	[OFApplication terminate];
}
@end







|



|
|

|
|



|
|

|
|




|
|
|

|
|



|


|


|

|
|

|
|

|
|

|
|


|


|
|


|

|

|

|

|

|


|


|




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
	    [OFColor maroon], [OFColor red], [OFColor purple],
	    [OFColor fuchsia], [OFColor green], [OFColor lime], [OFColor olive],
	    [OFColor yellow], [OFColor navy], [OFColor blue], [OFColor teal],
	    [OFColor aqua], nil];
	size_t i;
	OFEnumerator OF_GENERIC(OFColor *) *reverseEnumerator;

	[OFStdOut writeFormat: @"%dx%d\n", OFStdOut.columns, OFStdOut.rows];

	i = 0;
	for (OFColor *color in colors) {
		[OFStdOut setForegroundColor: color];
		[OFStdOut writeFormat: @"%zx", i++];
	}
	[OFStdOut reset];
	[OFStdOut writeLine: @"R"];

	i = 0;
	for (OFColor *color in colors) {
		[OFStdOut setBackgroundColor: color];
		[OFStdOut writeFormat: @"%zx", i++];
	}
	[OFStdOut reset];
	[OFStdOut writeLine: @"R"];

	i = 0;
	reverseEnumerator = [colors.reversedArray objectEnumerator];
	for (OFColor *color in colors) {
		[OFStdOut setForegroundColor: color];
		[OFStdOut setBackgroundColor: [reverseEnumerator nextObject]];
		[OFStdOut writeFormat: @"%zx", i++];
	}
	[OFStdOut reset];
	[OFStdOut writeLine: @"R"];

	for (i = 0; i < colors.count * 2; i++) {
		if (i % 2)
			[OFStdOut setBackgroundColor: [colors objectAtIndex:
			    ((i / 2) + 2) % colors.count]];
		else
			[OFStdOut setForegroundColor:
			    [colors objectAtIndex: i / 2]];

		[OFStdOut writeFormat: @"%zx", i / 2];
	}
	[OFStdOut reset];
	[OFStdOut writeLine: @"R"];

	[OFStdOut writeLine: @"Press return"];
	[OFStdIn readLine];

	[OFStdOut setBackgroundColor: [OFColor green]];
	[OFStdOut writeString: @"Hello!"];
	[OFThread sleepForTimeInterval: 2];
	[OFStdOut eraseLine];
	[OFStdOut writeString: @"World!"];
	[OFThread sleepForTimeInterval: 2];

	[OFStdOut clear];
	[OFThread sleepForTimeInterval: 2];

	[OFStdOut setCursorPosition: OFPointMake(5, 3)];
	[OFStdOut writeString: @"Text at (5, 3)"];
	[OFThread sleepForTimeInterval: 2];

	[OFStdOut setRelativeCursorPosition: OFPointMake(-2, 0)];
	[OFThread sleepForTimeInterval: 2];
	[OFStdOut setRelativeCursorPosition: OFPointMake(2, 0)];
	[OFThread sleepForTimeInterval: 2];
	[OFStdOut setRelativeCursorPosition: OFPointMake(0, -2)];
	[OFThread sleepForTimeInterval: 2];
	[OFStdOut setRelativeCursorPosition: OFPointMake(0, 2)];
	[OFThread sleepForTimeInterval: 2];
	[OFStdOut setRelativeCursorPosition: OFPointMake(1, 1)];
	[OFThread sleepForTimeInterval: 2];
	[OFStdOut setRelativeCursorPosition: OFPointMake(-1, -1)];
	[OFThread sleepForTimeInterval: 2];

	[OFStdOut setCursorColumn: 2];
	[OFThread sleepForTimeInterval: 2];

	[OFStdOut reset];

	[OFApplication terminate];
}
@end

Modified utils/Makefile from [82be3d0820] to [521b7f89ce].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
include ../extra.mk

SUBDIRS += ${OFARC}	\
	   ${OFDNS}	\
	   ${OFHASH}	\
	   ${OFHTTP}	\
	   ${OFSOCK}	\
	   completions

include ../buildsys.mk

DISTCLEAN = objfw-config

install-extra: objfw-config objfw-compile objfw-new






<







1
2
3
4
5
6

7
8
9
10
11
12
13
include ../extra.mk

SUBDIRS += ${OFARC}	\
	   ${OFDNS}	\
	   ${OFHASH}	\
	   ${OFHTTP}	\

	   completions

include ../buildsys.mk

DISTCLEAN = objfw-config

install-extra: objfw-config objfw-compile objfw-new

Modified utils/objfw-compile from [8cbd1f94ba] to [3a536375c5].

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
#
#  Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
#                2018, 2019, 2020
#    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.
#


<
<
|







1
2


3
4
5
6
7
8
9
10
#!/bin/sh
#


#  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.
#
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
fi

parse_packages() {
	packages=""

	while test x"$1" != "x"; do
		case "$1" in
			--package)
				shift
				packages="$packages --package $1"
				;;
		esac
		shift
	done
}
parse_packages "$@"

show_help() {







|
|
|
|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
fi

parse_packages() {
	packages=""

	while test x"$1" != "x"; do
		case "$1" in
		--package)
			shift
			packages="$packages --package $1"
			;;
		esac
		shift
	done
}
parse_packages "$@"

show_help() {
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
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
lib="no"
plugin="no"
out_prefix=""
out_suffix=""

while test x"$1" != "x"; do
	case "$1" in
		-o|--out)
			shift
			out="$1"
			;;
		--lib)
			if test x"$plugin" = x"yes"; then
				echo "You can't use --lib and --plugin!"
				exit 1
			fi

			shift

			if ! echo "$1" | grep "^[0-9]\+\.[0-9]\+$" >/dev/null
			then
				echo "$1 is not a valid library version!"
				exit 1
			fi

			export LIB_MAJOR="${1%.*}"
			export LIB_MINOR="${1#*.}"

			lib="yes"
			OBJCFLAGS="$OBJCFLAGS $($OBJFW_CONFIG --lib-cflags)"
			out_prefix="$($OBJFW_CONFIG --lib-prefix)"
			out_suffix="$($OBJFW_CONFIG --lib-suffix)"
			;;
		--package)
			# Already included into the flags.
			shift
			;;
		--plugin)
			if test x"$lib" = x"yes"; then
				echo "You can't use --lib and --plugin!"
				exit 1
			fi

			plugin="yes"
			OBJCFLAGS="$OBJCFLAGS $($OBJFW_CONFIG --plugin-cflags)"
			LDFLAGS="$LDFLAGS $($OBJFW_CONFIG --plugin-ldflags)"
			out_suffix="$($OBJFW_CONFIG --plugin-suffix)"
			;;
		--arc)
			OBJCFLAGS="$OBJCFLAGS $($OBJFW_CONFIG --arc)"
			;;
		--builddir)
			shift
			builddir="$1"
			;;
		-D)
			shift
			CPPFLAGS="$CPPFLAGS -D$1"
			;;
		-D*)
			CPPFLAGS="$CPPFLAGS $1"
			;;
		-framework)
			shift
			LIBS="$LIBS -framework $1"
			;;
		-f*)
			OBJCFLAGS="$OBJCFLAGS $1"
			;;
		-F)
			shift
			LIBS="$LIBS -F$1"
			;;
		-F*)
			LIBS="$LIBS $1"
			;;
		-g*)
			OBJCFLAGS="$OBJCFLAGS $1"
			;;
		-I)
			shift
			CPPFLAGS="$CPPFLAGS -I$1"
			;;
		-I*)
			CPPFLAGS="$CPPFLAGS $1"
			;;
		-l)
			shift
			LIBS="$LIBS -l$1"
			;;
		-l*)
			LIBS="$LIBS $1"
			;;
		-L)
			shift
			LIBS="$LIBS -L$1"
			;;
		-L*)
			LIBS="$LIBS $1"
			;;
		-m*)
			OBJCFLAGS="$OBJCFLAGS $1"
			;;
		-O*)
			OBJCFLAGS="$OBJCFLAGS $1"
			;;
		-pthread)
			OBJCFLAGS="$OBJCFLAGS $1"
			LDFLAGS="$LDFLAGS $1"
			;;
		-std=*)
			OBJCFLAGS="$OBJCFLAGS $1"
			;;
		-Wl,*)
			LDFLAGS="$LDFLAGS $1"
			;;
		-W*)
			OBJCFLAGS="$OBJCFLAGS $1"
			;;
		--help)
			show_help
			exit 0
			;;
		-*)
			echo "Unknown option: $1"
			exit 1
			;;
		*.m)
			srcs="$srcs $1"
			;;
		*.mm)
			srcs="$srcs $1"
			link_stdcpp="yes"
			;;
		*)
			echo "Only .m and .mm files can be compiled!" 1>&2
			exit 1
			;;
	esac

	shift
done

if test x"$out" = x""; then
	echo "No output name specified! Use -o or --out!"
	exit 1
fi

case "$builddir" in
	"")
		;;
	*/)
		;;
	*)
		builddir="$builddir/"
		;;
esac

for i in $srcs; do
	case $i in
		*.m)
			if test x"$lib" = x"yes"; then
				obj="$builddir${i%.m}.lib.o"
			elif test x"$plugin" = x"yes"; then
				obj="$builddir${i%.m}.plugin.o"
			else
				obj="$builddir${i%.m}.o"
			fi
			;;
		*.mm)
			if test x"$lib" = x"yes"; then
				obj="$builddir${i%.mm}.lib.o"
			elif test x"$plugin" = x"yes"; then
				obj="$builddir${i%.mm}.plugin.o"
			else
				obj="$builddir${i%.mm}.o"
			fi
			;;
	esac
	objs="$objs $obj"
	build="no"
	deps=$($OBJC -E -M $CPPFLAGS $OBJCFLAGS $i |
		sed 's/.*: //' | sed 's/\\//g')

	if test -f "$obj"; then




		for dep in $deps; do
			test "$dep" -nt $obj && build="yes"
		done
	else
		build="yes"
	fi

	if test x"$build" = x"yes"; then
		link="yes"
		status_compiling $i
		mkdir -p "$(dirname $obj)" || status_compile_failed $i $?
		$OBJC $CPPFLAGS $OBJCFLAGS -c -o $obj $i || \







|
|
|
|
|
|
|
|
|

|

|
<
|
|
|

|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|











|
|
|
|
|
|
|




|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|



<
<

|
>
>
>
>



<
<







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
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
lib="no"
plugin="no"
out_prefix=""
out_suffix=""

while test x"$1" != "x"; do
	case "$1" in
	-o|--out)
		shift
		out="$1"
		;;
	--lib)
		if test x"$plugin" = x"yes"; then
			echo "You can't use --lib and --plugin!"
			exit 1
		fi

		shift

		if ! echo "$1" | grep "^[0-9]\+\.[0-9]\+$" >/dev/null; then

			echo "$1 is not a valid library version!"
			exit 1
		fi

		export LIB_MAJOR="${1%.*}"
		export LIB_MINOR="${1#*.}"

		lib="yes"
		OBJCFLAGS="$OBJCFLAGS $($OBJFW_CONFIG --lib-cflags)"
		out_prefix="$($OBJFW_CONFIG --lib-prefix)"
		out_suffix="$($OBJFW_CONFIG --lib-suffix)"
		;;
	--package)
		# Already included into the flags.
		shift
		;;
	--plugin)
		if test x"$lib" = x"yes"; then
			echo "You can't use --lib and --plugin!"
			exit 1
		fi

		plugin="yes"
		OBJCFLAGS="$OBJCFLAGS $($OBJFW_CONFIG --plugin-cflags)"
		LDFLAGS="$LDFLAGS $($OBJFW_CONFIG --plugin-ldflags)"
		out_suffix="$($OBJFW_CONFIG --plugin-suffix)"
		;;
	--arc)
		OBJCFLAGS="$OBJCFLAGS $($OBJFW_CONFIG --arc)"
		;;
	--builddir)
		shift
		builddir="$1"
		;;
	-D)
		shift
		CPPFLAGS="$CPPFLAGS -D$1"
		;;
	-D*)
		CPPFLAGS="$CPPFLAGS $1"
		;;
	-framework)
		shift
		LIBS="$LIBS -framework $1"
		;;
	-f*)
		OBJCFLAGS="$OBJCFLAGS $1"
		;;
	-F)
		shift
		LIBS="$LIBS -F$1"
		;;
	-F*)
		LIBS="$LIBS $1"
		;;
	-g*)
		OBJCFLAGS="$OBJCFLAGS $1"
		;;
	-I)
		shift
		CPPFLAGS="$CPPFLAGS -I$1"
		;;
	-I*)
		CPPFLAGS="$CPPFLAGS $1"
		;;
	-l)
		shift
		LIBS="$LIBS -l$1"
		;;
	-l*)
		LIBS="$LIBS $1"
		;;
	-L)
		shift
		LIBS="$LIBS -L$1"
		;;
	-L*)
		LIBS="$LIBS $1"
		;;
	-m*)
		OBJCFLAGS="$OBJCFLAGS $1"
		;;
	-O*)
		OBJCFLAGS="$OBJCFLAGS $1"
		;;
	-pthread)
		OBJCFLAGS="$OBJCFLAGS $1"
		LDFLAGS="$LDFLAGS $1"
		;;
	-std=*)
		OBJCFLAGS="$OBJCFLAGS $1"
		;;
	-Wl,*)
		LDFLAGS="$LDFLAGS $1"
		;;
	-W*)
		OBJCFLAGS="$OBJCFLAGS $1"
		;;
	--help)
		show_help
		exit 0
		;;
	-*)
		echo "Unknown option: $1"
		exit 1
		;;
	*.m)
		srcs="$srcs $1"
		;;
	*.mm)
		srcs="$srcs $1"
		link_stdcpp="yes"
		;;
	*)
		echo "Only .m and .mm files can be compiled!" 1>&2
		exit 1
		;;
	esac

	shift
done

if test x"$out" = x""; then
	echo "No output name specified! Use -o or --out!"
	exit 1
fi

case "$builddir" in
"")
	;;
*/)
	;;
*)
	builddir="$builddir/"
	;;
esac

for i in $srcs; do
	case $i in
	*.m)
		if test x"$lib" = x"yes"; then
			obj="$builddir${i%.m}.lib.o"
		elif test x"$plugin" = x"yes"; then
			obj="$builddir${i%.m}.plugin.o"
		else
			obj="$builddir${i%.m}.o"
		fi
		;;
	*.mm)
		if test x"$lib" = x"yes"; then
			obj="$builddir${i%.mm}.lib.o"
		elif test x"$plugin" = x"yes"; then
			obj="$builddir${i%.mm}.plugin.o"
		else
			obj="$builddir${i%.mm}.o"
		fi
		;;
	esac
	objs="$objs $obj"
	build="no"



	if test ! -f "$obj" -o "$i" -nt "$obj"; then
		build="yes"
	else
		deps=$($OBJC -E -M $CPPFLAGS $OBJCFLAGS $i |
			sed 's/.*: //' | sed 's/\\//g')
		for dep in $deps; do
			test "$dep" -nt $obj && build="yes"
		done


	fi

	if test x"$build" = x"yes"; then
		link="yes"
		status_compiling $i
		mkdir -p "$(dirname $obj)" || status_compile_failed $i $?
		$OBJC $CPPFLAGS $OBJCFLAGS -c -o $obj $i || \

Modified utils/objfw-config.in from [c6ea4d4954] to [c7e6aa0918].

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
#
#  Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
#                2018, 2019, 2020
#    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.
#


<
<
|







1
2


3
4
5
6
7
8
9
10
#!/bin/sh
#


#  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.
#
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
	. "$packagesdir/$1.oc"
	set +e
}

parse_packages() {
	while test x"$1" != "x"; do
		case "$1" in
			--package)
				shift
				package_depends_on "$1"
				;;
		esac
		shift
	done
}
parse_packages "$@"

flag_printed="no"
output_flag() {
	if test x"$flag_printed" = x"yes"; then
		printf " %s" "$1"
	else
		printf "%s" "$1"
		flag_printed="yes"
	fi
}

while test x"$1" != "x"; do
	case "$1" in
		--all)
			output_flag "$CFLAGS $CPPFLAGS $CXXFLAGS $OBJCFLAGS"
			output_flag "$LDFLAGS $LDFLAGS_REEXPORT $LDFLAGS_RPATH"
			output_flag "$LIBS"
			;;
		--arc)
			output_flag "-fobjc-arc -fobjc-arc-exceptions"
			;;
		--cflags)
			output_flag "$CFLAGS"
			;;
		--cppflags)
			output_flag "$CPPFLAGS"
			;;
		--cxxflags)
			output_flag "$CXXFLAGS"
			;;
		--framework-libs)
			output_flag "$FRAMEWORK_LIBS"
			;;
		--help)
			show_help 0
			;;
		--objc)
			output_flag "$OBJC"
			;;
		--objcflags)
			output_flag "$OBJCFLAGS"
			;;
		--libs)
			output_flag "$LIBS"
			;;
		--lib-cflags)
			if test x"$LIB_MAJOR" = x"" -o x"$LIB_MINOR" = x""; then
				echo "LIB_MAJOR and LIB_MINOR need to be set!" \
					1>&2
				exit 1
			fi

			output_flag "$LIB_CFLAGS"
			;;
		--lib-ldflags)
			if test x"$SHARED_LIB" = x"" -o x"$LIB_MAJOR" = x"" \
			-o x"$LIB_MINOR" = x""; then
				printf "SHARED_LIB, LIB_MAJOR and " 2>&1
				echo "LIB_MINOR need to be set!" 1>&2
				exit 1
			fi

			output_flag "$LIB_LDFLAGS"
			;;
		--lib-prefix)
			if test x"$LIB_MAJOR" = x"" -o x"$LIB_MINOR" = x""; then
				echo "LIB_MAJOR and LIB_MINOR need to be set!" \
					1>&2
				exit 1
			fi

			output_flag "$LIB_PREFIX"
			;;
		--lib-suffix)
			if test x"$LIB_MAJOR" = x"" -o x"$LIB_MINOR" = x""; then
				echo "LIB_MAJOR and LIB_MINOR need to be set!" \
					1>&2
				exit 1
			fi

			output_flag "$LIB_SUFFIX"
			;;
		--ldflags)
			output_flag "$LDFLAGS"
			;;
		--reexport)
			output_flag "$LDFLAGS_REEXPORT"
			;;
		--rpath)
			output_flag "$LDFLAGS_RPATH"
			;;
		--package)
			# Already included into the flags.
			shift
			;;
		--packages-dir)
			output_flag "$packagesdir"
			;;
		--plugin-cflags)
			output_flag "$PLUGIN_CFLAGS"
			;;
		--plugin-ldflags)
			output_flag "$PLUGIN_LDFLAGS"
			;;
		--plugin-suffix)
			output_flag "$PLUGIN_SUFFIX"
			;;
		--prog-suffix)
			output_flag "$PROG_SUFFIX"
			;;
		--static-libs)
			output_flag "$STATIC_LIBS"
			;;
		--version)
			output_flag "$VERSION"
			;;
		*)
			echo "Invalid option: $1" 1>&2
			exit 1
			;;
	esac
	shift
done

test x"$flag_printed" = x"yes" && echo
exit 0







|
|
|
|


















|
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
|

|
|
|
|
|
|
|
|
|

|
|
|
|
|
<
|
|

|
|
|
|
|
<
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|






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
	. "$packagesdir/$1.oc"
	set +e
}

parse_packages() {
	while test x"$1" != "x"; do
		case "$1" in
		--package)
			shift
			package_depends_on "$1"
			;;
		esac
		shift
	done
}
parse_packages "$@"

flag_printed="no"
output_flag() {
	if test x"$flag_printed" = x"yes"; then
		printf " %s" "$1"
	else
		printf "%s" "$1"
		flag_printed="yes"
	fi
}

while test x"$1" != "x"; do
	case "$1" in
	--all)
		output_flag "$CFLAGS $CPPFLAGS $CXXFLAGS $OBJCFLAGS"
		output_flag "$LDFLAGS $LDFLAGS_REEXPORT $LDFLAGS_RPATH $LIBS"

		;;
	--arc)
		output_flag "-fobjc-arc -fobjc-arc-exceptions"
		;;
	--cflags)
		output_flag "$CFLAGS"
		;;
	--cppflags)
		output_flag "$CPPFLAGS"
		;;
	--cxxflags)
		output_flag "$CXXFLAGS"
		;;
	--framework-libs)
		output_flag "$FRAMEWORK_LIBS"
		;;
	--help)
		show_help 0
		;;
	--objc)
		output_flag "$OBJC"
		;;
	--objcflags)
		output_flag "$OBJCFLAGS"
		;;
	--libs)
		output_flag "$LIBS"
		;;
	--lib-cflags)
		if test x"$LIB_MAJOR" = x"" -o x"$LIB_MINOR" = x""; then
			echo "LIB_MAJOR and LIB_MINOR need to be set!" 1>&2

			exit 1
		fi

		output_flag "$LIB_CFLAGS"
		;;
	--lib-ldflags)
		if test x"$SHARED_LIB" = x"" -o x"$LIB_MAJOR" = x"" \
		    -o x"$LIB_MINOR" = x""; then
			printf "SHARED_LIB, LIB_MAJOR and " 2>&1
			echo "LIB_MINOR need to be set!" 1>&2
			exit 1
		fi

		output_flag "$LIB_LDFLAGS"
		;;
	--lib-prefix)
		if test x"$LIB_MAJOR" = x"" -o x"$LIB_MINOR" = x""; then
			echo "LIB_MAJOR and LIB_MINOR need to be set!" 1>&2

			exit 1
		fi

		output_flag "$LIB_PREFIX"
		;;
	--lib-suffix)
		if test x"$LIB_MAJOR" = x"" -o x"$LIB_MINOR" = x""; then
			echo "LIB_MAJOR and LIB_MINOR need to be set!" 1>&2

			exit 1
		fi

		output_flag "$LIB_SUFFIX"
		;;
	--ldflags)
		output_flag "$LDFLAGS"
		;;
	--reexport)
		output_flag "$LDFLAGS_REEXPORT"
		;;
	--rpath)
		output_flag "$LDFLAGS_RPATH"
		;;
	--package)
		# Already included into the flags.
		shift
		;;
	--packages-dir)
		output_flag "$packagesdir"
		;;
	--plugin-cflags)
		output_flag "$PLUGIN_CFLAGS"
		;;
	--plugin-ldflags)
		output_flag "$PLUGIN_LDFLAGS"
		;;
	--plugin-suffix)
		output_flag "$PLUGIN_SUFFIX"
		;;
	--prog-suffix)
		output_flag "$PROG_SUFFIX"
		;;
	--static-libs)
		output_flag "$STATIC_LIBS"
		;;
	--version)
		output_flag "$VERSION"
		;;
	*)
		echo "Invalid option: $1" 1>&2
		exit 1
		;;
	esac
	shift
done

test x"$flag_printed" = x"yes" && echo
exit 0

Modified utils/objfw-new from [c18da52b55] to [6a6815b394].

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

type="$1"
name="$2"

test -z "$name" && show_help

case "$1" in
	app)
		test -f "$name.m" && already_exists "$name.m"

		cat >"$name.m" <<__EOF__
#import <ObjFW/ObjFW.h>

@interface $name: OFObject <OFApplicationDelegate>
@end

OF_APPLICATION_DELEGATE($name)

@implementation $name
- (void)applicationDidFinishLaunching
{
	[OFApplication terminate];
}
@end
__EOF__
		;;
	class)
		test -f "$name.h" && already_exists "$name.h"
		test -f "$name.m" && already_exists "$name.m"

		cat >"$name.h" <<__EOF__
#import <ObjFW/ObjFW.h>

@interface $name: OFObject
@end
__EOF__
		cat >"$name.m" <<__EOF__
#import "$name.h"

@implementation $name
@end
__EOF__
		;;
	*)
		show_help
		;;
esac







|
|

|














|
|
|
|

|











|
|
|
|

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

type="$1"
name="$2"

test -z "$name" && show_help

case "$1" in
app)
	test -f "$name.m" && already_exists "$name.m"

	cat >"$name.m" <<__EOF__
#import <ObjFW/ObjFW.h>

@interface $name: OFObject <OFApplicationDelegate>
@end

OF_APPLICATION_DELEGATE($name)

@implementation $name
- (void)applicationDidFinishLaunching
{
	[OFApplication terminate];
}
@end
__EOF__
	;;
class)
	test -f "$name.h" && already_exists "$name.h"
	test -f "$name.m" && already_exists "$name.m"

	cat >"$name.h" <<__EOF__
#import <ObjFW/ObjFW.h>

@interface $name: OFObject
@end
__EOF__
		cat >"$name.m" <<__EOF__
#import "$name.h"

@implementation $name
@end
__EOF__
	;;
*)
	show_help
	;;
esac

Modified utils/ofarc/Archive.h from [4953abe2f6] to [c57fe80fee].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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 "OFFile.h"
#import "OFArray.h"

@protocol Archive <OFObject>
+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (of_string_encoding_t)encoding;
- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (of_string_encoding_t)encoding;
- (void)listFiles;
- (void)extractFiles: (OFArray OF_GENERIC(OFString *) *)files;
- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files;
@optional
- (void)addFiles: (OFArray OF_GENERIC(OFString *) *)files;
@end

<
<
|




















|


|






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 "OFObject.h"
#import "OFFile.h"
#import "OFArray.h"

@protocol Archive <OFObject>
+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (OFStringEncoding)encoding;
- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (OFStringEncoding)encoding;
- (void)listFiles;
- (void)extractFiles: (OFArray OF_GENERIC(OFString *) *)files;
- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files;
@optional
- (void)addFiles: (OFArray OF_GENERIC(OFString *) *)files;
@end

Modified utils/ofarc/GZIPArchive.h from [08bf5643f5] to [384191029a].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *

Modified utils/ofarc/GZIPArchive.m from [1991cfed6e] to [2e682cb46c].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
static OFArc *app;

static void
setPermissions(OFString *destination, OFString *source)
{
#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
	OFFileManager *fileManager = [OFFileManager defaultManager];
	of_file_attributes_t attributes =
	    [fileManager attributesOfItemAtPath: source];
	of_file_attribute_key_t key = of_file_attribute_key_posix_permissions;
	of_file_attributes_t destinationAttributes = [OFDictionary
	    dictionaryWithObject: [attributes objectForKey: key]
			  forKey: key];

	[fileManager setAttributes: destinationAttributes
		      ofItemAtPath: destination];
#endif
}

static void
setModificationDate(OFString *path, OFGZIPStream *stream)
{
	OFDate *modificationDate = stream.modificationDate;
	of_file_attributes_t attributes;

	if (modificationDate == nil)
		return;

	attributes = [OFDictionary
	    dictionaryWithObject: modificationDate
			  forKey: of_file_attribute_key_modification_date];
	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
}

@implementation GZIPArchive
+ (void)initialize
{
	if (self == [GZIPArchive class])
		app = (OFArc *)[OFApplication sharedApplication].delegate;
}

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithStream: stream
					mode: mode
				    encoding: encoding] autorelease];
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		_stream = [[OFGZIPStream alloc] initWithStream: stream
							  mode: mode];
	} @catch (id e) {







|
|
|
|












|






|













|








|







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
static OFArc *app;

static void
setPermissions(OFString *destination, OFString *source)
{
#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
	OFFileManager *fileManager = [OFFileManager defaultManager];
	OFFileAttributes attributes = [fileManager
	    attributesOfItemAtPath: source];
	OFFileAttributeKey key = OFFilePOSIXPermissions;
	OFFileAttributes destinationAttributes = [OFDictionary
	    dictionaryWithObject: [attributes objectForKey: key]
			  forKey: key];

	[fileManager setAttributes: destinationAttributes
		      ofItemAtPath: destination];
#endif
}

static void
setModificationDate(OFString *path, OFGZIPStream *stream)
{
	OFDate *modificationDate = stream.modificationDate;
	OFFileAttributes attributes;

	if (modificationDate == nil)
		return;

	attributes = [OFDictionary
	    dictionaryWithObject: modificationDate
			  forKey: OFFileModificationDate];
	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
}

@implementation GZIPArchive
+ (void)initialize
{
	if (self == [GZIPArchive class])
		app = (OFArc *)[OFApplication sharedApplication].delegate;
}

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithStream: stream
					mode: mode
				    encoding: encoding] autorelease];
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_stream = [[OFGZIPStream alloc] initWithStream: stream
							  mode: mode];
	} @catch (id e) {
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
	[_stream release];

	[super dealloc];
}

- (void)listFiles
{
	[of_stderr writeLine: OF_LOCALIZED(@"cannot_list_gz",
	    @"Cannot list files of a .gz archive!")];
	app->_exitStatus = 1;
}

- (void)extractFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFString *fileName;
	OFFile *output;

	if (files.count != 0) {
		[of_stderr writeLine:
		    OF_LOCALIZED(@"cannot_extract_specific_file_from_gz",
		    @"Cannot extract a specific file of a .gz archive!")];
		app->_exitStatus = 1;
		return;
	}

	fileName = app->_archivePath.lastPathComponent
	    .stringByDeletingPathExtension;

	if (app->_outputLevel >= 0)
		[of_stdout writeString: OF_LOCALIZED(@"extracting_file",
		    @"Extracting %[file]...",
		    @"file", fileName)];

	if (![app shouldExtractFile: fileName
			outFileName: fileName])
		return;

	output = [OFFile fileWithPath: fileName
				 mode: @"w"];
	setPermissions(fileName, app->_archivePath);

	while (!_stream.atEndOfStream) {
		ssize_t length = [app copyBlockFromStream: _stream
						 toStream: output
						 fileName: fileName];

		if (length < 0) {
			app->_exitStatus = 1;
			return;
		}
	}

	[output close];
	setModificationDate(fileName, _stream);

	if (app->_outputLevel >= 0) {
		[of_stdout writeString: @"\r"];
		[of_stdout writeLine: OF_LOCALIZED(@"extracting_file_done",
		    @"Extracting %[file]... done",
		    @"file", fileName)];
	}
}

- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFString *fileName = app->_archivePath.lastPathComponent
	    .stringByDeletingPathExtension;

	if (files.count > 0) {
		[of_stderr writeLine: OF_LOCALIZED(
		    @"cannot_print_specific_file_from_gz",
		    @"Cannot print a specific file of a .gz archive!")];
		app->_exitStatus = 1;
		return;
	}

	while (!_stream.atEndOfStream) {
		ssize_t length = [app copyBlockFromStream: _stream
						 toStream: of_stdout
						 fileName: fileName];

		if (length < 0) {
			app->_exitStatus = 1;
			return;
		}
	}
}
@end







|










|










|



|
<


|
<

















|
|











|








|









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
	[_stream release];

	[super dealloc];
}

- (void)listFiles
{
	[OFStdErr writeLine: OF_LOCALIZED(@"cannot_list_gz",
	    @"Cannot list files of a .gz archive!")];
	app->_exitStatus = 1;
}

- (void)extractFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFString *fileName;
	OFFile *output;

	if (files.count != 0) {
		[OFStdErr writeLine:
		    OF_LOCALIZED(@"cannot_extract_specific_file_from_gz",
		    @"Cannot extract a specific file of a .gz archive!")];
		app->_exitStatus = 1;
		return;
	}

	fileName = app->_archivePath.lastPathComponent
	    .stringByDeletingPathExtension;

	if (app->_outputLevel >= 0)
		[OFStdOut writeString: OF_LOCALIZED(@"extracting_file",
		    @"Extracting %[file]...",
		    @"file", fileName)];

	if (![app shouldExtractFile: fileName outFileName: fileName])

		return;

	output = [OFFile fileWithPath: fileName mode: @"w"];

	setPermissions(fileName, app->_archivePath);

	while (!_stream.atEndOfStream) {
		ssize_t length = [app copyBlockFromStream: _stream
						 toStream: output
						 fileName: fileName];

		if (length < 0) {
			app->_exitStatus = 1;
			return;
		}
	}

	[output close];
	setModificationDate(fileName, _stream);

	if (app->_outputLevel >= 0) {
		[OFStdOut writeString: @"\r"];
		[OFStdOut writeLine: OF_LOCALIZED(@"extracting_file_done",
		    @"Extracting %[file]... done",
		    @"file", fileName)];
	}
}

- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFString *fileName = app->_archivePath.lastPathComponent
	    .stringByDeletingPathExtension;

	if (files.count > 0) {
		[OFStdErr writeLine: OF_LOCALIZED(
		    @"cannot_print_specific_file_from_gz",
		    @"Cannot print a specific file of a .gz archive!")];
		app->_exitStatus = 1;
		return;
	}

	while (!_stream.atEndOfStream) {
		ssize_t length = [app copyBlockFromStream: _stream
						 toStream: OFStdOut
						 fileName: fileName];

		if (length < 0) {
			app->_exitStatus = 1;
			return;
		}
	}
}
@end

Modified utils/ofarc/LHAArchive.h from [a8594d02df] to [5ad6942309].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *

Modified utils/ofarc/LHAArchive.m from [da084987c7] to [fb0bc51e00].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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

	if (mode == nil)
		return;

	mode = [OFNumber numberWithUnsignedShort:
	    mode.unsignedShortValue & 0777];

	of_file_attributes_t attributes = [OFDictionary
	    dictionaryWithObject: mode
			  forKey: of_file_attribute_key_posix_permissions];

	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
#endif
}

static void
setModificationDate(OFString *path, OFLHAArchiveEntry *entry)
{
	OFDate *modificationDate = entry.modificationDate;
	of_file_attributes_t attributes;

	if (modificationDate == nil) {
		/*
		 * Fall back to the original date if we have no modification
		 * date, as the modification date is a UNIX extension.
		 */
		modificationDate = entry.date;

		if (modificationDate == nil)
			return;
	}

	attributes = [OFDictionary
	    dictionaryWithObject: modificationDate
			  forKey: of_file_attribute_key_modification_date];
	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
}

@implementation LHAArchive
+ (void)initialize
{
	if (self == [LHAArchive class])
		app = (OFArc *)[OFApplication sharedApplication].delegate;
}

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithStream: stream
					mode: mode
				    encoding: encoding] autorelease];
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		_archive = [[OFLHAArchive alloc] initWithStream: stream
							   mode: mode];

		if (encoding != OF_STRING_ENCODING_AUTODETECT)
			_archive.encoding = encoding;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;







|

|










|














|













|








|







|







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

	if (mode == nil)
		return;

	mode = [OFNumber numberWithUnsignedShort:
	    mode.unsignedShortValue & 0777];

	OFFileAttributes attributes = [OFDictionary
	    dictionaryWithObject: mode
			  forKey: OFFilePOSIXPermissions];

	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
#endif
}

static void
setModificationDate(OFString *path, OFLHAArchiveEntry *entry)
{
	OFDate *modificationDate = entry.modificationDate;
	OFFileAttributes attributes;

	if (modificationDate == nil) {
		/*
		 * Fall back to the original date if we have no modification
		 * date, as the modification date is a UNIX extension.
		 */
		modificationDate = entry.date;

		if (modificationDate == nil)
			return;
	}

	attributes = [OFDictionary
	    dictionaryWithObject: modificationDate
			  forKey: OFFileModificationDate];
	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
}

@implementation LHAArchive
+ (void)initialize
{
	if (self == [LHAArchive class])
		app = (OFArc *)[OFApplication sharedApplication].delegate;
}

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithStream: stream
					mode: mode
				    encoding: encoding] autorelease];
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_archive = [[OFLHAArchive alloc] initWithStream: stream
							   mode: mode];

		if (encoding != OFStringEncodingAutodetect)
			_archive.encoding = encoding;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
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
268
269
- (void)listFiles
{
	OFLHAArchiveEntry *entry;

	while ((entry = [_archive nextEntry]) != nil) {
		void *pool = objc_autoreleasePoolPush();

		[of_stdout writeLine: entry.fileName];

		if (app->_outputLevel >= 1) {
			OFString *date = [entry.date
			    localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"];
			OFString *compressedSize = [OFString stringWithFormat:
			    @"%" PRIu32, entry.compressedSize];
			OFString *uncompressedSize = [OFString stringWithFormat:
			    @"%" PRIu32, entry.uncompressedSize];
			OFString *CRC16 = [OFString stringWithFormat:
			    @"%04" PRIX16, entry.CRC16];

			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_compressed_size",
			    @"["
			    @"    'Compressed: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", compressedSize)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_uncompressed_size",
			    @"["
			    @"    'Uncompressed: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", uncompressedSize)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_compression_method",
			    @"Compression method: %[method]",
			    @"method", entry.compressionMethod)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(@"list_crc16",
			    @"CRC16: %[crc16]",
			    @"crc16", CRC16)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(@"list_date",
			    @"Date: %[date]",
			    @"date", date)];

			if (entry.mode != nil) {
				OFString *modeString = [OFString
				    stringWithFormat:
				    @"%ho", entry.mode.unsignedShortValue];

				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(@"list_mode",
				    @"Mode: %[mode]",
				    @"mode", modeString)];
			}
			if (entry.UID != nil) {
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(@"list_uid",
				    @"UID: %[uid]",
				    @"uid", entry.UID)];
			}
			if (entry.GID != nil) {
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(@"list_gid",
				    @"GID: %[gid]",
				    @"gid", entry.GID)];
			}
			if (entry.owner != nil) {
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_owner",
				    @"Owner: %[owner]",
				    @"owner", entry.owner)];
			}
			if (entry.group != nil) {
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_group",
				    @"Group: %[group]",
				    @"group", entry.group)];
			}

			if (app->_outputLevel >= 2) {
				OFString *headerLevel = [OFString
				    stringWithFormat: @"%" PRIu8,
						      entry.headerLevel];

				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_header_level",
				    @"Header level: %[level]",
				    @"level", headerLevel)];

				if (entry.operatingSystemIdentifier != '\0') {
					OFString *OSID =
					    [OFString stringWithFormat: @"%c",
					    entry.operatingSystemIdentifier];

					[of_stdout writeString: @"\t"];
					[of_stdout writeLine: OF_LOCALIZED(
					    @"list_osid",
					    @"Operating system identifier: "
					    "%[osid]",
					    @"osid", OSID)];
				}

				if (entry.modificationDate != nil) {
					OFString *modificationDate =
					    entry.modificationDate.description;

					[of_stdout writeString: @"\t"];
					[of_stdout writeLine: OF_LOCALIZED(
					    @"list_modification_date",
					    @"Modification date: %[date]",
					    @"date", modificationDate)];
				}
			}

			if (app->_outputLevel >= 3) {
				OFString *extensions =
				    indent(entry.extensions.description);

				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_extensions",
				    @"Extensions: %[extensions]",
				    @"extensions", extensions)];
			}
		}

		objc_autoreleasePoolPop(pool);







|











|
|









|
|









|
|



|
|


|
|








|
|




|
|




|
|




|
|





|
|










|
|









|
|










|
|










|
|







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
- (void)listFiles
{
	OFLHAArchiveEntry *entry;

	while ((entry = [_archive nextEntry]) != nil) {
		void *pool = objc_autoreleasePoolPush();

		[OFStdOut writeLine: entry.fileName];

		if (app->_outputLevel >= 1) {
			OFString *date = [entry.date
			    localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"];
			OFString *compressedSize = [OFString stringWithFormat:
			    @"%" PRIu32, entry.compressedSize];
			OFString *uncompressedSize = [OFString stringWithFormat:
			    @"%" PRIu32, entry.uncompressedSize];
			OFString *CRC16 = [OFString stringWithFormat:
			    @"%04" PRIX16, entry.CRC16];

			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"list_compressed_size",
			    @"["
			    @"    'Compressed: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", compressedSize)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"list_uncompressed_size",
			    @"["
			    @"    'Uncompressed: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", uncompressedSize)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"list_compression_method",
			    @"Compression method: %[method]",
			    @"method", entry.compressionMethod)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(@"list_crc16",
			    @"CRC16: %[crc16]",
			    @"crc16", CRC16)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(@"list_date",
			    @"Date: %[date]",
			    @"date", date)];

			if (entry.mode != nil) {
				OFString *modeString = [OFString
				    stringWithFormat:
				    @"%ho", entry.mode.unsignedShortValue];

				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(@"list_mode",
				    @"Mode: %[mode]",
				    @"mode", modeString)];
			}
			if (entry.UID != nil) {
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(@"list_uid",
				    @"UID: %[uid]",
				    @"uid", entry.UID)];
			}
			if (entry.GID != nil) {
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(@"list_gid",
				    @"GID: %[gid]",
				    @"gid", entry.GID)];
			}
			if (entry.owner != nil) {
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_owner",
				    @"Owner: %[owner]",
				    @"owner", entry.owner)];
			}
			if (entry.group != nil) {
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_group",
				    @"Group: %[group]",
				    @"group", entry.group)];
			}

			if (app->_outputLevel >= 2) {
				OFString *headerLevel = [OFString
				    stringWithFormat: @"%" PRIu8,
						      entry.headerLevel];

				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_header_level",
				    @"Header level: %[level]",
				    @"level", headerLevel)];

				if (entry.operatingSystemIdentifier != '\0') {
					OFString *OSID =
					    [OFString stringWithFormat: @"%c",
					    entry.operatingSystemIdentifier];

					[OFStdOut writeString: @"\t"];
					[OFStdOut writeLine: OF_LOCALIZED(
					    @"list_osid",
					    @"Operating system identifier: "
					    "%[osid]",
					    @"osid", OSID)];
				}

				if (entry.modificationDate != nil) {
					OFString *modificationDate =
					    entry.modificationDate.description;

					[OFStdOut writeString: @"\t"];
					[OFStdOut writeLine: OF_LOCALIZED(
					    @"list_modification_date",
					    @"Modification date: %[date]",
					    @"date", modificationDate)];
				}
			}

			if (app->_outputLevel >= 3) {
				OFString *extensions =
				    indent(entry.extensions.description);

				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_extensions",
				    @"Extensions: %[extensions]",
				    @"extensions", extensions)];
			}
		}

		objc_autoreleasePoolPop(pool);
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
		if (!all && ![files containsObject: fileName])
			continue;

		[missing removeObject: fileName];

		outFileName = [app safeLocalPathForPath: fileName];
		if (outFileName == nil) {
			[of_stderr writeLine: OF_LOCALIZED(
			    @"refusing_to_extract_file",
			    @"Refusing to extract %[file]!",
			    @"file", fileName)];

			app->_exitStatus = 1;
			goto outer_loop_end;
		}

		if (app->_outputLevel >= 0)
			[of_stdout writeString: OF_LOCALIZED(@"extracting_file",
			    @"Extracting %[file]...",
			    @"file", fileName)];

		if ([fileName hasSuffix: @"/"]) {
			[fileManager createDirectoryAtPath: outFileName
					     createParents: true];
			setPermissions(outFileName, entry);
			setModificationDate(outFileName, entry);

			if (app->_outputLevel >= 0) {
				[of_stdout writeString: @"\r"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"extracting_file_done",
				    @"Extracting %[file]... done",
				    @"file", fileName)];
			}

			goto outer_loop_end;
		}

		directory = outFileName.stringByDeletingLastPathComponent;
		if (![fileManager directoryExistsAtPath: directory])
			[fileManager createDirectoryAtPath: directory
					     createParents: true];

		if (![app shouldExtractFile: fileName
				outFileName: outFileName])
			goto outer_loop_end;

		stream = [_archive streamForReadingCurrentEntry];
		output = [OFFile fileWithPath: outFileName
					 mode: @"w"];
		setPermissions(outFileName, entry);

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: output
							 fileName: fileName];








|









|










|
|













|
<



|
<







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
		if (!all && ![files containsObject: fileName])
			continue;

		[missing removeObject: fileName];

		outFileName = [app safeLocalPathForPath: fileName];
		if (outFileName == nil) {
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"refusing_to_extract_file",
			    @"Refusing to extract %[file]!",
			    @"file", fileName)];

			app->_exitStatus = 1;
			goto outer_loop_end;
		}

		if (app->_outputLevel >= 0)
			[OFStdOut writeString: OF_LOCALIZED(@"extracting_file",
			    @"Extracting %[file]...",
			    @"file", fileName)];

		if ([fileName hasSuffix: @"/"]) {
			[fileManager createDirectoryAtPath: outFileName
					     createParents: true];
			setPermissions(outFileName, entry);
			setModificationDate(outFileName, entry);

			if (app->_outputLevel >= 0) {
				[OFStdOut writeString: @"\r"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"extracting_file_done",
				    @"Extracting %[file]... done",
				    @"file", fileName)];
			}

			goto outer_loop_end;
		}

		directory = outFileName.stringByDeletingLastPathComponent;
		if (![fileManager directoryExistsAtPath: directory])
			[fileManager createDirectoryAtPath: directory
					     createParents: true];

		if (![app shouldExtractFile: fileName outFileName: outFileName])

			goto outer_loop_end;

		stream = [_archive streamForReadingCurrentEntry];
		output = [OFFile fileWithPath: outFileName mode: @"w"];

		setPermissions(outFileName, entry);

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: output
							 fileName: fileName];

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
			if (app->_outputLevel >= 0 && percent != newPercent) {
				OFString *percentString;

				percent = newPercent;
				percentString = [OFString stringWithFormat:
				    @"%3u", percent];

				[of_stdout writeString: @"\r"];
				[of_stdout writeString: OF_LOCALIZED(
				    @"extracting_file_percent",
				    @"Extracting %[file]... %[percent]%",
				    @"file", fileName,
				    @"percent", percentString)];
			}
		}

		[output close];
		setModificationDate(outFileName, entry);

		if (app->_outputLevel >= 0) {
			[of_stdout writeString: @"\r"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"extracting_file_done",
			    @"Extracting %[file]... done",
			    @"file", fileName)];
		}

outer_loop_end:
		objc_autoreleasePoolPop(pool);
	}

	if (missing.count > 0) {
		for (OFString *file in missing)
			[of_stderr writeLine: OF_LOCALIZED(
			    @"file_not_in_archive",
			    @"File %[file] is not in the archive!",
			    @"file", file)];

		app->_exitStatus = 1;
	}
}

- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files_
{
	OFMutableSet *files;
	OFLHAArchiveEntry *entry;

	if (files_.count < 1) {
		[of_stderr writeLine: OF_LOCALIZED(@"print_no_file_specified",
		    @"Need one or more files to print!")];
		app->_exitStatus = 1;
		return;
	}

	files = [OFMutableSet setWithArray: files_];

	while ((entry = [_archive nextEntry]) != nil) {
		OFString *fileName = entry.fileName;
		OFStream *stream;

		if (![files containsObject: fileName])
			continue;

		stream = [_archive streamForReadingCurrentEntry];

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: of_stdout
							 fileName: fileName];

			if (length < 0) {
				app->_exitStatus = 1;
				return;
			}
		}

		[files removeObject: fileName];
		[stream close];

		if (files.count == 0)
			break;
	}

	for (OFString *file in files) {
		[of_stderr writeLine: OF_LOCALIZED(@"file_not_in_archive",
		    @"File %[file] is not in the archive!",
		    @"file", file)];
		app->_exitStatus = 1;
	}
}

- (void)addFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFFileManager *fileManager = [OFFileManager defaultManager];

	if (files.count < 1) {
		[of_stderr writeLine: OF_LOCALIZED(@"add_no_file_specified",
		    @"Need one or more files to add!")];
		app->_exitStatus = 1;
		return;
	}

	for (OFString *fileName in files) {
		void *pool = objc_autoreleasePoolPush();
		of_file_attributes_t attributes;
		of_file_type_t type;
		OFMutableLHAArchiveEntry *entry;
		OFStream *output;

		if (app->_outputLevel >= 0)
			[of_stdout writeString: OF_LOCALIZED(@"adding_file",
			    @"Adding %[file]...",
			    @"file", fileName)];

		attributes = [fileManager attributesOfItemAtPath: fileName];
		type = attributes.fileType;
		entry = [OFMutableLHAArchiveEntry entryWithFileName: fileName];

#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
		entry.mode = [OFNumber numberWithUnsignedLong:
		    attributes.filePOSIXPermissions];
#endif
		entry.date = attributes.fileModificationDate;

#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
		entry.UID =
		    [OFNumber numberWithUnsignedLong: attributes.filePOSIXUID];
		entry.GID =
		    [OFNumber numberWithUnsignedLong: attributes.filePOSIXGID];
		entry.owner = attributes.fileOwner;
		entry.group = attributes.fileGroup;
#endif

		if ([type isEqual: of_file_type_directory])
			entry.compressionMethod = @"-lhd-";






		output = [_archive streamForWritingEntry: entry];

		if ([type isEqual: of_file_type_regular]) {
			unsigned long long written = 0;
			unsigned long long size = attributes.fileSize;
			int8_t percent = -1, newPercent;

			OFFile *input = [OFFile fileWithPath: fileName
							mode: @"r"];








|
|











|
|











|














|


















|
















|











|







|
|




|














|
|
|
|
|
|


|

>
>
>
>
>



|







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
			if (app->_outputLevel >= 0 && percent != newPercent) {
				OFString *percentString;

				percent = newPercent;
				percentString = [OFString stringWithFormat:
				    @"%3u", percent];

				[OFStdOut writeString: @"\r"];
				[OFStdOut writeString: OF_LOCALIZED(
				    @"extracting_file_percent",
				    @"Extracting %[file]... %[percent]%",
				    @"file", fileName,
				    @"percent", percentString)];
			}
		}

		[output close];
		setModificationDate(outFileName, entry);

		if (app->_outputLevel >= 0) {
			[OFStdOut writeString: @"\r"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"extracting_file_done",
			    @"Extracting %[file]... done",
			    @"file", fileName)];
		}

outer_loop_end:
		objc_autoreleasePoolPop(pool);
	}

	if (missing.count > 0) {
		for (OFString *file in missing)
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"file_not_in_archive",
			    @"File %[file] is not in the archive!",
			    @"file", file)];

		app->_exitStatus = 1;
	}
}

- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files_
{
	OFMutableSet *files;
	OFLHAArchiveEntry *entry;

	if (files_.count < 1) {
		[OFStdErr writeLine: OF_LOCALIZED(@"print_no_file_specified",
		    @"Need one or more files to print!")];
		app->_exitStatus = 1;
		return;
	}

	files = [OFMutableSet setWithArray: files_];

	while ((entry = [_archive nextEntry]) != nil) {
		OFString *fileName = entry.fileName;
		OFStream *stream;

		if (![files containsObject: fileName])
			continue;

		stream = [_archive streamForReadingCurrentEntry];

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: OFStdOut
							 fileName: fileName];

			if (length < 0) {
				app->_exitStatus = 1;
				return;
			}
		}

		[files removeObject: fileName];
		[stream close];

		if (files.count == 0)
			break;
	}

	for (OFString *file in files) {
		[OFStdErr writeLine: OF_LOCALIZED(@"file_not_in_archive",
		    @"File %[file] is not in the archive!",
		    @"file", file)];
		app->_exitStatus = 1;
	}
}

- (void)addFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFFileManager *fileManager = [OFFileManager defaultManager];

	if (files.count < 1) {
		[OFStdErr writeLine: OF_LOCALIZED(@"add_no_file_specified",
		    @"Need one or more files to add!")];
		app->_exitStatus = 1;
		return;
	}

	for (OFString *fileName in files) {
		void *pool = objc_autoreleasePoolPush();
		OFFileAttributes attributes;
		OFFileAttributeType type;
		OFMutableLHAArchiveEntry *entry;
		OFStream *output;

		if (app->_outputLevel >= 0)
			[OFStdOut writeString: OF_LOCALIZED(@"adding_file",
			    @"Adding %[file]...",
			    @"file", fileName)];

		attributes = [fileManager attributesOfItemAtPath: fileName];
		type = attributes.fileType;
		entry = [OFMutableLHAArchiveEntry entryWithFileName: fileName];

#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
		entry.mode = [OFNumber numberWithUnsignedLong:
		    attributes.filePOSIXPermissions];
#endif
		entry.date = attributes.fileModificationDate;

#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
		entry.UID = [OFNumber numberWithUnsignedLong:
		    attributes.fileOwnerAccountID];
		entry.GID = [OFNumber numberWithUnsignedLong:
		    attributes.fileGroupOwnerAccountID];
		entry.owner = attributes.fileOwnerAccountName;
		entry.group = attributes.fileGroupOwnerAccountName;
#endif

		if ([type isEqual: OFFileTypeDirectory]) {
			entry.compressionMethod = @"-lhd-";

			if (![entry.fileName hasSuffix: @"/"])
				entry.fileName = [entry.fileName
				    stringByAppendingString: @"/"];
		}

		output = [_archive streamForWritingEntry: entry];

		if ([type isEqual: OFFileTypeRegular]) {
			unsigned long long written = 0;
			unsigned long long size = attributes.fileSize;
			int8_t percent = -1, newPercent;

			OFFile *input = [OFFile fileWithPath: fileName
							mode: @"r"];

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
				    percent != newPercent) {
					OFString *percentString;

					percent = newPercent;
					percentString = [OFString
					    stringWithFormat: @"%3u", percent];

					[of_stdout writeString: @"\r"];
					[of_stdout writeString: OF_LOCALIZED(
					    @"adding_file_percent",
					    @"Adding %[file]... %[percent]%",
					    @"file", fileName,
					    @"percent", percentString)];
				}
			}
		}

		if (app->_outputLevel >= 0) {
			[of_stdout writeString: @"\r"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"adding_file_done",
			    @"Adding %[file]... done",
			    @"file", fileName)];
		}

		[output close];








|
|









|
|







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
				    percent != newPercent) {
					OFString *percentString;

					percent = newPercent;
					percentString = [OFString
					    stringWithFormat: @"%3u", percent];

					[OFStdOut writeString: @"\r"];
					[OFStdOut writeString: OF_LOCALIZED(
					    @"adding_file_percent",
					    @"Adding %[file]... %[percent]%",
					    @"file", fileName,
					    @"percent", percentString)];
				}
			}
		}

		if (app->_outputLevel >= 0) {
			[OFStdOut writeString: @"\r"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"adding_file_done",
			    @"Adding %[file]... done",
			    @"file", fileName)];
		}

		[output close];

Modified utils/ofarc/OFArc.h from [0119985fc9] to [926be48eea].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
	OFString *_archivePath;
	int _exitStatus;
}

- (id <Archive>)openArchiveWithPath: (OFString *)path
			       type: (OFString *)type
			       mode: (char)mode
			   encoding: (of_string_encoding_t)encoding;
- (bool)shouldExtractFile: (OFString *)fileName
	      outFileName: (OFString *)outFileName;
- (ssize_t)copyBlockFromStream: (OFStream *)input
		      toStream: (OFStream *)output
		      fileName: (OFString *)fileName;
- (nullable OFString *)safeLocalPathForPath: (OFString *)path;
@end

OF_ASSUME_NONNULL_END







|









35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
	OFString *_archivePath;
	int _exitStatus;
}

- (id <Archive>)openArchiveWithPath: (OFString *)path
			       type: (OFString *)type
			       mode: (char)mode
			   encoding: (OFStringEncoding)encoding;
- (bool)shouldExtractFile: (OFString *)fileName
	      outFileName: (OFString *)outFileName;
- (ssize_t)copyBlockFromStream: (OFStream *)input
		      toStream: (OFStream *)output
		      fileName: (OFString *)fileName;
- (nullable OFString *)safeLocalPathForPath: (OFString *)path;
@end

OF_ASSUME_NONNULL_END

Modified utils/ofarc/OFArc.m from [f2fa0ce091] to [6ddf1e5e68].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFOpenItemFailedException.h"
#import "OFReadFailedException.h"
#import "OFSeekFailedException.h"
#import "OFWriteFailedException.h"

#define BUFFER_SIZE 4096

OF_APPLICATION_DELEGATE(OFArc)

static void
help(OFStream *stream, bool full, int status)
{
	[stream writeLine: OF_LOCALIZED(@"usage",







|







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFOpenItemFailedException.h"
#import "OFReadFailedException.h"
#import "OFSeekFailedException.h"
#import "OFWriteFailedException.h"

#define bufferSize 4096

OF_APPLICATION_DELEGATE(OFArc)

static void
help(OFStream *stream, bool full, int status)
{
	[stream writeLine: OF_LOCALIZED(@"usage",
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
		    @"    -x  --extract     Extract files")];
	}

	[OFApplication terminateWithStatus: status];
}

static void
mutuallyExclusiveError(of_unichar_t shortOption1, OFString *longOption1,
    of_unichar_t shortOption2, OFString *longOption2)
{
	OFString *shortOption1Str = [OFString stringWithFormat: @"%C",
								shortOption1];
	OFString *shortOption2Str = [OFString stringWithFormat: @"%C",
								shortOption2];

	[of_stderr writeLine: OF_LOCALIZED(@"2_options_mutually_exclusive",
	    @"Error: -%[shortopt1] / --%[longopt1] and "
	    @"-%[shortopt2] / --%[longopt2] "
	    @"are mutually exclusive!",
	    @"shortopt1", shortOption1Str,
	    @"longopt1", longOption1,
	    @"shortopt2", shortOption2Str,
	    @"longopt2", longOption2)];
	[OFApplication terminateWithStatus: 1];
}

static void
mutuallyExclusiveError5(of_unichar_t shortOption1, OFString *longOption1,
    of_unichar_t shortOption2, OFString *longOption2,
    of_unichar_t shortOption3, OFString *longOption3,
    of_unichar_t shortOption4, OFString *longOption4,
    of_unichar_t shortOption5, OFString *longOption5)
{
	OFString *shortOption1Str = [OFString stringWithFormat: @"%C",
								shortOption1];
	OFString *shortOption2Str = [OFString stringWithFormat: @"%C",
								shortOption2];
	OFString *shortOption3Str = [OFString stringWithFormat: @"%C",
								shortOption3];
	OFString *shortOption4Str = [OFString stringWithFormat: @"%C",
								shortOption4];
	OFString *shortOption5Str = [OFString stringWithFormat: @"%C",
								shortOption5];

	[of_stderr writeLine: OF_LOCALIZED(@"5_options_mutually_exclusive",
	    @"Error: -%[shortopt1] / --%[longopt1], "
	    @"-%[shortopt2] / --%[longopt2], -%[shortopt3] / --%[longopt3], "
	    @"-%[shortopt4] / --%[longopt4] and\n"
	    @"       -%[shortopt5] / --%[longopt5] are mutually exclusive!",
	    @"shortopt1", shortOption1Str,
	    @"longopt1", longOption1,
	    @"shortopt2", shortOption2Str,
	    @"longopt2", longOption2,
	    @"shortopt3", shortOption3Str,
	    @"longopt3", longOption3,
	    @"shortopt4", shortOption4Str,
	    @"longopt4", longOption4,
	    @"shortopt5", shortOption5Str,
	    @"longopt5", longOption5)];
	[OFApplication terminateWithStatus: 1];
}

static void
writingNotSupported(OFString *type)
{
	[of_stderr writeLine: OF_LOCALIZED(
	    @"writing_not_supported",
	    @"Writing archives of type %[type] is not (yet) supported!",
	    @"type", type)];
}






















@implementation OFArc
- (void)applicationDidFinishLaunching
{
	OFString *outputDir, *encodingString, *type;
	const of_options_parser_option_t options[] = {
		{ 'a', @"append", 0, NULL, NULL },
		{ 'c', @"create", 0, NULL, NULL },
		{ 'C', @"directory", 1, NULL, &outputDir },
		{ 'E', @"encoding", 1, NULL, &encodingString },
		{ 'f', @"force", 0, NULL, NULL },
		{ 'h', @"help", 0, NULL, NULL },
		{ 'l', @"list", 0, NULL, NULL },
		{ 'n', @"no-clobber", 0, NULL, NULL },
		{ 'p', @"print", 0, NULL, NULL },
		{ 'q', @"quiet", 0, NULL, NULL },
		{ 't', @"type", 1, NULL, &type },
		{ 'v', @"verbose", 0, NULL, NULL },
		{ 'x', @"extract", 0, NULL, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	of_unichar_t option, mode = '\0';
	of_string_encoding_t encoding = OF_STRING_ENCODING_AUTODETECT;
	OFOptionsParser *optionsParser;
	OFArray OF_GENERIC(OFString *) *remainingArguments, *files;
	id <Archive> archive;

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [OFSandbox sandbox];
	sandbox.allowsStdIO = true;
	sandbox.allowsReadingFiles = true;
	sandbox.allowsWritingFiles = true;
	sandbox.allowsCreatingFiles = true;
	sandbox.allowsChangingFileAttributes = true;
	sandbox.allowsUserDatabaseReading = true;
	/* Dropped after parsing options */
	sandbox.allowsUnveil = true;

	[OFApplication activateSandbox: sandbox];
#endif

#ifndef OF_AMIGAOS
	[OFLocale addLanguageDirectory: @LANGUAGE_DIR];
#else
	[OFLocale addLanguageDirectory: @"PROGDIR:/share/ofarc/lang"];
#endif







|
|






|











|
|
|
|
|












|




















|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|















|
|















|







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
		    @"    -x  --extract     Extract files")];
	}

	[OFApplication terminateWithStatus: status];
}

static void
mutuallyExclusiveError(OFUnichar shortOption1, OFString *longOption1,
    OFUnichar shortOption2, OFString *longOption2)
{
	OFString *shortOption1Str = [OFString stringWithFormat: @"%C",
								shortOption1];
	OFString *shortOption2Str = [OFString stringWithFormat: @"%C",
								shortOption2];

	[OFStdErr writeLine: OF_LOCALIZED(@"2_options_mutually_exclusive",
	    @"Error: -%[shortopt1] / --%[longopt1] and "
	    @"-%[shortopt2] / --%[longopt2] "
	    @"are mutually exclusive!",
	    @"shortopt1", shortOption1Str,
	    @"longopt1", longOption1,
	    @"shortopt2", shortOption2Str,
	    @"longopt2", longOption2)];
	[OFApplication terminateWithStatus: 1];
}

static void
mutuallyExclusiveError5(OFUnichar shortOption1, OFString *longOption1,
    OFUnichar shortOption2, OFString *longOption2,
    OFUnichar shortOption3, OFString *longOption3,
    OFUnichar shortOption4, OFString *longOption4,
    OFUnichar shortOption5, OFString *longOption5)
{
	OFString *shortOption1Str = [OFString stringWithFormat: @"%C",
								shortOption1];
	OFString *shortOption2Str = [OFString stringWithFormat: @"%C",
								shortOption2];
	OFString *shortOption3Str = [OFString stringWithFormat: @"%C",
								shortOption3];
	OFString *shortOption4Str = [OFString stringWithFormat: @"%C",
								shortOption4];
	OFString *shortOption5Str = [OFString stringWithFormat: @"%C",
								shortOption5];

	[OFStdErr writeLine: OF_LOCALIZED(@"5_options_mutually_exclusive",
	    @"Error: -%[shortopt1] / --%[longopt1], "
	    @"-%[shortopt2] / --%[longopt2], -%[shortopt3] / --%[longopt3], "
	    @"-%[shortopt4] / --%[longopt4] and\n"
	    @"       -%[shortopt5] / --%[longopt5] are mutually exclusive!",
	    @"shortopt1", shortOption1Str,
	    @"longopt1", longOption1,
	    @"shortopt2", shortOption2Str,
	    @"longopt2", longOption2,
	    @"shortopt3", shortOption3Str,
	    @"longopt3", longOption3,
	    @"shortopt4", shortOption4Str,
	    @"longopt4", longOption4,
	    @"shortopt5", shortOption5Str,
	    @"longopt5", longOption5)];
	[OFApplication terminateWithStatus: 1];
}

static void
writingNotSupported(OFString *type)
{
	[OFStdErr writeLine: OF_LOCALIZED(
	    @"writing_not_supported",
	    @"Writing archives of type %[type] is not (yet) supported!",
	    @"type", type)];
}

static void
addFiles(id <Archive> archive, OFArray OF_GENERIC(OFString *) *files)
{
	OFMutableArray *expandedFiles =
	    [OFMutableArray arrayWithCapacity: files.count];
	OFFileManager *fileManager = [OFFileManager defaultManager];

	for (OFString *file in files) {
		OFFileAttributes attributes =
		    [fileManager attributesOfItemAtPath: file];

		if ([attributes.fileType isEqual: OFFileTypeDirectory])
			[expandedFiles addObjectsFromArray: 
			    [fileManager subpathsOfDirectoryAtPath: file]];
		else
			[expandedFiles addObject: file];
	}

	[archive addFiles: expandedFiles];
}

@implementation OFArc
- (void)applicationDidFinishLaunching
{
	OFString *outputDir, *encodingString, *type;
	const OFOptionsParserOption options[] = {
		{ 'a', @"append", 0, NULL, NULL },
		{ 'c', @"create", 0, NULL, NULL },
		{ 'C', @"directory", 1, NULL, &outputDir },
		{ 'E', @"encoding", 1, NULL, &encodingString },
		{ 'f', @"force", 0, NULL, NULL },
		{ 'h', @"help", 0, NULL, NULL },
		{ 'l', @"list", 0, NULL, NULL },
		{ 'n', @"no-clobber", 0, NULL, NULL },
		{ 'p', @"print", 0, NULL, NULL },
		{ 'q', @"quiet", 0, NULL, NULL },
		{ 't', @"type", 1, NULL, &type },
		{ 'v', @"verbose", 0, NULL, NULL },
		{ 'x', @"extract", 0, NULL, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFUnichar option, mode = '\0';
	OFStringEncoding encoding = OFStringEncodingAutodetect;
	OFOptionsParser *optionsParser;
	OFArray OF_GENERIC(OFString *) *remainingArguments, *files;
	id <Archive> archive;

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [OFSandbox sandbox];
	sandbox.allowsStdIO = true;
	sandbox.allowsReadingFiles = true;
	sandbox.allowsWritingFiles = true;
	sandbox.allowsCreatingFiles = true;
	sandbox.allowsChangingFileAttributes = true;
	sandbox.allowsUserDatabaseReading = true;
	/* Dropped after parsing options */
	sandbox.allowsUnveil = true;

	[OFApplication of_activateSandbox: sandbox];
#endif

#ifndef OF_AMIGAOS
	[OFLocale addLanguageDirectory: @LANGUAGE_DIR];
#else
	[OFLocale addLanguageDirectory: @"PROGDIR:/share/ofarc/lang"];
#endif
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
				    'l', @"list",
				    'p', @"print",
				    'x', @"extract");

			mode = option;
			break;
		case 'h':
			help(of_stdout, true, 0);
			break;
		case '=':
			[of_stderr writeLine: OF_LOCALIZED(
			    @"option_takes_no_argument",
			    @"%[prog]: Option --%[opt] takes no argument",
			    @"prog", [OFApplication programName],
			    @"opt", optionsParser.lastLongOption)];

			[OFApplication terminateWithStatus: 1];
			break;
		case ':':
			if (optionsParser.lastLongOption != nil)
				[of_stderr writeLine: OF_LOCALIZED(
				    @"long_option_requires_argument",
				    @"%[prog]: Option --%[opt] requires an "
				    @"argument",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%C",
				    optionsParser.lastOption];
				[of_stderr writeLine: OF_LOCALIZED(
				    @"option_requires_argument",
				    @"%[prog]: Option -%[opt] requires an "
				    @"argument",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if (optionsParser.lastLongOption != nil)
				[of_stderr writeLine: OF_LOCALIZED(
				    @"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%C",
				    optionsParser.lastOption];
				[of_stderr writeLine: OF_LOCALIZED(
				    @"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

	@try {
		if (encodingString != nil)
			encoding = of_string_parse_encoding(encodingString);
	} @catch (OFInvalidArgumentException *e) {
		[of_stderr writeLine: OF_LOCALIZED(
		    @"invalid_encoding",
		    @"%[prog]: Invalid encoding: %[encoding]",
		    @"prog", [OFApplication programName],
		    @"encoding", encodingString)];

		[OFApplication terminateWithStatus: 1];
	}

	remainingArguments = optionsParser.remainingArguments;

	switch (mode) {
	case 'a':
	case 'c':
		if (remainingArguments.count < 1)
			help(of_stderr, false, 1);

		files = [remainingArguments objectsInRange:
		    of_range(1, remainingArguments.count - 1)];

#ifdef OF_HAVE_SANDBOX
		if (![remainingArguments.firstObject isEqual: @"-"])
			[sandbox unveilPath: remainingArguments.firstObject
				permissions: (mode == 'a' ? @"rwc" : @"wc")];

		for (OFString *path in files)
			[sandbox unveilPath: path
				permissions: @"r"];

		sandbox.allowsUnveil = false;
		[OFApplication activateSandbox: sandbox];
#endif

		archive = [self
		    openArchiveWithPath: remainingArguments.firstObject
				   type: type
				   mode: mode
			       encoding: encoding];

		[archive addFiles: files];
		break;
	case 'l':
		if (remainingArguments.count != 1)
			help(of_stderr, false, 1);

#ifdef OF_HAVE_SANDBOX
		if (![remainingArguments.firstObject isEqual: @"-"])
			[sandbox unveilPath: remainingArguments.firstObject
				permissions: @"r"];

		sandbox.allowsUnveil = false;
		[OFApplication activateSandbox: sandbox];
#endif

		archive = [self
		    openArchiveWithPath: remainingArguments.firstObject
				   type: type
				   mode: mode
			       encoding: encoding];

		[archive listFiles];
		break;
	case 'p':
		if (remainingArguments.count < 1)
			help(of_stderr, false, 1);

#ifdef OF_HAVE_SANDBOX
		if (![remainingArguments.firstObject isEqual: @"-"])
			[sandbox unveilPath: remainingArguments.firstObject
				permissions: @"r"];

		sandbox.allowsUnveil = false;
		[OFApplication activateSandbox: sandbox];
#endif

		files = [remainingArguments objectsInRange:
		    of_range(1, remainingArguments.count - 1)];

		archive = [self
		    openArchiveWithPath: remainingArguments.firstObject
				   type: type
				   mode: mode
			       encoding: encoding];

		[archive printFiles: files];
		break;
	case 'x':
		if (remainingArguments.count < 1)
			help(of_stderr, false, 1);

		files = [remainingArguments objectsInRange:
		    of_range(1, remainingArguments.count - 1)];

#ifdef OF_HAVE_SANDBOX
		if (![remainingArguments.firstObject isEqual: @"-"])
			[sandbox unveilPath: remainingArguments.firstObject
				permissions: @"r"];

		if (files.count > 0)
			for (OFString *path in files)
				[sandbox unveilPath: path
					permissions: @"wc"];
		else {
			OFString *path = (outputDir != nil
			    ? outputDir : OF_PATH_CURRENT_DIRECTORY);




			/* We need 'r' to change the directory to it. */
			[sandbox unveilPath: path
				permissions: @"rwc"];
		}

		sandbox.allowsUnveil = false;
		[OFApplication activateSandbox: sandbox];
#endif

		archive = [self
		    openArchiveWithPath: remainingArguments.firstObject
				   type: type
				   mode: mode
			       encoding: encoding];







|


|









|









|











|








|













|

|














|


|







|
<


|








|



|







|












|







|



|











|


|








|
<

|
|
>
>
>
>

|
<



|







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
				    'l', @"list",
				    'p', @"print",
				    'x', @"extract");

			mode = option;
			break;
		case 'h':
			help(OFStdOut, true, 0);
			break;
		case '=':
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"option_takes_no_argument",
			    @"%[prog]: Option --%[opt] takes no argument",
			    @"prog", [OFApplication programName],
			    @"opt", optionsParser.lastLongOption)];

			[OFApplication terminateWithStatus: 1];
			break;
		case ':':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"long_option_requires_argument",
				    @"%[prog]: Option --%[opt] requires an "
				    @"argument",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%C",
				    optionsParser.lastOption];
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"option_requires_argument",
				    @"%[prog]: Option -%[opt] requires an "
				    @"argument",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%C",
				    optionsParser.lastOption];
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

	@try {
		if (encodingString != nil)
			encoding = OFStringEncodingParseName(encodingString);
	} @catch (OFInvalidArgumentException *e) {
		[OFStdErr writeLine: OF_LOCALIZED(
		    @"invalid_encoding",
		    @"%[prog]: Invalid encoding: %[encoding]",
		    @"prog", [OFApplication programName],
		    @"encoding", encodingString)];

		[OFApplication terminateWithStatus: 1];
	}

	remainingArguments = optionsParser.remainingArguments;

	switch (mode) {
	case 'a':
	case 'c':
		if (remainingArguments.count < 1)
			help(OFStdErr, false, 1);

		files = [remainingArguments objectsInRange:
		    OFRangeMake(1, remainingArguments.count - 1)];

#ifdef OF_HAVE_SANDBOX
		if (![remainingArguments.firstObject isEqual: @"-"])
			[sandbox unveilPath: remainingArguments.firstObject
				permissions: (mode == 'a' ? @"rwc" : @"wc")];

		for (OFString *path in files)
			[sandbox unveilPath: path permissions: @"r"];


		sandbox.allowsUnveil = false;
		[OFApplication of_activateSandbox: sandbox];
#endif

		archive = [self
		    openArchiveWithPath: remainingArguments.firstObject
				   type: type
				   mode: mode
			       encoding: encoding];

		addFiles(archive, files);
		break;
	case 'l':
		if (remainingArguments.count != 1)
			help(OFStdErr, false, 1);

#ifdef OF_HAVE_SANDBOX
		if (![remainingArguments.firstObject isEqual: @"-"])
			[sandbox unveilPath: remainingArguments.firstObject
				permissions: @"r"];

		sandbox.allowsUnveil = false;
		[OFApplication of_activateSandbox: sandbox];
#endif

		archive = [self
		    openArchiveWithPath: remainingArguments.firstObject
				   type: type
				   mode: mode
			       encoding: encoding];

		[archive listFiles];
		break;
	case 'p':
		if (remainingArguments.count < 1)
			help(OFStdErr, false, 1);

#ifdef OF_HAVE_SANDBOX
		if (![remainingArguments.firstObject isEqual: @"-"])
			[sandbox unveilPath: remainingArguments.firstObject
				permissions: @"r"];

		sandbox.allowsUnveil = false;
		[OFApplication of_activateSandbox: sandbox];
#endif

		files = [remainingArguments objectsInRange:
		    OFRangeMake(1, remainingArguments.count - 1)];

		archive = [self
		    openArchiveWithPath: remainingArguments.firstObject
				   type: type
				   mode: mode
			       encoding: encoding];

		[archive printFiles: files];
		break;
	case 'x':
		if (remainingArguments.count < 1)
			help(OFStdErr, false, 1);

		files = [remainingArguments objectsInRange:
		    OFRangeMake(1, remainingArguments.count - 1)];

#ifdef OF_HAVE_SANDBOX
		if (![remainingArguments.firstObject isEqual: @"-"])
			[sandbox unveilPath: remainingArguments.firstObject
				permissions: @"r"];

		if (files.count > 0)
			for (OFString *path in files)
				[sandbox unveilPath: path permissions: @"wc"];

		else {
			OFString *path = outputDir;

			if (path == nil)
				path = [[OFFileManager defaultManager]
				    currentDirectoryPath];

			/* We need 'r' to change the directory to it. */
			[sandbox unveilPath: path permissions: @"rwc"];

		}

		sandbox.allowsUnveil = false;
		[OFApplication of_activateSandbox: sandbox];
#endif

		archive = [self
		    openArchiveWithPath: remainingArguments.firstObject
				   type: type
				   mode: mode
			       encoding: encoding];
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

		@try {
			[archive extractFiles: files];
		} @catch (OFCreateDirectoryFailedException *e) {
			OFString *error = [OFString
			    stringWithCString: strerror(e.errNo)
				     encoding: [OFLocale encoding]];
			[of_stderr writeString: @"\r"];
			[of_stderr writeLine: OF_LOCALIZED(
			    @"failed_to_create_directory",
			    @"Failed to create directory %[dir]: %[error]",
			    @"dir", e.URL.fileSystemRepresentation,
			    @"error", error)];
			_exitStatus = 1;
		} @catch (OFOpenItemFailedException *e) {
			OFString *error = [OFString
			    stringWithCString: strerror(e.errNo)
				     encoding: [OFLocale encoding]];
			[of_stderr writeString: @"\r"];
			[of_stderr writeLine: OF_LOCALIZED(
			    @"failed_to_open_file",
			    @"Failed to open file %[file]: %[error]",
			    @"file", e.path,
			    @"error", error)];
			_exitStatus = 1;
		}

		break;
	default:
		help(of_stderr, true, 1);
		break;
	}

	[OFApplication terminateWithStatus: _exitStatus];
}

- (id <Archive>)openArchiveWithPath: (OFString *)path
			       type: (OFString *)type
			       mode: (char)mode
			   encoding: (of_string_encoding_t)encoding
{
	OFString *modeString, *fileModeString;
	OFStream *file = nil;
	id <Archive> archive = nil;

	[_archivePath release];
	_archivePath = [path copy];







|
|









|
|









|









|







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

		@try {
			[archive extractFiles: files];
		} @catch (OFCreateDirectoryFailedException *e) {
			OFString *error = [OFString
			    stringWithCString: strerror(e.errNo)
				     encoding: [OFLocale encoding]];
			[OFStdErr writeString: @"\r"];
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"failed_to_create_directory",
			    @"Failed to create directory %[dir]: %[error]",
			    @"dir", e.URL.fileSystemRepresentation,
			    @"error", error)];
			_exitStatus = 1;
		} @catch (OFOpenItemFailedException *e) {
			OFString *error = [OFString
			    stringWithCString: strerror(e.errNo)
				     encoding: [OFLocale encoding]];
			[OFStdErr writeString: @"\r"];
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"failed_to_open_file",
			    @"Failed to open file %[file]: %[error]",
			    @"file", e.path,
			    @"error", error)];
			_exitStatus = 1;
		}

		break;
	default:
		help(OFStdErr, true, 1);
		break;
	}

	[OFApplication terminateWithStatus: _exitStatus];
}

- (id <Archive>)openArchiveWithPath: (OFString *)path
			       type: (OFString *)type
			       mode: (char)mode
			   encoding: (OFStringEncoding)encoding
{
	OFString *modeString, *fileModeString;
	OFStream *file = nil;
	id <Archive> archive = nil;

	[_archivePath release];
	_archivePath = [path copy];
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
		@throw [OFInvalidArgumentException exception];
	}

	if ([path isEqual: @"-"]) {
		switch (mode) {
		case 'a':
		case 'c':
			file = of_stdout;
			break;
		case 'l':
		case 'p':
		case 'x':
			file = of_stdin;
			break;
		default:
			@throw [OFInvalidArgumentException exception];
		}
	} else {
		@try {
			file = [OFFile fileWithPath: path
					       mode: fileModeString];
		} @catch (OFOpenItemFailedException *e) {
			OFString *error = [OFString
			    stringWithCString: strerror(e.errNo)
				     encoding: [OFLocale encoding]];
			[of_stderr writeString: @"\r"];
			[of_stderr writeLine: OF_LOCALIZED(
			    @"failed_to_open_file",
			    @"Failed to open file %[file]: %[error]",
			    @"file", e.path,
			    @"error", error)];
			[OFApplication terminateWithStatus: 1];
		}
	}







|




|






|
<




|
|







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
		@throw [OFInvalidArgumentException exception];
	}

	if ([path isEqual: @"-"]) {
		switch (mode) {
		case 'a':
		case 'c':
			file = OFStdOut;
			break;
		case 'l':
		case 'p':
		case 'x':
			file = OFStdIn;
			break;
		default:
			@throw [OFInvalidArgumentException exception];
		}
	} else {
		@try {
			file = [OFFile fileWithPath: path mode: fileModeString];

		} @catch (OFOpenItemFailedException *e) {
			OFString *error = [OFString
			    stringWithCString: strerror(e.errNo)
				     encoding: [OFLocale encoding]];
			[OFStdErr writeString: @"\r"];
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"failed_to_open_file",
			    @"Failed to open file %[file]: %[error]",
			    @"file", e.path,
			    @"error", error)];
			[OFApplication terminateWithStatus: 1];
		}
	}
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
							   mode: modeString
						       encoding: encoding];
		} else if ([type isEqual: @"zip"])
			archive = [ZIPArchive archiveWithStream: file
							   mode: modeString
						       encoding: encoding];
		else {
			[of_stderr writeLine: OF_LOCALIZED(
			    @"unknown_archive_type",
			    @"Unknown archive type: %[type]",
			    @"type", type)];
			goto error;
		}
	} @catch (OFNotImplementedException *e) {
		if ((mode == 'a' || mode == 'c') && sel_isEqual(e.selector,
		    @selector(initWithStream:mode:))) {
			writingNotSupported(type);
			goto error;
		}

		@throw e;
	} @catch (OFReadFailedException *e) {
		OFString *error = [OFString
		    stringWithCString: strerror(e.errNo)
			     encoding: [OFLocale encoding]];
		[of_stderr writeLine: OF_LOCALIZED(@"failed_to_read_file",
		    @"Failed to read file %[file]: %[error]",
		    @"file", path,
		    @"error", error)];
		goto error;
	} @catch (OFSeekFailedException *e) {
		OFString *error = [OFString
		    stringWithCString: strerror(e.errNo)
			     encoding: [OFLocale encoding]];
		[of_stderr writeLine: OF_LOCALIZED(@"failed_to_seek_in_file",
		    @"Failed to seek in file %[file]: %[error]",
		    @"file", path,
		    @"error", error)];
		goto error;
	} @catch (OFInvalidFormatException *e) {
		[of_stderr writeLine: OF_LOCALIZED(
		    @"file_is_not_a_valid_archive",
		    @"File %[file] is not a valid archive!",
		    @"file", path)];
		goto error;
	}

	if ((mode == 'a' || mode == 'c') &&







|

















|








|





|







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
							   mode: modeString
						       encoding: encoding];
		} else if ([type isEqual: @"zip"])
			archive = [ZIPArchive archiveWithStream: file
							   mode: modeString
						       encoding: encoding];
		else {
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"unknown_archive_type",
			    @"Unknown archive type: %[type]",
			    @"type", type)];
			goto error;
		}
	} @catch (OFNotImplementedException *e) {
		if ((mode == 'a' || mode == 'c') && sel_isEqual(e.selector,
		    @selector(initWithStream:mode:))) {
			writingNotSupported(type);
			goto error;
		}

		@throw e;
	} @catch (OFReadFailedException *e) {
		OFString *error = [OFString
		    stringWithCString: strerror(e.errNo)
			     encoding: [OFLocale encoding]];
		[OFStdErr writeLine: OF_LOCALIZED(@"failed_to_read_file",
		    @"Failed to read file %[file]: %[error]",
		    @"file", path,
		    @"error", error)];
		goto error;
	} @catch (OFSeekFailedException *e) {
		OFString *error = [OFString
		    stringWithCString: strerror(e.errNo)
			     encoding: [OFLocale encoding]];
		[OFStdErr writeLine: OF_LOCALIZED(@"failed_to_seek_in_file",
		    @"Failed to seek in file %[file]: %[error]",
		    @"file", path,
		    @"error", error)];
		goto error;
	} @catch (OFInvalidFormatException *e) {
		[OFStdErr writeLine: OF_LOCALIZED(
		    @"file_is_not_a_valid_archive",
		    @"File %[file] is not a valid archive!",
		    @"file", path)];
		goto error;
	}

	if ((mode == 'a' || mode == 'c') &&
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

	if (_overwrite == 1 ||
	    ![[OFFileManager defaultManager] fileExistsAtPath: outFileName])
		return true;

	if (_overwrite == -1) {
		if (_outputLevel >= 0) {
			[of_stdout writeString: @" "];
			[of_stdout writeLine:
			    OF_LOCALIZED(@"file_skipped", @"skipped")];
		}
		return false;
	}

	do {
		[of_stderr writeString: @"\r"];
		[of_stderr writeString: OF_LOCALIZED(@"ask_overwrite",
		    @"Overwrite %[file]? [ynAN?]",
		    @"file", fileName)];
		[of_stderr writeString: @" "];

		line = [of_stdin readLine];

		if ([line isEqual: @"?"])
			[of_stderr writeLine: OF_LOCALIZED(
			    @"ask_overwrite_help",
			    @" y: yes\n"
			    @" n: no\n"
			    @" A: always\n"
			    @" N: never")];
	} while (![line isEqual: @"y"] && ![line isEqual: @"n"] &&
	    ![line isEqual: @"N"] && ![line isEqual: @"A"]);

	if ([line isEqual: @"A"])
		_overwrite = 1;
	else if ([line isEqual: @"N"])
		_overwrite = -1;

	if ([line isEqual: @"n"] || [line isEqual: @"N"]) {
		if (_outputLevel >= 0)
			[of_stdout writeLine: OF_LOCALIZED(@"skipping_file",
			    @"Skipping %[file]...",
			    @"file", fileName)];

		return false;
	}

	if (_outputLevel >= 0)
		[of_stdout writeString: OF_LOCALIZED(@"extracting_file",
		    @"Extracting %[file]...",
		    @"file", fileName)];

	return true;
}

- (ssize_t)copyBlockFromStream: (OFStream *)input
		      toStream: (OFStream *)output
		      fileName: (OFString *)fileName
{
	char buffer[BUFFER_SIZE];
	size_t length;

	@try {
		length = [input readIntoBuffer: buffer
					length: BUFFER_SIZE];
	} @catch (OFReadFailedException *e) {
		OFString *error = [OFString
		    stringWithCString: strerror(e.errNo)
			     encoding: [OFLocale encoding]];
		[of_stdout writeString: @"\r"];
		[of_stderr writeLine: OF_LOCALIZED(@"failed_to_read_file",
		    @"Failed to read file %[file]: %[error]",
		    @"file", fileName,
		    @"error", error)];
		return -1;
	}

	@try {
		[output writeBuffer: buffer
			     length: length];
	} @catch (OFWriteFailedException *e) {
		OFString *error = [OFString
		    stringWithCString: strerror(e.errNo)
			     encoding: [OFLocale encoding]];
		[of_stdout writeString: @"\r"];
		[of_stderr writeLine: OF_LOCALIZED(@"failed_to_write_file",
		    @"Failed to write file %[file]: %[error]",
		    @"file", fileName,
		    @"error", error)];
		return -1;
	}

	return length;







|
|






|
|


|

|


|















|







|










|



|
<




|
|







|
<




|
|







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

	if (_overwrite == 1 ||
	    ![[OFFileManager defaultManager] fileExistsAtPath: outFileName])
		return true;

	if (_overwrite == -1) {
		if (_outputLevel >= 0) {
			[OFStdOut writeString: @" "];
			[OFStdOut writeLine:
			    OF_LOCALIZED(@"file_skipped", @"skipped")];
		}
		return false;
	}

	do {
		[OFStdErr writeString: @"\r"];
		[OFStdErr writeString: OF_LOCALIZED(@"ask_overwrite",
		    @"Overwrite %[file]? [ynAN?]",
		    @"file", fileName)];
		[OFStdErr writeString: @" "];

		line = [OFStdIn readLine];

		if ([line isEqual: @"?"])
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"ask_overwrite_help",
			    @" y: yes\n"
			    @" n: no\n"
			    @" A: always\n"
			    @" N: never")];
	} while (![line isEqual: @"y"] && ![line isEqual: @"n"] &&
	    ![line isEqual: @"N"] && ![line isEqual: @"A"]);

	if ([line isEqual: @"A"])
		_overwrite = 1;
	else if ([line isEqual: @"N"])
		_overwrite = -1;

	if ([line isEqual: @"n"] || [line isEqual: @"N"]) {
		if (_outputLevel >= 0)
			[OFStdOut writeLine: OF_LOCALIZED(@"skipping_file",
			    @"Skipping %[file]...",
			    @"file", fileName)];

		return false;
	}

	if (_outputLevel >= 0)
		[OFStdOut writeString: OF_LOCALIZED(@"extracting_file",
		    @"Extracting %[file]...",
		    @"file", fileName)];

	return true;
}

- (ssize_t)copyBlockFromStream: (OFStream *)input
		      toStream: (OFStream *)output
		      fileName: (OFString *)fileName
{
	char buffer[bufferSize];
	size_t length;

	@try {
		length = [input readIntoBuffer: buffer length: bufferSize];

	} @catch (OFReadFailedException *e) {
		OFString *error = [OFString
		    stringWithCString: strerror(e.errNo)
			     encoding: [OFLocale encoding]];
		[OFStdOut writeString: @"\r"];
		[OFStdErr writeLine: OF_LOCALIZED(@"failed_to_read_file",
		    @"Failed to read file %[file]: %[error]",
		    @"file", fileName,
		    @"error", error)];
		return -1;
	}

	@try {
		[output writeBuffer: buffer length: length];

	} @catch (OFWriteFailedException *e) {
		OFString *error = [OFString
		    stringWithCString: strerror(e.errNo)
			     encoding: [OFLocale encoding]];
		[OFStdOut writeString: @"\r"];
		[OFStdErr writeLine: OF_LOCALIZED(@"failed_to_write_file",
		    @"Failed to write file %[file]: %[error]",
		    @"file", fileName,
		    @"error", error)];
		return -1;
	}

	return length;

Modified utils/ofarc/TarArchive.h from [55ced9e062] to [5ee57845a6].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *

Modified utils/ofarc/TarArchive.m from [3ca97fcaef] to [52a1c25d9d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
static OFArc *app;

static void
setPermissions(OFString *path, OFTarArchiveEntry *entry)
{
#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
	OFNumber *mode = [OFNumber numberWithUnsignedShort: entry.mode & 0777];
	of_file_attributes_t attributes = [OFDictionary
	    dictionaryWithObject: mode
			  forKey: of_file_attribute_key_posix_permissions];

	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
#endif
}

static void
setModificationDate(OFString *path, OFTarArchiveEntry *entry)
{
	OFDate *modificationDate = entry.modificationDate;
	of_file_attributes_t attributes;

	if (modificationDate == nil)
		return;

	attributes = [OFDictionary
	    dictionaryWithObject: modificationDate
			  forKey: of_file_attribute_key_modification_date];
	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
}

@implementation TarArchive
+ (void)initialize
{
	if (self == [TarArchive class])
		app = (OFArc *)[OFApplication sharedApplication].delegate;
}

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithStream: stream
					mode: mode
				    encoding: encoding] autorelease];
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		_archive = [[OFTarArchive alloc] initWithStream: stream
							   mode: mode];

		if (encoding != OF_STRING_ENCODING_AUTODETECT)
			_archive.encoding = encoding;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;







|

|










|






|













|








|







|







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
static OFArc *app;

static void
setPermissions(OFString *path, OFTarArchiveEntry *entry)
{
#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
	OFNumber *mode = [OFNumber numberWithUnsignedShort: entry.mode & 0777];
	OFFileAttributes attributes = [OFDictionary
	    dictionaryWithObject: mode
			  forKey: OFFilePOSIXPermissions];

	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
#endif
}

static void
setModificationDate(OFString *path, OFTarArchiveEntry *entry)
{
	OFDate *modificationDate = entry.modificationDate;
	OFFileAttributes attributes;

	if (modificationDate == nil)
		return;

	attributes = [OFDictionary
	    dictionaryWithObject: modificationDate
			  forKey: OFFileModificationDate];
	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
}

@implementation TarArchive
+ (void)initialize
{
	if (self == [TarArchive class])
		app = (OFArc *)[OFApplication sharedApplication].delegate;
}

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithStream: stream
					mode: mode
				    encoding: encoding] autorelease];
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_archive = [[OFTarArchive alloc] initWithStream: stream
							   mode: mode];

		if (encoding != OFStringEncodingAutodetect)
			_archive.encoding = encoding;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
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
- (void)listFiles
{
	OFTarArchiveEntry *entry;

	while ((entry = [_archive nextEntry]) != nil) {
		void *pool = objc_autoreleasePoolPush();

		[of_stdout writeLine: entry.fileName];

		if (app->_outputLevel >= 1) {
			OFString *date = [entry.modificationDate
			    localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"];
			OFString *size = [OFString stringWithFormat:
			    @"%" PRIu64, entry.size];
			OFString *mode = [OFString stringWithFormat:
			    @"%06o", entry.mode];
			OFString *UID = [OFString stringWithFormat:
			    @"%u", entry.UID];
			OFString *GID = [OFString stringWithFormat:
			    @"%u", entry.GID];

			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(@"list_size",
			    @"["
			    @"    'Size: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", size)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(@"list_mode",
			    @"Mode: %[mode]",
			    @"mode", mode)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(@"list_uid",
			    @"UID: %[uid]",
			    @"uid", UID)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(@"list_gid",
			    @"GID: %[gid]",
			    @"gid", GID)];

			if (entry.owner != nil) {
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_owner",
				    @"Owner: %[owner]",
				    @"owner", entry.owner)];
			}
			if (entry.group != nil) {
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_group",
				    @"Group: %[group]",
				    @"group", entry.group)];
			}

			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_modification_date",
			    @"Modification date: %[date]",
			    @"date", date)];
		}

		if (app->_outputLevel >= 2) {
			[of_stdout writeString: @"\t"];

			switch (entry.type) {
			case OF_TAR_ARCHIVE_ENTRY_TYPE_FILE:
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_normal",
				    @"Type: Normal file")];
				break;
			case OF_TAR_ARCHIVE_ENTRY_TYPE_LINK:
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_hardlink",
				    @"Type: Hard link")];
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_link_target",
				    @"Target file name: %[target]",
				    @"target", entry.targetFileName)];
				break;
			case OF_TAR_ARCHIVE_ENTRY_TYPE_SYMLINK:
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_symlink",
				    @"Type: Symbolic link")];
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_link_target",
				    @"Target file name: %[target]",
				    @"target", entry.targetFileName)];
				break;
			case OF_TAR_ARCHIVE_ENTRY_TYPE_CHARACTER_DEVICE: {
				OFString *majorString = [OFString
				    stringWithFormat: @"%d", entry.deviceMajor];
				OFString *minorString = [OFString
				    stringWithFormat: @"%d", entry.deviceMinor];

				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_character_device",
				    @"Type: Character device")];
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_device_major",
				    @"Device major: %[major]",
				    @"major", majorString)];
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_device_minor",
				    @"Device minor: %[minor]",
				    @"minor", minorString)];
				break;
			}
			case OF_TAR_ARCHIVE_ENTRY_TYPE_BLOCK_DEVICE: {
				OFString *majorString = [OFString
				    stringWithFormat: @"%d", entry.deviceMajor];
				OFString *minorString = [OFString
				    stringWithFormat: @"%d", entry.deviceMinor];

				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_block_device",
				    @"Type: Block device")];
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_device_major",
				    @"Device major: %[major]",
				    @"major", majorString)];
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_device_minor",
				    @"Device minor: %[minor]",
				    @"minor", minorString)];
				break;
			}
			case OF_TAR_ARCHIVE_ENTRY_TYPE_DIRECTORY:
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_directory",
				    @"Type: Directory")];
				break;
			case OF_TAR_ARCHIVE_ENTRY_TYPE_FIFO:
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_fifo",
				    @"Type: FIFO")];
				break;
			case OF_TAR_ARCHIVE_ENTRY_TYPE_CONTIGUOUS_FILE:
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_contiguous_file",
				    @"Type: Contiguous file")];
				break;
			default:
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_type_unknown",
				    @"Type: Unknown")];
				break;
			}
		}

		objc_autoreleasePoolPop(pool);







|













|
|








|
|


|
|


|
|




|
|





|
|





|
|






|


|
|



|
|


|
|




|
|


|
|




|





|


|
|



|
|





|





|


|
|



|
|





|
|



|
|



|
|




|







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
- (void)listFiles
{
	OFTarArchiveEntry *entry;

	while ((entry = [_archive nextEntry]) != nil) {
		void *pool = objc_autoreleasePoolPush();

		[OFStdOut writeLine: entry.fileName];

		if (app->_outputLevel >= 1) {
			OFString *date = [entry.modificationDate
			    localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"];
			OFString *size = [OFString stringWithFormat:
			    @"%" PRIu64, entry.size];
			OFString *mode = [OFString stringWithFormat:
			    @"%06o", entry.mode];
			OFString *UID = [OFString stringWithFormat:
			    @"%u", entry.UID];
			OFString *GID = [OFString stringWithFormat:
			    @"%u", entry.GID];

			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(@"list_size",
			    @"["
			    @"    'Size: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", size)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(@"list_mode",
			    @"Mode: %[mode]",
			    @"mode", mode)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(@"list_uid",
			    @"UID: %[uid]",
			    @"uid", UID)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(@"list_gid",
			    @"GID: %[gid]",
			    @"gid", GID)];

			if (entry.owner != nil) {
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_owner",
				    @"Owner: %[owner]",
				    @"owner", entry.owner)];
			}
			if (entry.group != nil) {
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_group",
				    @"Group: %[group]",
				    @"group", entry.group)];
			}

			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"list_modification_date",
			    @"Modification date: %[date]",
			    @"date", date)];
		}

		if (app->_outputLevel >= 2) {
			[OFStdOut writeString: @"\t"];

			switch (entry.type) {
			case OFTarArchiveEntryTypeFile:
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_normal",
				    @"Type: Normal file")];
				break;
			case OFTarArchiveEntryTypeLink:
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_hardlink",
				    @"Type: Hard link")];
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_link_target",
				    @"Target file name: %[target]",
				    @"target", entry.targetFileName)];
				break;
			case OFTarArchiveEntryTypeSymlink:
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_symlink",
				    @"Type: Symbolic link")];
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_link_target",
				    @"Target file name: %[target]",
				    @"target", entry.targetFileName)];
				break;
			case OFTarArchiveEntryTypeCharacterDevice: {
				OFString *majorString = [OFString
				    stringWithFormat: @"%d", entry.deviceMajor];
				OFString *minorString = [OFString
				    stringWithFormat: @"%d", entry.deviceMinor];

				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_character_device",
				    @"Type: Character device")];
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_device_major",
				    @"Device major: %[major]",
				    @"major", majorString)];
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_device_minor",
				    @"Device minor: %[minor]",
				    @"minor", minorString)];
				break;
			}
			case OFTarArchiveEntryTypeBlockDevice: {
				OFString *majorString = [OFString
				    stringWithFormat: @"%d", entry.deviceMajor];
				OFString *minorString = [OFString
				    stringWithFormat: @"%d", entry.deviceMinor];

				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_block_device",
				    @"Type: Block device")];
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_device_major",
				    @"Device major: %[major]",
				    @"major", majorString)];
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_device_minor",
				    @"Device minor: %[minor]",
				    @"minor", minorString)];
				break;
			}
			case OFTarArchiveEntryTypeDirectory:
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_directory",
				    @"Type: Directory")];
				break;
			case OFTarArchiveEntryTypeFIFO:
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_fifo",
				    @"Type: FIFO")];
				break;
			case OFTarArchiveEntryTypeContiguousFile:
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_contiguous_file",
				    @"Type: Contiguous file")];
				break;
			default:
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_type_unknown",
				    @"Type: Unknown")];
				break;
			}
		}

		objc_autoreleasePoolPop(pool);
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
	OFMutableSet OF_GENERIC(OFString *) *missing =
	    [OFMutableSet setWithArray: files];
	OFTarArchiveEntry *entry;

	while ((entry = [_archive nextEntry]) != nil) {
		void *pool = objc_autoreleasePoolPush();
		OFString *fileName = entry.fileName;
		of_tar_archive_entry_type_t type = entry.type;
		OFString *outFileName, *directory;
		OFFile *output;
		OFStream *stream;
		uint64_t written = 0, size = entry.size;
		int8_t percent = -1, newPercent;

		if (!all && ![files containsObject: fileName])
			continue;

		if (type != OF_TAR_ARCHIVE_ENTRY_TYPE_FILE &&
		    type != OF_TAR_ARCHIVE_ENTRY_TYPE_DIRECTORY) {
			if (app->_outputLevel >= 0)
				[of_stdout writeLine: OF_LOCALIZED(
				    @"skipping_file",
				    @"Skipping %[file]...",
				    @"file", fileName)];
			continue;
		}

		[missing removeObject: fileName];

		outFileName = [app safeLocalPathForPath: fileName];
		if (outFileName == nil) {
			[of_stderr writeLine: OF_LOCALIZED(
			    @"refusing_to_extract_file",
			    @"Refusing to extract %[file]!",
			    @"file", fileName)];

			app->_exitStatus = 1;
			goto outer_loop_end;
		}

		if (app->_outputLevel >= 0)
			[of_stdout writeString: OF_LOCALIZED(@"extracting_file",
			    @"Extracting %[file]...",
			    @"file", fileName)];

		if (type == OF_TAR_ARCHIVE_ENTRY_TYPE_DIRECTORY ||
		    (type == OF_TAR_ARCHIVE_ENTRY_TYPE_FILE &&
		    [fileName hasSuffix: @"/"])) {
			[fileManager createDirectoryAtPath: outFileName
					     createParents: true];
			setPermissions(outFileName, entry);
			setModificationDate(outFileName, entry);

			if (app->_outputLevel >= 0) {
				[of_stdout writeString: @"\r"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"extracting_file_done",
				    @"Extracting %[file]... done",
				    @"file", fileName)];
			}

			goto outer_loop_end;
		}

		directory = outFileName.stringByDeletingLastPathComponent;
		if (![fileManager directoryExistsAtPath: directory])
			[fileManager createDirectoryAtPath: directory
					     createParents: true];

		if (![app shouldExtractFile: fileName
				outFileName: outFileName])
			goto outer_loop_end;

		stream = [_archive streamForReadingCurrentEntry];
		output = [OFFile fileWithPath: outFileName
					 mode: @"w"];
		setPermissions(outFileName, entry);

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: output
							 fileName: fileName];








|









|
|

|










|









|



|
|







|
|













|
<



|
<







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
	OFMutableSet OF_GENERIC(OFString *) *missing =
	    [OFMutableSet setWithArray: files];
	OFTarArchiveEntry *entry;

	while ((entry = [_archive nextEntry]) != nil) {
		void *pool = objc_autoreleasePoolPush();
		OFString *fileName = entry.fileName;
		OFTarArchiveEntryType type = entry.type;
		OFString *outFileName, *directory;
		OFFile *output;
		OFStream *stream;
		uint64_t written = 0, size = entry.size;
		int8_t percent = -1, newPercent;

		if (!all && ![files containsObject: fileName])
			continue;

		if (type != OFTarArchiveEntryTypeFile &&
		    type != OFTarArchiveEntryTypeDirectory) {
			if (app->_outputLevel >= 0)
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"skipping_file",
				    @"Skipping %[file]...",
				    @"file", fileName)];
			continue;
		}

		[missing removeObject: fileName];

		outFileName = [app safeLocalPathForPath: fileName];
		if (outFileName == nil) {
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"refusing_to_extract_file",
			    @"Refusing to extract %[file]!",
			    @"file", fileName)];

			app->_exitStatus = 1;
			goto outer_loop_end;
		}

		if (app->_outputLevel >= 0)
			[OFStdOut writeString: OF_LOCALIZED(@"extracting_file",
			    @"Extracting %[file]...",
			    @"file", fileName)];

		if (type == OFTarArchiveEntryTypeDirectory ||
		    (type == OFTarArchiveEntryTypeFile &&
		    [fileName hasSuffix: @"/"])) {
			[fileManager createDirectoryAtPath: outFileName
					     createParents: true];
			setPermissions(outFileName, entry);
			setModificationDate(outFileName, entry);

			if (app->_outputLevel >= 0) {
				[OFStdOut writeString: @"\r"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"extracting_file_done",
				    @"Extracting %[file]... done",
				    @"file", fileName)];
			}

			goto outer_loop_end;
		}

		directory = outFileName.stringByDeletingLastPathComponent;
		if (![fileManager directoryExistsAtPath: directory])
			[fileManager createDirectoryAtPath: directory
					     createParents: true];

		if (![app shouldExtractFile: fileName outFileName: outFileName])

			goto outer_loop_end;

		stream = [_archive streamForReadingCurrentEntry];
		output = [OFFile fileWithPath: outFileName mode: @"w"];

		setPermissions(outFileName, entry);

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: output
							 fileName: fileName];

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
			if (app->_outputLevel >= 0 && percent != newPercent) {
				OFString *percentString;

				percent = newPercent;
				percentString = [OFString stringWithFormat:
				    @"%3u", percent];

				[of_stdout writeString: @"\r"];
				[of_stdout writeString: OF_LOCALIZED(
				    @"extracting_file_percent",
				    @"Extracting %[file]... %[percent]%",
				    @"file", fileName,
				    @"percent", percentString)];
			}
		}

		[output close];
		setModificationDate(outFileName, entry);

		if (app->_outputLevel >= 0) {
			[of_stdout writeString: @"\r"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"extracting_file_done",
			    @"Extracting %[file]... done",
			    @"file", fileName)];
		}

outer_loop_end:
		objc_autoreleasePoolPop(pool);
	}

	if (missing.count > 0) {
		for (OFString *file in missing)
			[of_stderr writeLine: OF_LOCALIZED(
			    @"file_not_in_archive",
			    @"File %[file] is not in the archive!",
			    @"file", file)];

		app->_exitStatus = 1;
	}
}

- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files_
{
	OFMutableSet *files;
	OFTarArchiveEntry *entry;

	if (files_.count < 1) {
		[of_stderr writeLine: OF_LOCALIZED(@"print_no_file_specified",
		    @"Need one or more files to print!")];
		app->_exitStatus = 1;
		return;
	}

	files = [OFMutableSet setWithArray: files_];

	while ((entry = [_archive nextEntry]) != nil) {
		OFString *fileName = entry.fileName;
		OFStream *stream;

		if (![files containsObject: fileName])
			continue;

		stream = [_archive streamForReadingCurrentEntry];

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: of_stdout
							 fileName: fileName];

			if (length < 0) {
				app->_exitStatus = 1;
				return;
			}
		}

		[files removeObject: fileName];
		[stream close];

		if (files.count == 0)
			break;
	}

	for (OFString *file in files) {
		[of_stderr writeLine: OF_LOCALIZED(@"file_not_in_archive",
		    @"File %[file] is not in the archive!",
		    @"file", file)];
		app->_exitStatus = 1;
	}
}

- (void)addFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFFileManager *fileManager = [OFFileManager defaultManager];

	if (files.count < 1) {
		[of_stderr writeLine: OF_LOCALIZED(@"add_no_file_specified",
		    @"Need one or more files to add!")];
		app->_exitStatus = 1;
		return;
	}

	for (OFString *fileName in files) {
		void *pool = objc_autoreleasePoolPush();
		of_file_attributes_t attributes;
		of_file_type_t type;
		OFMutableTarArchiveEntry *entry;
		OFStream *output;

		if (app->_outputLevel >= 0)
			[of_stdout writeString: OF_LOCALIZED(@"adding_file",
			    @"Adding %[file]...",
			    @"file", fileName)];

		attributes = [fileManager attributesOfItemAtPath: fileName];
		type = attributes.fileType;
		entry = [OFMutableTarArchiveEntry entryWithFileName: fileName];

#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
		entry.mode = attributes.filePOSIXPermissions;
#endif
		entry.size = attributes.fileSize;
		entry.modificationDate = attributes.fileModificationDate;

#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
		entry.UID = attributes.filePOSIXUID;
		entry.GID = attributes.filePOSIXGID;
		entry.owner = attributes.fileOwner;
		entry.group = attributes.fileGroup;
#endif

		if ([type isEqual: of_file_type_regular])
			entry.type = OF_TAR_ARCHIVE_ENTRY_TYPE_FILE;
		else if ([type isEqual: of_file_type_directory]) {
			entry.type = OF_TAR_ARCHIVE_ENTRY_TYPE_DIRECTORY;
			entry.size = 0;
		} else if ([type isEqual: of_file_type_symbolic_link]) {
			entry.type = OF_TAR_ARCHIVE_ENTRY_TYPE_SYMLINK;
			entry.targetFileName =
			    attributes.fileSymbolicLinkDestination;
			entry.size = 0;
		}

		[entry makeImmutable];

		output = [_archive streamForWritingEntry: entry];

		if (entry.type == OF_TAR_ARCHIVE_ENTRY_TYPE_FILE) {
			uint64_t written = 0, size = entry.size;
			int8_t percent = -1, newPercent;

			OFFile *input = [OFFile fileWithPath: fileName
							mode: @"r"];

			while (!input.atEndOfStream) {







|
|











|
|











|














|


















|
















|











|







|
|




|














|
|
|
|


|
|
|
|

|
|









|







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
			if (app->_outputLevel >= 0 && percent != newPercent) {
				OFString *percentString;

				percent = newPercent;
				percentString = [OFString stringWithFormat:
				    @"%3u", percent];

				[OFStdOut writeString: @"\r"];
				[OFStdOut writeString: OF_LOCALIZED(
				    @"extracting_file_percent",
				    @"Extracting %[file]... %[percent]%",
				    @"file", fileName,
				    @"percent", percentString)];
			}
		}

		[output close];
		setModificationDate(outFileName, entry);

		if (app->_outputLevel >= 0) {
			[OFStdOut writeString: @"\r"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"extracting_file_done",
			    @"Extracting %[file]... done",
			    @"file", fileName)];
		}

outer_loop_end:
		objc_autoreleasePoolPop(pool);
	}

	if (missing.count > 0) {
		for (OFString *file in missing)
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"file_not_in_archive",
			    @"File %[file] is not in the archive!",
			    @"file", file)];

		app->_exitStatus = 1;
	}
}

- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files_
{
	OFMutableSet *files;
	OFTarArchiveEntry *entry;

	if (files_.count < 1) {
		[OFStdErr writeLine: OF_LOCALIZED(@"print_no_file_specified",
		    @"Need one or more files to print!")];
		app->_exitStatus = 1;
		return;
	}

	files = [OFMutableSet setWithArray: files_];

	while ((entry = [_archive nextEntry]) != nil) {
		OFString *fileName = entry.fileName;
		OFStream *stream;

		if (![files containsObject: fileName])
			continue;

		stream = [_archive streamForReadingCurrentEntry];

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: OFStdOut
							 fileName: fileName];

			if (length < 0) {
				app->_exitStatus = 1;
				return;
			}
		}

		[files removeObject: fileName];
		[stream close];

		if (files.count == 0)
			break;
	}

	for (OFString *file in files) {
		[OFStdErr writeLine: OF_LOCALIZED(@"file_not_in_archive",
		    @"File %[file] is not in the archive!",
		    @"file", file)];
		app->_exitStatus = 1;
	}
}

- (void)addFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFFileManager *fileManager = [OFFileManager defaultManager];

	if (files.count < 1) {
		[OFStdErr writeLine: OF_LOCALIZED(@"add_no_file_specified",
		    @"Need one or more files to add!")];
		app->_exitStatus = 1;
		return;
	}

	for (OFString *fileName in files) {
		void *pool = objc_autoreleasePoolPush();
		OFFileAttributes attributes;
		OFFileAttributeType type;
		OFMutableTarArchiveEntry *entry;
		OFStream *output;

		if (app->_outputLevel >= 0)
			[OFStdOut writeString: OF_LOCALIZED(@"adding_file",
			    @"Adding %[file]...",
			    @"file", fileName)];

		attributes = [fileManager attributesOfItemAtPath: fileName];
		type = attributes.fileType;
		entry = [OFMutableTarArchiveEntry entryWithFileName: fileName];

#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
		entry.mode = attributes.filePOSIXPermissions;
#endif
		entry.size = attributes.fileSize;
		entry.modificationDate = attributes.fileModificationDate;

#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER
		entry.UID = attributes.fileOwnerAccountID;
		entry.GID = attributes.fileGroupOwnerAccountID;
		entry.owner = attributes.fileOwnerAccountName;
		entry.group = attributes.fileGroupOwnerAccountName;
#endif

		if ([type isEqual: OFFileTypeRegular])
			entry.type = OFTarArchiveEntryTypeFile;
		else if ([type isEqual: OFFileTypeDirectory]) {
			entry.type = OFTarArchiveEntryTypeDirectory;
			entry.size = 0;
		} else if ([type isEqual: OFFileTypeSymbolicLink]) {
			entry.type = OFTarArchiveEntryTypeSymlink;
			entry.targetFileName =
			    attributes.fileSymbolicLinkDestination;
			entry.size = 0;
		}

		[entry makeImmutable];

		output = [_archive streamForWritingEntry: entry];

		if (entry.type == OFTarArchiveEntryTypeFile) {
			uint64_t written = 0, size = entry.size;
			int8_t percent = -1, newPercent;

			OFFile *input = [OFFile fileWithPath: fileName
							mode: @"r"];

			while (!input.atEndOfStream) {
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
				    percent != newPercent) {
					OFString *percentString;

					percent = newPercent;
					percentString = [OFString
					    stringWithFormat: @"%3u", percent];

					[of_stdout writeString: @"\r"];
					[of_stdout writeString: OF_LOCALIZED(
					    @"adding_file_percent",
					    @"Adding %[file]... %[percent]%",
					    @"file", fileName,
					    @"percent", percentString)];
				}
			}
		}

		if (app->_outputLevel >= 0) {
			[of_stdout writeString: @"\r"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"adding_file_done",
			    @"Adding %[file]... done",
			    @"file", fileName)];
		}

		[output close];








|
|









|
|







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
				    percent != newPercent) {
					OFString *percentString;

					percent = newPercent;
					percentString = [OFString
					    stringWithFormat: @"%3u", percent];

					[OFStdOut writeString: @"\r"];
					[OFStdOut writeString: OF_LOCALIZED(
					    @"adding_file_percent",
					    @"Adding %[file]... %[percent]%",
					    @"file", fileName,
					    @"percent", percentString)];
				}
			}
		}

		if (app->_outputLevel >= 0) {
			[OFStdOut writeString: @"\r"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"adding_file_done",
			    @"Adding %[file]... done",
			    @"file", fileName)];
		}

		[output close];

Modified utils/ofarc/ZIPArchive.h from [14f409a4c2] to [8566715690].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *

Modified utils/ofarc/ZIPArchive.m from [4b4d3715d0] to [0cdfabe7b0].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
static OFArc *app;

static void
setPermissions(OFString *path, OFZIPArchiveEntry *entry)
{
#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
	if ((entry.versionMadeBy >> 8) ==
	    OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_UNIX) {
		OFNumber *mode = [OFNumber numberWithUnsignedShort:
		    (entry.versionSpecificAttributes >> 16) & 0777];
		of_file_attribute_key_t key =
		    of_file_attribute_key_posix_permissions;
		of_file_attributes_t attributes = [OFDictionary
		    dictionaryWithObject: mode
				  forKey: key];

		[[OFFileManager defaultManager] setAttributes: attributes
						 ofItemAtPath: path];
	}
#endif
}

static void
setModificationDate(OFString *path, OFZIPArchiveEntry *entry)
{
	OFDate *modificationDate = entry.modificationDate;
	of_file_attributes_t attributes;

	if (modificationDate == nil)
		return;

	attributes = [OFDictionary
	    dictionaryWithObject: modificationDate
			  forKey: of_file_attribute_key_modification_date];
	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
}

@implementation ZIPArchive
+ (void)initialize
{
	if (self == [ZIPArchive class])
		app = (OFArc *)[OFApplication sharedApplication].delegate;
}

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithStream: stream
					mode: mode
				    encoding: encoding] autorelease];
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		_archive = [[OFZIPArchive alloc] initWithStream: stream
							   mode: mode];
	} @catch (id e) {







|


<
<
|

|











|






|













|








|







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
static OFArc *app;

static void
setPermissions(OFString *path, OFZIPArchiveEntry *entry)
{
#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
	if ((entry.versionMadeBy >> 8) ==
	    OFZIPArchiveEntryAttributeCompatibilityUNIX) {
		OFNumber *mode = [OFNumber numberWithUnsignedShort:
		    (entry.versionSpecificAttributes >> 16) & 0777];


		OFFileAttributes attributes = [OFDictionary
		    dictionaryWithObject: mode
				  forKey: OFFilePOSIXPermissions];

		[[OFFileManager defaultManager] setAttributes: attributes
						 ofItemAtPath: path];
	}
#endif
}

static void
setModificationDate(OFString *path, OFZIPArchiveEntry *entry)
{
	OFDate *modificationDate = entry.modificationDate;
	OFFileAttributes attributes;

	if (modificationDate == nil)
		return;

	attributes = [OFDictionary
	    dictionaryWithObject: modificationDate
			  forKey: OFFileModificationDate];
	[[OFFileManager defaultManager] setAttributes: attributes
					 ofItemAtPath: path];
}

@implementation ZIPArchive
+ (void)initialize
{
	if (self == [ZIPArchive class])
		app = (OFArc *)[OFApplication sharedApplication].delegate;
}

+ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream
			     mode: (OFString *)mode
			 encoding: (OFStringEncoding)encoding
{
	return [[[self alloc] initWithStream: stream
					mode: mode
				    encoding: encoding] autorelease];
}

- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream
			  mode: (OFString *)mode
		      encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_archive = [[OFZIPArchive alloc] initWithStream: stream
							   mode: mode];
	} @catch (id e) {
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
}

- (void)listFiles
{
	for (OFZIPArchiveEntry *entry in _archive.entries) {
		void *pool = objc_autoreleasePoolPush();

		[of_stdout writeLine: entry.fileName];

		if (app->_outputLevel >= 1) {
			OFString *compressedSize = [OFString
			    stringWithFormat: @"%" PRIu64,
					      entry.compressedSize];
			OFString *uncompressedSize = [OFString
			    stringWithFormat: @"%" PRIu64,
					      entry.uncompressedSize];
			OFString *compressionMethod =
			    of_zip_archive_entry_compression_method_to_string(
			    entry.compressionMethod);
			OFString *CRC32 = [OFString
			    stringWithFormat: @"%08" PRIX32, entry.CRC32];
			OFString *modificationDate = [entry.modificationDate
			    localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"];

			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_compressed_size",
			    @"["
			    @"    'Compressed: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", compressedSize)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_uncompressed_size",
			    @"["
			    @"    'Uncompressed: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", uncompressedSize)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_compression_method",
			    @"Compression method: %[method]",
			    @"method", compressionMethod)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(@"list_crc32",
			    @"CRC32: %[crc32]",
			    @"crc32", CRC32)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_modification_date",
			    @"Modification date: %[date]",
			    @"date", modificationDate)];

			if (app->_outputLevel >= 2) {
				uint16_t versionMadeBy = entry.versionMadeBy;



				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_version_made_by",
				    @"Version made by: %[version]",
				    @"version",
				    of_zip_archive_entry_version_to_string(
				    versionMadeBy))];
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_min_version_needed",
				    @"Minimum version needed: %[version]",
				    @"version",
				    of_zip_archive_entry_version_to_string(
				    entry.minVersionNeeded))];

				if ((versionMadeBy >> 8) ==
				    OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_UNIX) {
					uint32_t mode = entry
					    .versionSpecificAttributes >> 16;
					OFString *modeString = [OFString
					    stringWithFormat: @"%06o", mode];
					[of_stdout writeString: @"\t"];
					[of_stdout writeLine: OF_LOCALIZED(
					    @"list_mode",
					    @"Mode: %[mode]",
					    @"mode", modeString)];
				}
			}

			if (app->_outputLevel >= 3) {
				OFString *GPBF = [OFString stringWithFormat:
				    @"%04" PRIx16, entry.generalPurposeBitFlag];

				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_general_purpose_bit_flag",
				    @"General purpose bit flag: %[gpbf]",
				    @"gpbf", GPBF)];

				if (entry.extraField != nil) {
					[of_stdout writeString: @"\t"];
					[of_stdout writeLine: OF_LOCALIZED(
					    @"list_extra_field",
					    @"Extra field: %[extra]",
					    @"extra",
					    entry.extraField.description)];
				}
			}

			if (entry.fileComment.length > 0) {
				[of_stdout writeString: @"\t"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"list_comment",
				    @"Comment: %[comment]",
				    @"comment", entry.fileComment)];
			}
		}

		objc_autoreleasePoolPop(pool);







|









|






|
|









|
|









|
|



|
|


|
|






>
>

|
|



|

|
|



|


|
<




|
|










|
|





|
|








|
|







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
}

- (void)listFiles
{
	for (OFZIPArchiveEntry *entry in _archive.entries) {
		void *pool = objc_autoreleasePoolPush();

		[OFStdOut writeLine: entry.fileName];

		if (app->_outputLevel >= 1) {
			OFString *compressedSize = [OFString
			    stringWithFormat: @"%" PRIu64,
					      entry.compressedSize];
			OFString *uncompressedSize = [OFString
			    stringWithFormat: @"%" PRIu64,
					      entry.uncompressedSize];
			OFString *compressionMethod =
			    OFZIPArchiveEntryCompressionMethodName(
			    entry.compressionMethod);
			OFString *CRC32 = [OFString
			    stringWithFormat: @"%08" PRIX32, entry.CRC32];
			OFString *modificationDate = [entry.modificationDate
			    localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"];

			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"list_compressed_size",
			    @"["
			    @"    'Compressed: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", compressedSize)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"list_uncompressed_size",
			    @"["
			    @"    'Uncompressed: ',"
			    @"    ["
			    @"        {'size == 1': '1 byte'},"
			    @"        {'': '%[size] bytes'}"
			    @"    ]"
			    @"]".objectByParsingJSON,
			    @"size", uncompressedSize)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"list_compression_method",
			    @"Compression method: %[method]",
			    @"method", compressionMethod)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(@"list_crc32",
			    @"CRC32: %[crc32]",
			    @"crc32", CRC32)];
			[OFStdOut writeString: @"\t"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"list_modification_date",
			    @"Modification date: %[date]",
			    @"date", modificationDate)];

			if (app->_outputLevel >= 2) {
				uint16_t versionMadeBy = entry.versionMadeBy;
				OFZIPArchiveEntryAttributeCompatibility UNIX =
				    OFZIPArchiveEntryAttributeCompatibilityUNIX;

				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_version_made_by",
				    @"Version made by: %[version]",
				    @"version",
				    OFZIPArchiveEntryVersionToString(
				    versionMadeBy))];
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_min_version_needed",
				    @"Minimum version needed: %[version]",
				    @"version",
				    OFZIPArchiveEntryVersionToString(
				    entry.minVersionNeeded))];

				if ((versionMadeBy >> 8) == UNIX) {

					uint32_t mode = entry
					    .versionSpecificAttributes >> 16;
					OFString *modeString = [OFString
					    stringWithFormat: @"%06o", mode];
					[OFStdOut writeString: @"\t"];
					[OFStdOut writeLine: OF_LOCALIZED(
					    @"list_mode",
					    @"Mode: %[mode]",
					    @"mode", modeString)];
				}
			}

			if (app->_outputLevel >= 3) {
				OFString *GPBF = [OFString stringWithFormat:
				    @"%04" PRIx16, entry.generalPurposeBitFlag];

				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_general_purpose_bit_flag",
				    @"General purpose bit flag: %[gpbf]",
				    @"gpbf", GPBF)];

				if (entry.extraField != nil) {
					[OFStdOut writeString: @"\t"];
					[OFStdOut writeLine: OF_LOCALIZED(
					    @"list_extra_field",
					    @"Extra field: %[extra]",
					    @"extra",
					    entry.extraField.description)];
				}
			}

			if (entry.fileComment.length > 0) {
				[OFStdOut writeString: @"\t"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"list_comment",
				    @"Comment: %[comment]",
				    @"comment", entry.fileComment)];
			}
		}

		objc_autoreleasePoolPop(pool);
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
		if (!all && ![files containsObject: fileName])
			continue;

		[missing removeObject: fileName];

		outFileName = [app safeLocalPathForPath: fileName];
		if (outFileName == nil) {
			[of_stderr writeLine: OF_LOCALIZED(
			    @"refusing_to_extract_file",
			    @"Refusing to extract %[file]!",
			    @"file", fileName)];

			app->_exitStatus = 1;
			goto outer_loop_end;
		}

		if (app->_outputLevel >= 0)
			[of_stdout writeString: OF_LOCALIZED(@"extracting_file",
			    @"Extracting %[file]...",
			    @"file", fileName)];

		if ([fileName hasSuffix: @"/"]) {
			[fileManager createDirectoryAtPath: outFileName
					     createParents: true];
			setPermissions(outFileName, entry);
			setModificationDate(outFileName, entry);

			if (app->_outputLevel >= 0) {
				[of_stdout writeString: @"\r"];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"extracting_file_done",
				    @"Extracting %[file]... done",
				    @"file", fileName)];
			}

			goto outer_loop_end;
		}

		directory = outFileName.stringByDeletingLastPathComponent;
		if (![fileManager directoryExistsAtPath: directory])
			[fileManager createDirectoryAtPath: directory
					     createParents: true];

		if (![app shouldExtractFile: fileName
				outFileName: outFileName])
			goto outer_loop_end;

		stream = [_archive streamForReadingFile: fileName];
		output = [OFFile fileWithPath: outFileName
					 mode: @"w"];
		setPermissions(outFileName, entry);

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: output
							 fileName: fileName];








|









|










|
|













|
<



|
<







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
		if (!all && ![files containsObject: fileName])
			continue;

		[missing removeObject: fileName];

		outFileName = [app safeLocalPathForPath: fileName];
		if (outFileName == nil) {
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"refusing_to_extract_file",
			    @"Refusing to extract %[file]!",
			    @"file", fileName)];

			app->_exitStatus = 1;
			goto outer_loop_end;
		}

		if (app->_outputLevel >= 0)
			[OFStdOut writeString: OF_LOCALIZED(@"extracting_file",
			    @"Extracting %[file]...",
			    @"file", fileName)];

		if ([fileName hasSuffix: @"/"]) {
			[fileManager createDirectoryAtPath: outFileName
					     createParents: true];
			setPermissions(outFileName, entry);
			setModificationDate(outFileName, entry);

			if (app->_outputLevel >= 0) {
				[OFStdOut writeString: @"\r"];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"extracting_file_done",
				    @"Extracting %[file]... done",
				    @"file", fileName)];
			}

			goto outer_loop_end;
		}

		directory = outFileName.stringByDeletingLastPathComponent;
		if (![fileManager directoryExistsAtPath: directory])
			[fileManager createDirectoryAtPath: directory
					     createParents: true];

		if (![app shouldExtractFile: fileName outFileName: outFileName])

			goto outer_loop_end;

		stream = [_archive streamForReadingFile: fileName];
		output = [OFFile fileWithPath: outFileName mode: @"w"];

		setPermissions(outFileName, entry);

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: output
							 fileName: fileName];

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
			if (app->_outputLevel >= 0 && percent != newPercent) {
				OFString *percentString;

				percent = newPercent;
				percentString = [OFString stringWithFormat:
				    @"%3u", percent];

				[of_stdout writeString: @"\r"];
				[of_stdout writeString: OF_LOCALIZED(
				    @"extracting_file_percent",
				    @"Extracting %[file]... %[percent]%",
				    @"file", fileName,
				    @"percent", percentString)];
			}
		}

		[output close];
		setModificationDate(outFileName, entry);

		if (app->_outputLevel >= 0) {
			[of_stdout writeString: @"\r"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"extracting_file_done",
			    @"Extracting %[file]... done",
			    @"file", fileName)];
		}

outer_loop_end:
		objc_autoreleasePoolPop(pool);
	}

	if (missing.count > 0) {
		for (OFString *file in missing)
			[of_stderr writeLine: OF_LOCALIZED(
			    @"file_not_in_archive",
			    @"File %[file] is not in the archive!",
			    @"file", file)];

		app->_exitStatus = 1;
	}
}

- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFStream *stream;

	if (files.count < 1) {
		[of_stderr writeLine: OF_LOCALIZED(@"print_no_file_specified",
		    @"Need one or more files to print!")];
		app->_exitStatus = 1;
		return;
	}

	for (OFString *path in files) {
		@try {
			stream = [_archive streamForReadingFile: path];
		} @catch (OFOpenItemFailedException *e) {
			if (e.errNo == ENOENT) {
				[of_stderr writeLine: OF_LOCALIZED(
				    @"file_not_in_archive",
				    @"File %[file] is not in the archive!",
				    @"file", e.path)];
				app->_exitStatus = 1;
				continue;
			}

			@throw e;
		}

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: of_stdout
							 fileName: path];

			if (length < 0) {
				app->_exitStatus = 1;
				return;
			}
		}

		[stream close];
	}
}

- (void)addFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFFileManager *fileManager = [OFFileManager defaultManager];

	if (files.count < 1) {
		[of_stderr writeLine: OF_LOCALIZED(@"add_no_file_specified",
		    @"Need one or more files to add!")];
		app->_exitStatus = 1;
		return;
	}

	for (OFString *localFileName in files) {
		void *pool = objc_autoreleasePoolPush();
		OFArray OF_GENERIC (OFString *) *components;
		OFString *fileName;
		of_file_attributes_t attributes;
		bool isDirectory = false;
		OFMutableZIPArchiveEntry *entry;
		unsigned long long size;
		OFStream *output;

		components = localFileName.pathComponents;
		fileName = [components componentsJoinedByString: @"/"];

		attributes = [fileManager
		    attributesOfItemAtPath: localFileName];

		if ([attributes.fileType isEqual: of_file_type_directory]) {
			isDirectory = true;
			fileName = [fileName stringByAppendingString: @"/"];
		}

		if (app->_outputLevel >= 0)
			[of_stdout writeString: OF_LOCALIZED(@"adding_file",
			    @"Adding %[file]...",
			    @"file", fileName)];

		entry = [OFMutableZIPArchiveEntry entryWithFileName: fileName];

		size = (isDirectory ? 0 : attributes.fileSize);
		if (size > INT64_MAX)
			@throw [OFOutOfRangeException exception];

		entry.compressedSize = (int64_t)size;
		entry.uncompressedSize = (int64_t)size;

		entry.compressionMethod =
		    OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_NONE;
		entry.modificationDate = attributes.fileModificationDate;

		[entry makeImmutable];

		output = [_archive streamForWritingEntry: entry];

		if (!isDirectory) {







|
|











|
|











|













|










|












|

















|









|











|





|













|







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
			if (app->_outputLevel >= 0 && percent != newPercent) {
				OFString *percentString;

				percent = newPercent;
				percentString = [OFString stringWithFormat:
				    @"%3u", percent];

				[OFStdOut writeString: @"\r"];
				[OFStdOut writeString: OF_LOCALIZED(
				    @"extracting_file_percent",
				    @"Extracting %[file]... %[percent]%",
				    @"file", fileName,
				    @"percent", percentString)];
			}
		}

		[output close];
		setModificationDate(outFileName, entry);

		if (app->_outputLevel >= 0) {
			[OFStdOut writeString: @"\r"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"extracting_file_done",
			    @"Extracting %[file]... done",
			    @"file", fileName)];
		}

outer_loop_end:
		objc_autoreleasePoolPop(pool);
	}

	if (missing.count > 0) {
		for (OFString *file in missing)
			[OFStdErr writeLine: OF_LOCALIZED(
			    @"file_not_in_archive",
			    @"File %[file] is not in the archive!",
			    @"file", file)];

		app->_exitStatus = 1;
	}
}

- (void)printFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFStream *stream;

	if (files.count < 1) {
		[OFStdErr writeLine: OF_LOCALIZED(@"print_no_file_specified",
		    @"Need one or more files to print!")];
		app->_exitStatus = 1;
		return;
	}

	for (OFString *path in files) {
		@try {
			stream = [_archive streamForReadingFile: path];
		} @catch (OFOpenItemFailedException *e) {
			if (e.errNo == ENOENT) {
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"file_not_in_archive",
				    @"File %[file] is not in the archive!",
				    @"file", e.path)];
				app->_exitStatus = 1;
				continue;
			}

			@throw e;
		}

		while (!stream.atEndOfStream) {
			ssize_t length = [app copyBlockFromStream: stream
							 toStream: OFStdOut
							 fileName: path];

			if (length < 0) {
				app->_exitStatus = 1;
				return;
			}
		}

		[stream close];
	}
}

- (void)addFiles: (OFArray OF_GENERIC(OFString *) *)files
{
	OFFileManager *fileManager = [OFFileManager defaultManager];

	if (files.count < 1) {
		[OFStdErr writeLine: OF_LOCALIZED(@"add_no_file_specified",
		    @"Need one or more files to add!")];
		app->_exitStatus = 1;
		return;
	}

	for (OFString *localFileName in files) {
		void *pool = objc_autoreleasePoolPush();
		OFArray OF_GENERIC (OFString *) *components;
		OFString *fileName;
		OFFileAttributes attributes;
		bool isDirectory = false;
		OFMutableZIPArchiveEntry *entry;
		unsigned long long size;
		OFStream *output;

		components = localFileName.pathComponents;
		fileName = [components componentsJoinedByString: @"/"];

		attributes = [fileManager
		    attributesOfItemAtPath: localFileName];

		if ([attributes.fileType isEqual: OFFileTypeDirectory]) {
			isDirectory = true;
			fileName = [fileName stringByAppendingString: @"/"];
		}

		if (app->_outputLevel >= 0)
			[OFStdOut writeString: OF_LOCALIZED(@"adding_file",
			    @"Adding %[file]...",
			    @"file", fileName)];

		entry = [OFMutableZIPArchiveEntry entryWithFileName: fileName];

		size = (isDirectory ? 0 : attributes.fileSize);
		if (size > INT64_MAX)
			@throw [OFOutOfRangeException exception];

		entry.compressedSize = (int64_t)size;
		entry.uncompressedSize = (int64_t)size;

		entry.compressionMethod =
		    OFZIPArchiveEntryCompressionMethodNone;
		entry.modificationDate = attributes.fileModificationDate;

		[entry makeImmutable];

		output = [_archive streamForWritingEntry: entry];

		if (!isDirectory) {
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
				    percent != newPercent) {
					OFString *percentString;

					percent = newPercent;
					percentString = [OFString
					    stringWithFormat: @"%3u", percent];

					[of_stdout writeString: @"\r"];
					[of_stdout writeString: OF_LOCALIZED(
					    @"adding_file_percent",
					    @"Adding %[file]... %[percent]%",
					    @"file", fileName,
					    @"percent", percentString)];
				}
			}
		}

		if (app->_outputLevel >= 0) {
			[of_stdout writeString: @"\r"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"adding_file_done",
			    @"Adding %[file]... done",
			    @"file", fileName)];
		}

		[output close];








|
|









|
|







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
				    percent != newPercent) {
					OFString *percentString;

					percent = newPercent;
					percentString = [OFString
					    stringWithFormat: @"%3u", percent];

					[OFStdOut writeString: @"\r"];
					[OFStdOut writeString: OF_LOCALIZED(
					    @"adding_file_percent",
					    @"Adding %[file]... %[percent]%",
					    @"file", fileName,
					    @"percent", percentString)];
				}
			}
		}

		if (app->_outputLevel >= 0) {
			[OFStdOut writeString: @"\r"];
			[OFStdOut writeLine: OF_LOCALIZED(
			    @"adding_file_done",
			    @"Adding %[file]... done",
			    @"file", fileName)];
		}

		[output close];

Modified utils/ofdns/OFDNS.m from [7cbfa15818] to [4408352570].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@end

OF_APPLICATION_DELEGATE(OFDNS)

static void
help(OFStream *stream, bool full, int status)
{
	[of_stderr writeLine:
	    OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] -[chst] domain1 [domain2 ...]",
	    @"prog", [OFApplication programName])];

	if (full) {
		[stream writeString: @"\n"];
		[stream writeLine: OF_LOCALIZED(@"full_usage",







|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@end

OF_APPLICATION_DELEGATE(OFDNS)

static void
help(OFStream *stream, bool full, int status)
{
	[OFStdErr writeLine:
	    OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] -[chst] domain1 [domain2 ...]",
	    @"prog", [OFApplication programName])];

	if (full) {
		[stream writeString: @"\n"];
		[stream writeLine: OF_LOCALIZED(@"full_usage",
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
  didPerformQuery: (OFDNSQuery *)query
	 response: (OFDNSResponse *)response
	exception: (id)exception
{
	_inFlight--;

	if (exception == nil)
		[of_stdout writeFormat: @"%@\n", response];
	else {
		[of_stderr writeLine: OF_LOCALIZED(
		    @"failed_to_resolve",
		    @"Failed to resolve: %[exception]",
		    @"exception", exception)];
		_errors++;
	}

	if (_inFlight == 0)
		[OFApplication terminateWithStatus: _errors];
}

- (void)applicationDidFinishLaunching
{
	OFString *DNSClassString, *server;
	const of_options_parser_option_t options[] = {
		{ 'c', @"class", 1, NULL, &DNSClassString },
		{ 'h', @"help", 0, NULL, NULL },
		{ 's', @"server", 1, NULL, &server },
		{ 't', @"type", 1, NULL, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFMutableArray OF_GENERIC(OFString *) *recordTypes;
	OFOptionsParser *optionsParser;
	of_unichar_t option;
	OFArray OF_GENERIC(OFString *) *remainingArguments;
	OFDNSResolver *resolver;
	of_dns_class_t DNSClass;

#ifdef OF_HAVE_FILES
# ifndef OF_AMIGAOS
	[OFLocale addLanguageDirectory: @LANGUAGE_DIR];
# else
	[OFLocale addLanguageDirectory: @"PROGDIR:/share/ofdns/lang"];
# endif
#endif

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [[OFSandbox alloc] init];
	@try {
		sandbox.allowsStdIO = true;
		sandbox.allowsDNS = true;

		[OFApplication activateSandbox: sandbox];
	} @finally {
		[sandbox release];
	}
#endif

	recordTypes = [OFMutableArray array];

	optionsParser = [OFOptionsParser parserWithOptions: options];
	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case 't':
			[recordTypes addObject: optionsParser.argument];
			break;
		case 'h':
			help(of_stdout, true, 0);
			break;
		case ':':
			if (optionsParser.lastLongOption != nil)
				[of_stderr writeLine: OF_LOCALIZED(
				    @"long_option_required_argument",
				    @"%[prog]: Option --%[opt] requires an "
				    @"argument",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%C",
				    optionsParser.lastOption];
				[of_stderr writeLine: OF_LOCALIZED(
				    @"option_requires_argument",
				    @"%[prog]: Option -%[opt] requires an "
				    @"argument",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if (optionsParser.lastLongOption != nil)
				[of_stderr writeLine: OF_LOCALIZED(
				    @"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%C",
				    optionsParser.lastOption];
				[of_stderr writeLine: OF_LOCALIZED(
				    @"Unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

	remainingArguments = optionsParser.remainingArguments;

	if (remainingArguments.count < 1)
		help(of_stderr, false, 1);

	resolver = [OFDNSResolver resolver];
	DNSClass = (DNSClassString != nil
	    ? of_dns_class_parse(DNSClassString)
	    : OF_DNS_CLASS_IN);

	if (recordTypes.count == 0)
		[recordTypes addObject: @"ALL"];

	if (server != nil) {
		resolver.configReloadInterval = 0;
		resolver.nameServers = [OFArray arrayWithObject: server];
	}

	for (OFString *domainName in remainingArguments) {
		for (OFString *recordTypeString in recordTypes) {
			of_dns_record_type_t recordType =
			    of_dns_record_type_parse(recordTypeString);
			OFDNSQuery *query =
			    [OFDNSQuery queryWithDomainName: domainName
						   DNSClass: DNSClass
						 recordType: recordType];

			_inFlight++;
			[resolver asyncPerformQuery: query
					   delegate: self];
		}
	}
}
@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
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
  didPerformQuery: (OFDNSQuery *)query
	 response: (OFDNSResponse *)response
	exception: (id)exception
{
	_inFlight--;

	if (exception == nil)
		[OFStdOut writeFormat: @"%@\n", response];
	else {
		[OFStdErr writeLine: OF_LOCALIZED(
		    @"failed_to_resolve",
		    @"Failed to resolve: %[exception]",
		    @"exception", exception)];
		_errors++;
	}

	if (_inFlight == 0)
		[OFApplication terminateWithStatus: _errors];
}

- (void)applicationDidFinishLaunching
{
	OFString *DNSClassString, *server;
	const OFOptionsParserOption options[] = {
		{ 'c', @"class", 1, NULL, &DNSClassString },
		{ 'h', @"help", 0, NULL, NULL },
		{ 's', @"server", 1, NULL, &server },
		{ 't', @"type", 1, NULL, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFMutableArray OF_GENERIC(OFString *) *recordTypes;
	OFOptionsParser *optionsParser;
	OFUnichar option;
	OFArray OF_GENERIC(OFString *) *remainingArguments;
	OFDNSResolver *resolver;
	OFDNSClass DNSClass;

#ifdef OF_HAVE_FILES
# ifndef OF_AMIGAOS
	[OFLocale addLanguageDirectory: @LANGUAGE_DIR];
# else
	[OFLocale addLanguageDirectory: @"PROGDIR:/share/ofdns/lang"];
# endif
#endif

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [[OFSandbox alloc] init];
	@try {
		sandbox.allowsStdIO = true;
		sandbox.allowsDNS = true;

		[OFApplication of_activateSandbox: sandbox];
	} @finally {
		[sandbox release];
	}
#endif

	recordTypes = [OFMutableArray array];

	optionsParser = [OFOptionsParser parserWithOptions: options];
	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case 't':
			[recordTypes addObject: optionsParser.argument];
			break;
		case 'h':
			help(OFStdOut, true, 0);
			break;
		case ':':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"long_option_required_argument",
				    @"%[prog]: Option --%[opt] requires an "
				    @"argument",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%C",
				    optionsParser.lastOption];
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"option_requires_argument",
				    @"%[prog]: Option -%[opt] requires an "
				    @"argument",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%C",
				    optionsParser.lastOption];
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"Unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

	remainingArguments = optionsParser.remainingArguments;

	if (remainingArguments.count < 1)
		help(OFStdErr, false, 1);

	resolver = [OFDNSResolver resolver];
	DNSClass = (DNSClassString != nil
	    ? OFDNSClassParseName(DNSClassString) : OFDNSClassIN);


	if (recordTypes.count == 0)
		[recordTypes addObject: @"ALL"];

	if (server != nil) {
		resolver.configReloadInterval = 0;
		resolver.nameServers = [OFArray arrayWithObject: server];
	}

	for (OFString *domainName in remainingArguments) {
		for (OFString *recordTypeString in recordTypes) {
			OFDNSRecordType recordType =
			    OFDNSRecordTypeParseName(recordTypeString);
			OFDNSQuery *query =
			    [OFDNSQuery queryWithDomainName: domainName
						   DNSClass: DNSClass
						 recordType: recordType];

			_inFlight++;
			[resolver asyncPerformQuery: query delegate: self];

		}
	}
}
@end

Modified utils/ofhash/OFHash.m from [58d815f0b3] to [1e29e681a3].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
@end

OF_APPLICATION_DELEGATE(OFHash)

static void
help(void)
{
	[of_stderr writeLine: OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] [--md5|--ripemd160|--sha1|--sha224|--sha256|"
	    @"--sha384|--sha512] file1 [file2 ...]",
	    @"prog", [OFApplication programName])];

	[OFApplication terminateWithStatus: 1];
}

static void
printHash(OFString *algo, OFString *path, id <OFCryptoHash> hash)
{

	const unsigned char *digest = hash.digest;


	size_t digestSize = hash.digestSize;

	[of_stdout writeFormat: @"%@ ", algo];

	for (size_t i = 0; i < digestSize; i++)
		[of_stdout writeFormat: @"%02x", digest[i]];

	[of_stdout writeFormat: @"  %@\n", path];
}

@implementation OFHash
- (void)applicationDidFinishLaunching
{
	int exitStatus = 0;
	bool calculateMD5, calculateRIPEMD160, calculateSHA1, calculateSHA224;
	bool calculateSHA256, calculateSHA384, calculateSHA512;
	const of_options_parser_option_t options[] = {
		{ '\0', @"md5", 0, &calculateMD5, NULL },
		{ '\0', @"ripemd160", 0, &calculateRIPEMD160, NULL },
		{ '\0', @"sha1", 0, &calculateSHA1, NULL },
		{ '\0', @"sha224", 0, &calculateSHA224, NULL },
		{ '\0', @"sha256", 0, &calculateSHA256, NULL },
		{ '\0', @"sha384", 0, &calculateSHA384, NULL },
		{ '\0', @"sha512", 0, &calculateSHA512, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFOptionsParser *optionsParser =
	    [OFOptionsParser parserWithOptions: options];
	of_unichar_t option;
	OFMD5Hash *MD5Hash = nil;
	OFRIPEMD160Hash *RIPEMD160Hash = nil;
	OFSHA1Hash *SHA1Hash = nil;
	OFSHA224Hash *SHA224Hash = nil;
	OFSHA256Hash *SHA256Hash = nil;
	OFSHA384Hash *SHA384Hash = nil;
	OFSHA512Hash *SHA512Hash = nil;

#ifndef OF_AMIGAOS
	[OFLocale addLanguageDirectory: @LANGUAGE_DIR];
#else
	[OFLocale addLanguageDirectory: @"PROGDIR:/share/ofhash/lang"];
#endif

	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case '?':
			if (optionsParser.lastLongOption != nil)
				[of_stderr writeLine:
				    OF_LOCALIZED(@"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString stringWithFormat:
				    @"%c", optionsParser.lastOption];
				[of_stderr writeLine:
				    OF_LOCALIZED(@"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [OFSandbox sandbox];
	@try {
		sandbox.allowsStdIO = true;
		sandbox.allowsReadingFiles = true;
		sandbox.allowsUserDatabaseReading = true;

		for (OFString *path in optionsParser.remainingArguments)
			[sandbox unveilPath: path
				permissions: @"r"];

		[sandbox unveilPath: @LANGUAGE_DIR
			permissions: @"r"];

		[OFApplication activateSandbox: sandbox];
	} @finally {
		[sandbox release];
	}
#endif

	if (!calculateMD5 && !calculateRIPEMD160 && !calculateSHA1 &&
	    !calculateSHA224 && !calculateSHA256 && !calculateSHA384 &&
	    !calculateSHA512)
		help();

	if (optionsParser.remainingArguments.count < 1)
		help();

	if (calculateMD5)
		MD5Hash = [OFMD5Hash cryptoHashWithAllowsSwappableMemory: true];
	if (calculateRIPEMD160)
		RIPEMD160Hash =
		    [OFRIPEMD160Hash cryptoHashWithAllowsSwappableMemory: true];
	if (calculateSHA1)
		SHA1Hash =
		    [OFSHA1Hash cryptoHashWithAllowsSwappableMemory: true];
	if (calculateSHA224)
		SHA224Hash =
		    [OFSHA224Hash cryptoHashWithAllowsSwappableMemory: true];
	if (calculateSHA256)
		SHA256Hash =
		    [OFSHA256Hash cryptoHashWithAllowsSwappableMemory: true];
	if (calculateSHA384)
		SHA384Hash =
		    [OFSHA384Hash cryptoHashWithAllowsSwappableMemory: true];
	if (calculateSHA512)
		SHA512Hash =
		    [OFSHA512Hash cryptoHashWithAllowsSwappableMemory: true];

	for (OFString *path in optionsParser.remainingArguments) {
		void *pool = objc_autoreleasePoolPush();
		OFStream *file;

		if ([path isEqual: @"-"])
			file = of_stdin;
		else {
			@try {
				file = [OFFile fileWithPath: path
						       mode: @"r"];
			} @catch (OFOpenItemFailedException *e) {
				OFString *error = [OFString
				    stringWithCString: strerror(e.errNo)
					     encoding: [OFLocale encoding]];

				[of_stderr writeLine: OF_LOCALIZED(
				    @"failed_to_open_file",
				    @"Failed to open file %[file]: %[error]",
				    @"file", e.path,
				    @"error", error)];

				exitStatus = 1;
				goto outer_loop_end;







|
|
|






|

>
|
>
>
|

|


|

|








|











|


















|







|



















|
<

|
<

|














|


|

<
|

<
|

<
|

<
|

<
|






|


|
<





|







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
@end

OF_APPLICATION_DELEGATE(OFHash)

static void
help(void)
{
	[OFStdErr writeLine: OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] [--md5] [--ripemd160] [--sha1] [--sha224] "
	    @"[--sha256] [--sha384] [--sha512] file1 [file2 ...]",
	    @"prog", [OFApplication programName])];

	[OFApplication terminateWithStatus: 1];
}

static void
printHash(OFString *algo, OFString *path, id <OFCryptographicHash> hash)
{
	size_t digestSize = hash.digestSize;
	const unsigned char *digest;

	[hash calculate];
	digest = hash.digest;

	[OFStdOut writeFormat: @"%@ ", algo];

	for (size_t i = 0; i < digestSize; i++)
		[OFStdOut writeFormat: @"%02x", digest[i]];

	[OFStdOut writeFormat: @"  %@\n", path];
}

@implementation OFHash
- (void)applicationDidFinishLaunching
{
	int exitStatus = 0;
	bool calculateMD5, calculateRIPEMD160, calculateSHA1, calculateSHA224;
	bool calculateSHA256, calculateSHA384, calculateSHA512;
	const OFOptionsParserOption options[] = {
		{ '\0', @"md5", 0, &calculateMD5, NULL },
		{ '\0', @"ripemd160", 0, &calculateRIPEMD160, NULL },
		{ '\0', @"sha1", 0, &calculateSHA1, NULL },
		{ '\0', @"sha224", 0, &calculateSHA224, NULL },
		{ '\0', @"sha256", 0, &calculateSHA256, NULL },
		{ '\0', @"sha384", 0, &calculateSHA384, NULL },
		{ '\0', @"sha512", 0, &calculateSHA512, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFOptionsParser *optionsParser =
	    [OFOptionsParser parserWithOptions: options];
	OFUnichar option;
	OFMD5Hash *MD5Hash = nil;
	OFRIPEMD160Hash *RIPEMD160Hash = nil;
	OFSHA1Hash *SHA1Hash = nil;
	OFSHA224Hash *SHA224Hash = nil;
	OFSHA256Hash *SHA256Hash = nil;
	OFSHA384Hash *SHA384Hash = nil;
	OFSHA512Hash *SHA512Hash = nil;

#ifndef OF_AMIGAOS
	[OFLocale addLanguageDirectory: @LANGUAGE_DIR];
#else
	[OFLocale addLanguageDirectory: @"PROGDIR:/share/ofhash/lang"];
#endif

	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case '?':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeLine:
				    OF_LOCALIZED(@"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString stringWithFormat:
				    @"%c", optionsParser.lastOption];
				[OFStdErr writeLine:
				    OF_LOCALIZED(@"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [OFSandbox sandbox];
	@try {
		sandbox.allowsStdIO = true;
		sandbox.allowsReadingFiles = true;
		sandbox.allowsUserDatabaseReading = true;

		for (OFString *path in optionsParser.remainingArguments)
			[sandbox unveilPath: path permissions: @"r"];


		[sandbox unveilPath: @LANGUAGE_DIR permissions: @"r"];


		[OFApplication of_activateSandbox: sandbox];
	} @finally {
		[sandbox release];
	}
#endif

	if (!calculateMD5 && !calculateRIPEMD160 && !calculateSHA1 &&
	    !calculateSHA224 && !calculateSHA256 && !calculateSHA384 &&
	    !calculateSHA512)
		help();

	if (optionsParser.remainingArguments.count < 1)
		help();

	if (calculateMD5)
		MD5Hash = [OFMD5Hash hashWithAllowsSwappableMemory: true];
	if (calculateRIPEMD160)
		RIPEMD160Hash =
		    [OFRIPEMD160Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA1)

		SHA1Hash = [OFSHA1Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA224)

		SHA224Hash = [OFSHA224Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA256)

		SHA256Hash = [OFSHA256Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA384)

		SHA384Hash = [OFSHA384Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA512)

		SHA512Hash = [OFSHA512Hash hashWithAllowsSwappableMemory: true];

	for (OFString *path in optionsParser.remainingArguments) {
		void *pool = objc_autoreleasePoolPush();
		OFStream *file;

		if ([path isEqual: @"-"])
			file = OFStdIn;
		else {
			@try {
				file = [OFFile fileWithPath: path mode: @"r"];

			} @catch (OFOpenItemFailedException *e) {
				OFString *error = [OFString
				    stringWithCString: strerror(e.errNo)
					     encoding: [OFLocale encoding]];

				[OFStdErr writeLine: OF_LOCALIZED(
				    @"failed_to_open_file",
				    @"Failed to open file %[file]: %[error]",
				    @"file", e.path,
				    @"error", error)];

				exitStatus = 1;
				goto outer_loop_end;
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
				length = [file readIntoBuffer: buffer
						       length: 1024];
			} @catch (OFReadFailedException *e) {
				OFString *error = [OFString
				    stringWithCString: strerror(e.errNo)
					     encoding: [OFLocale encoding]];

				[of_stderr writeLine: OF_LOCALIZED(
				    @"failed_to_read_file",
				    @"Failed to read %[file]: %[error]",
				    @"file", path,
				    @"error", error)];

				exitStatus = 1;
				goto outer_loop_end;







|







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
				length = [file readIntoBuffer: buffer
						       length: 1024];
			} @catch (OFReadFailedException *e) {
				OFString *error = [OFString
				    stringWithCString: strerror(e.errNo)
					     encoding: [OFLocale encoding]];

				[OFStdErr writeLine: OF_LOCALIZED(
				    @"failed_to_read_file",
				    @"Failed to read %[file]: %[error]",
				    @"file", path,
				    @"error", error)];

				exitStatus = 1;
				goto outer_loop_end;

Modified utils/ofhash/lang/de.json from [775fc52ec2] to [e3d21e1de1].

1
2
3
4
5
6
7
8
9
10
{
    "usage": [
        "Benutzung: %[prog] [--md5|--ripemd160|--sha1|--sha224|--sha256|",
        "--sha384|--sha512] datei1 [datei2 ...]"
    ],
    "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]",
    "unknown_option": "%[prog]: Unbekannte Option: -%[opt]",
    "failed_to_open_file": "Fehler beim Öffnen der Datei %[file]: %[error]",
    "failed_to_read_file": "Fehler beim Lesen der Datei %[file]: %[error]"
}


|
|






1
2
3
4
5
6
7
8
9
10
{
    "usage": [
        "Benutzung: %[prog] [--md5] [--ripemd160] [--sha1] [--sha224] ",
        "[--sha256] [--sha384] [--sha512] datei1 [datei2 ...]"
    ],
    "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]",
    "unknown_option": "%[prog]: Unbekannte Option: -%[opt]",
    "failed_to_open_file": "Fehler beim Öffnen der Datei %[file]: %[error]",
    "failed_to_read_file": "Fehler beim Lesen der Datei %[file]: %[error]"
}

Modified utils/ofhttp/Makefile from [ca0ac82289] to [a12779af52].

11
12
13
14
15
16
17

18
19


20
21
22
23
24
PACKAGE_NAME = ofhttp

${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2}

CPPFLAGS += -I../../src					\
	    -I../../src/runtime				\
	    -I../../src/exceptions			\

	    -I../..					\
	    -DLANGUAGE_DIR=\"${datadir}/ofhttp/lang\"


LIBS := -L../../src -lobjfw						\
	-L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS}	\
	${LIBS}
LD = ${OBJC}
LDFLAGS += ${LDFLAGS_RPATH}







>

|
>
>
|




11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
PACKAGE_NAME = ofhttp

${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2}

CPPFLAGS += -I../../src					\
	    -I../../src/runtime				\
	    -I../../src/exceptions			\
	    -I../../src/tls				\
	    -I../..					\
	    -DLANGUAGE_DIR='"${datadir}/ofhttp/lang"'	\
	    -DLIB_PREFIX='"${LIB_PREFIX}"'		\
	    -DLIB_SUFFIX='"${LIB_SUFFIX}"'
LIBS := -L../../src -L../../src/tls ${OFHTTP_LIBS} -lobjfw		\
	-L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS}	\
	${LIBS}
LD = ${OBJC}
LDFLAGS += ${LDFLAGS_RPATH}

Modified utils/ofhttp/OFHTTP.m from [c172d83732] to [aa1d962c4e].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
31
32
33
34
35
36
37
38
39




40
41
42
43
44
45
46
#ifdef OF_HAVE_PLUGINS
# import "OFPlugin.h"
#endif
#import "OFSandbox.h"
#import "OFStdIOStream.h"
#import "OFSystemInfo.h"
#import "OFTCPSocket.h"
#import "OFTLSSocket.h"
#import "OFURL.h"





#import "OFConnectionFailedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"
#import "OFOpenItemFailedException.h"







|

>
>
>
>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#ifdef OF_HAVE_PLUGINS
# import "OFPlugin.h"
#endif
#import "OFSandbox.h"
#import "OFStdIOStream.h"
#import "OFSystemInfo.h"
#import "OFTCPSocket.h"
#import "OFTLSStream.h"
#import "OFURL.h"

#ifdef HAVE_TLS_SUPPORT
# import "ObjFWTLS.h"
#endif

#import "OFConnectionFailedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"
#import "OFOpenItemFailedException.h"
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
{
	OFArray OF_GENERIC(OFString *) *_URLs;
	size_t _URLIndex;
	int _errorCode;
	OFString *_outputPath, *_currentFileName;
	bool _continue, _force, _detectFileName, _detectFileNameRequest;
	bool _detectedFileName, _quiet, _verbose, _insecure, _ignoreStatus;

	OFStream *_body;
	of_http_request_method_t _method;
	OFMutableDictionary *_clientHeaders;
	OFHTTPClient *_HTTPClient;
	char *_buffer;
	OFStream *_output;
	unsigned long long _received, _length, _resumedFrom;
	ProgressBar *_progressBar;
}

- (void)downloadNextURL;
@end









OF_APPLICATION_DELEGATE(OFHTTP)

static void
help(OFStream *stream, bool full, int status)
{
	[of_stderr writeLine:
	    OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] -[cehHmoOPqv] url1 [url2 ...]",
	    @"prog", [OFApplication programName])];

	if (full) {
		[stream writeString: @"\n"];
		[stream writeLine: OF_LOCALIZED(@"full_usage",







>

|










>
>
>
>
>
>
>
>






|







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
{
	OFArray OF_GENERIC(OFString *) *_URLs;
	size_t _URLIndex;
	int _errorCode;
	OFString *_outputPath, *_currentFileName;
	bool _continue, _force, _detectFileName, _detectFileNameRequest;
	bool _detectedFileName, _quiet, _verbose, _insecure, _ignoreStatus;
	bool _useUnicode;
	OFStream *_body;
	OFHTTPRequestMethod _method;
	OFMutableDictionary *_clientHeaders;
	OFHTTPClient *_HTTPClient;
	char *_buffer;
	OFStream *_output;
	unsigned long long _received, _length, _resumedFrom;
	ProgressBar *_progressBar;
}

- (void)downloadNextURL;
@end

#ifdef HAVE_TLS_SUPPORT
void
_reference_to_ObjFWTLS(void)
{
	_ObjFWTLS_reference = 1;
}
#endif

OF_APPLICATION_DELEGATE(OFHTTP)

static void
help(OFStream *stream, bool full, int status)
{
	[OFStdErr writeLine:
	    OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] -[cehHmoOPqv] url1 [url2 ...]",
	    @"prog", [OFApplication programName])];

	if (full) {
		[stream writeString: @"\n"];
		[stream writeLine: OF_LOCALIZED(@"full_usage",
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
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
static OFString *
fileNameFromContentDisposition(OFString *contentDisposition)
{
	void *pool;
	const char *UTF8String;
	size_t UTF8StringLength;
	enum {
		DISPOSITION_TYPE,
		DISPOSITION_TYPE_SEMICOLON,
		DISPOSITION_PARAM_NAME_SKIP_SPACE,
		DISPOSITION_PARAM_NAME,
		DISPOSITION_PARAM_VALUE,
		DISPOSITION_PARAM_QUOTED,
		DISPOSITION_PARAM_UNQUOTED,
		DISPOSITION_EXPECT_SEMICOLON
	} state;
	size_t last;
	OFString *type = nil, *paramName = nil, *paramValue;
	OFMutableDictionary *params;
	OFString *fileName;

	if (contentDisposition == nil)
		return nil;

	pool = objc_autoreleasePoolPush();

	UTF8String = contentDisposition.UTF8String;
	UTF8StringLength = contentDisposition.UTF8StringLength;
	state = DISPOSITION_TYPE;
	params = [OFMutableDictionary dictionary];
	last = 0;

	for (size_t i = 0; i < UTF8StringLength; i++) {
		switch (state) {
		case DISPOSITION_TYPE:
			if (UTF8String[i] == ';' || UTF8String[i] == ' ') {
				type = [OFString
				    stringWithUTF8String: UTF8String
						  length: i];

				state = (UTF8String[i] == ';'
				    ? DISPOSITION_PARAM_NAME_SKIP_SPACE
				    : DISPOSITION_TYPE_SEMICOLON);
				last = i + 1;
			}
			break;
		case DISPOSITION_TYPE_SEMICOLON:
			if (UTF8String[i] == ';') {
				state = DISPOSITION_PARAM_NAME_SKIP_SPACE;
				last = i + 1;
			} else if (UTF8String[i] != ' ') {
				objc_autoreleasePoolPop(pool);
				return nil;
			}
			break;
		case DISPOSITION_PARAM_NAME_SKIP_SPACE:
			if (UTF8String[i] != ' ') {
				state = DISPOSITION_PARAM_NAME;
				last = i;
				i--;
			}
			break;
		case DISPOSITION_PARAM_NAME:
			if (UTF8String[i] == '=') {
				paramName = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				state = DISPOSITION_PARAM_VALUE;
			}
			break;
		case DISPOSITION_PARAM_VALUE:
			if (UTF8String[i] == '"') {
				state = DISPOSITION_PARAM_QUOTED;
				last = i + 1;
			} else {
				state = DISPOSITION_PARAM_UNQUOTED;
				last = i;
				i--;
			}
			break;
		case DISPOSITION_PARAM_QUOTED:
			if (UTF8String[i] == '"') {
				paramValue = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				[params setObject: paramValue
					   forKey: paramName.lowercaseString];

				state = DISPOSITION_EXPECT_SEMICOLON;
			}
			break;
		case DISPOSITION_PARAM_UNQUOTED:
			if (UTF8String[i] <= 31 || UTF8String[i] >= 127)
				return nil;

			switch (UTF8String[i]) {
			case ' ': case '"': case '(': case ')': case ',':
			case '/': case ':': case '<': case '=': case '>':
			case '?': case '@': case '[': case '\\': case ']':
			case '{': case '}':
				return nil;
			case ';':
				paramValue = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				[params setObject: paramValue
					   forKey: paramName.lowercaseString];

				state = DISPOSITION_PARAM_NAME_SKIP_SPACE;
				break;
			}
			break;
		case DISPOSITION_EXPECT_SEMICOLON:
			if (UTF8String[i] == ';') {
				state = DISPOSITION_PARAM_NAME_SKIP_SPACE;
				last = i + 1;
			} else if (UTF8String[i] != ' ') {
				objc_autoreleasePoolPop(pool);
				return nil;
			}
			break;
		}
	}

	if (state == DISPOSITION_PARAM_UNQUOTED) {
		paramValue = [OFString
		    stringWithUTF8String: UTF8String + last
				  length: UTF8StringLength - last];

		[params setObject: paramValue
			   forKey: paramName.lowercaseString];
	} else if (state != DISPOSITION_EXPECT_SEMICOLON) {
		objc_autoreleasePoolPop(pool);
		return nil;
	}

	if (![type isEqual: @"attachment"] ||
	    (fileName = [params objectForKey: @"filename"]) == nil) {
		objc_autoreleasePoolPop(pool);
		return nil;
	}

	fileName = fileName.lastPathComponent;

	[fileName retain];
	objc_autoreleasePoolPop(pool);
	return [fileName autorelease];
}

@implementation OFHTTP
#ifdef OF_HAVE_PLUGINS
+ (void)initialize
{
	if (self != [OFHTTP class])
		return;

	/* Opportunistically try loading ObjOpenSSL and ignore any errors. */
	of_dlopen(@"objopenssl", OF_RTLD_LAZY);
}
#endif

- (instancetype)init
{
	self = [super init];

	@try {
		_method = OF_HTTP_REQUEST_METHOD_GET;

		_clientHeaders = [[OFMutableDictionary alloc]
		    initWithObject: @"OFHTTP"
			    forKey: @"User-Agent"];

		_HTTPClient = [[OFHTTPClient alloc] init];
		_HTTPClient.delegate = self;

		_buffer = [self allocMemoryWithSize: [OFSystemInfo pageSize]];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)addHeader: (OFString *)header
{
	size_t pos = [header rangeOfString: @":"].location;
	OFString *name, *value;

	if (pos == OF_NOT_FOUND) {
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_input_header",
		    @"%[prog]: Headers must to be in format name:value!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	name = [header substringToIndex: pos]
	    .stringByDeletingEnclosingWhitespaces;

	value = [header substringFromIndex: pos + 1]
	    .stringByDeletingEnclosingWhitespaces;

	[_clientHeaders setObject: value
			   forKey: name];
}

- (void)setBody: (OFString *)path
{
	OFString *contentLength = nil;

	[_body release];
	_body = nil;

	if ([path isEqual: @"-"])
		_body = [of_stdin copy];
	else {
		_body = [[OFFile alloc] initWithPath: path
						mode: @"r"];

		@try {
			unsigned long long fileSize =
			    [[OFFileManager defaultManager]
			    attributesOfItemAtPath: path].fileSize;

			contentLength =







|
|
|
|
|
|
|
|













|





|






|
|



|

|






|

|




|





|


|

|


|




|








|


|

















|



|

|









|






|


















<
<
<
<
<
<
<
<
<
<
<





|








|













|
|











|
<










|

|
<







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
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
static OFString *
fileNameFromContentDisposition(OFString *contentDisposition)
{
	void *pool;
	const char *UTF8String;
	size_t UTF8StringLength;
	enum {
		stateDispositionType,
		stateDispositionTypeSemicolon,
		stateDispositionParamNameSkipSpace,
		stateDispositionParamName,
		stateDispositionParamValue,
		stateDispositionParamQuoted,
		stateDispositionParamUnquoted,
		stateDispositionExpectSemicolon
	} state;
	size_t last;
	OFString *type = nil, *paramName = nil, *paramValue;
	OFMutableDictionary *params;
	OFString *fileName;

	if (contentDisposition == nil)
		return nil;

	pool = objc_autoreleasePoolPush();

	UTF8String = contentDisposition.UTF8String;
	UTF8StringLength = contentDisposition.UTF8StringLength;
	state = stateDispositionType;
	params = [OFMutableDictionary dictionary];
	last = 0;

	for (size_t i = 0; i < UTF8StringLength; i++) {
		switch (state) {
		case stateDispositionType:
			if (UTF8String[i] == ';' || UTF8String[i] == ' ') {
				type = [OFString
				    stringWithUTF8String: UTF8String
						  length: i];

				state = (UTF8String[i] == ';'
				    ? stateDispositionParamNameSkipSpace
				    : stateDispositionTypeSemicolon);
				last = i + 1;
			}
			break;
		case stateDispositionTypeSemicolon:
			if (UTF8String[i] == ';') {
				state = stateDispositionParamNameSkipSpace;
				last = i + 1;
			} else if (UTF8String[i] != ' ') {
				objc_autoreleasePoolPop(pool);
				return nil;
			}
			break;
		case stateDispositionParamNameSkipSpace:
			if (UTF8String[i] != ' ') {
				state = stateDispositionParamName;
				last = i;
				i--;
			}
			break;
		case stateDispositionParamName:
			if (UTF8String[i] == '=') {
				paramName = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				state = stateDispositionParamValue;
			}
			break;
		case stateDispositionParamValue:
			if (UTF8String[i] == '"') {
				state = stateDispositionParamQuoted;
				last = i + 1;
			} else {
				state = stateDispositionParamUnquoted;
				last = i;
				i--;
			}
			break;
		case stateDispositionParamQuoted:
			if (UTF8String[i] == '"') {
				paramValue = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				[params setObject: paramValue
					   forKey: paramName.lowercaseString];

				state = stateDispositionExpectSemicolon;
			}
			break;
		case stateDispositionParamUnquoted:
			if (UTF8String[i] <= 31 || UTF8String[i] >= 127)
				return nil;

			switch (UTF8String[i]) {
			case ' ': case '"': case '(': case ')': case ',':
			case '/': case ':': case '<': case '=': case '>':
			case '?': case '@': case '[': case '\\': case ']':
			case '{': case '}':
				return nil;
			case ';':
				paramValue = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				[params setObject: paramValue
					   forKey: paramName.lowercaseString];

				state = stateDispositionParamNameSkipSpace;
				break;
			}
			break;
		case stateDispositionExpectSemicolon:
			if (UTF8String[i] == ';') {
				state = stateDispositionParamNameSkipSpace;
				last = i + 1;
			} else if (UTF8String[i] != ' ') {
				objc_autoreleasePoolPop(pool);
				return nil;
			}
			break;
		}
	}

	if (state == stateDispositionParamUnquoted) {
		paramValue = [OFString
		    stringWithUTF8String: UTF8String + last
				  length: UTF8StringLength - last];

		[params setObject: paramValue
			   forKey: paramName.lowercaseString];
	} else if (state != stateDispositionExpectSemicolon) {
		objc_autoreleasePoolPop(pool);
		return nil;
	}

	if (![type isEqual: @"attachment"] ||
	    (fileName = [params objectForKey: @"filename"]) == nil) {
		objc_autoreleasePoolPop(pool);
		return nil;
	}

	fileName = fileName.lastPathComponent;

	[fileName retain];
	objc_autoreleasePoolPop(pool);
	return [fileName autorelease];
}

@implementation OFHTTP











- (instancetype)init
{
	self = [super init];

	@try {
		_method = OFHTTPRequestMethodGet;

		_clientHeaders = [[OFMutableDictionary alloc]
		    initWithObject: @"OFHTTP"
			    forKey: @"User-Agent"];

		_HTTPClient = [[OFHTTPClient alloc] init];
		_HTTPClient.delegate = self;

		_buffer = OFAllocMemory(1, [OFSystemInfo pageSize]);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)addHeader: (OFString *)header
{
	size_t pos = [header rangeOfString: @":"].location;
	OFString *name, *value;

	if (pos == OFNotFound) {
		[OFStdErr writeLine: OF_LOCALIZED(@"invalid_input_header",
		    @"%[prog]: Headers must to be in format name:value!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	name = [header substringToIndex: pos]
	    .stringByDeletingEnclosingWhitespaces;

	value = [header substringFromIndex: pos + 1]
	    .stringByDeletingEnclosingWhitespaces;

	[_clientHeaders setObject: value forKey: name];

}

- (void)setBody: (OFString *)path
{
	OFString *contentLength = nil;

	[_body release];
	_body = nil;

	if ([path isEqual: @"-"])
		_body = [OFStdIn copy];
	else {
		_body = [[OFFile alloc] initWithPath: path mode: @"r"];


		@try {
			unsigned long long fileSize =
			    [[OFFileManager defaultManager]
			    attributesOfItemAtPath: path].fileSize;

			contentLength =
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
- (void)setMethod: (OFString *)method
{
	void *pool = objc_autoreleasePoolPush();

	method = method.uppercaseString;

	@try {
		_method = of_http_request_method_from_string(method);
	} @catch (OFInvalidArgumentException *e) {
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_input_method",
		    @"%[prog]: Invalid request method %[method]!",
		    @"prog", [OFApplication programName],
		    @"method", method)];
		[OFApplication terminateWithStatus: 1];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)setProxy: (OFString *)proxy
{
	@try {
		size_t pos = [proxy
		    rangeOfString: @":"
			  options: OF_STRING_SEARCH_BACKWARDS].location;
		OFString *host;
		unsigned long long port;

		if (pos == OF_NOT_FOUND)
			@throw [OFInvalidFormatException exception];

		host = [proxy substringToIndex: pos];
		port = [proxy substringFromIndex: pos + 1]
		    .unsignedLongLongValue;

		if (port > UINT16_MAX)
			@throw [OFOutOfRangeException exception];

		[OFTCPSocket setSOCKS5Host: host];
		[OFTCPSocket setSOCKS5Port: (uint16_t)port];
	} @catch (OFInvalidFormatException *e) {
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_input_proxy",
		    @"%[prog]: Proxy must to be in format host:port!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}
}

- (void)applicationDidFinishLaunching
{
	OFString *outputPath;
	const of_options_parser_option_t options[] = {
		{ 'b', @"body",	1, NULL, NULL },
		{ 'c', @"continue", 0, &_continue, NULL },
		{ 'f', @"force", 0, &_force, NULL },
		{ 'h', @"help",	0, NULL, NULL },
		{ 'H', @"header", 1, NULL, NULL },
		{ 'm', @"method", 1, NULL, NULL },
		{ 'o', @"output", 1, NULL, &outputPath },
		{ 'O', @"detect-filename", 0, &_detectFileName, NULL },
		{ 'P', @"socks5-proxy", 1, NULL, NULL },
		{ 'q', @"quiet", 0, &_quiet, NULL },
		{ 'v', @"verbose", 0, &_verbose, NULL },
		{ '\0', @"insecure", 0, &_insecure, NULL },
		{ '\0', @"ignore-status", 0, &_ignoreStatus, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFOptionsParser *optionsParser;
	of_unichar_t option;

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [OFSandbox sandbox];
	sandbox.allowsStdIO = true;
	sandbox.allowsReadingFiles = true;
	sandbox.allowsWritingFiles = true;
	sandbox.allowsCreatingFiles = true;
	sandbox.allowsIPSockets = true;
	sandbox.allowsDNS = true;
	sandbox.allowsUserDatabaseReading = true;
	sandbox.allowsTTY = true;
	/* Dropped after parsing options */
	sandbox.allowsUnveil = true;

	[OFApplication activateSandbox: sandbox];
#endif

#ifndef OF_AMIGAOS
	[OFLocale addLanguageDirectory: @LANGUAGE_DIR];
#else
	[OFLocale addLanguageDirectory: @"PROGDIR:/share/ofhttp/lang"];
#endif

	optionsParser = [OFOptionsParser parserWithOptions: options];
	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case 'b':
			[self setBody: optionsParser.argument];
			break;
		case 'h':
			help(of_stdout, true, 0);
			break;
		case 'H':
			[self addHeader: optionsParser.argument];
			break;
		case 'm':
			[self setMethod: optionsParser.argument];
			break;
		case 'P':
			[self setProxy: optionsParser.argument];
			break;
		case ':':
			if (optionsParser.lastLongOption != nil)
				[of_stderr writeLine:
				    OF_LOCALIZED(@"long_argument_missing",
				    @"%[prog]: Argument for option --%[opt] "
				    @"missing"
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%c",
				    optionsParser.lastOption];
				[of_stderr writeLine:
				    OF_LOCALIZED(@"argument_missing",
				    @"%[prog]: Argument for option -%[opt] "
				    @"missing",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		case '=':
			[of_stderr writeLine:
			    OF_LOCALIZED(@"option_takes_no_argument",
			    @"%[prog]: Option --%[opt] takes no argument",
			    @"prog", [OFApplication programName],
			    @"opt", optionsParser.lastLongOption)];

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if (optionsParser.lastLongOption != nil)
				[of_stderr writeLine:
				    OF_LOCALIZED(@"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%c",
				    optionsParser.lastOption];
				[of_stderr writeLine:
				    OF_LOCALIZED(@"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

#ifdef OF_HAVE_SANDBOX

	[sandbox unveilPath: (outputPath != nil



				 ? outputPath : OF_PATH_CURRENT_DIRECTORY)
		permissions: (_continue ? @"rwc" : @"wc")];

	/* In case we use ObjOpenSSL for https later */
	[sandbox unveilPath: @"/etc/ssl"
		permissions: @"r"];

	sandbox.allowsUnveil = false;
	[OFApplication activateSandbox: sandbox];
#endif

	_outputPath = [outputPath copy];
	_URLs = [optionsParser.remainingArguments copy];

	if (_URLs.count < 1)
		help(of_stderr, false, 1);

	if (_quiet && _verbose) {
		[of_stderr writeLine: OF_LOCALIZED(@"quiet_xor_verbose",
		    @"%[prog]: -q / --quiet and -v / --verbose are mutually "
		    @"exclusive!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_outputPath != nil && _detectFileName) {
		[of_stderr writeLine: OF_LOCALIZED(
		    @"output_xor_detect_filename",
		    @"%[prog]: -o / --output and -O / --detect-filename are "
		    @"mutually exclusive!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_outputPath != nil && _URLs.count > 1) {
		[of_stderr writeLine:
		    OF_LOCALIZED(@"output_only_with_one_url",
		    @"%[prog]: Cannot use -o / --output when more than one URL "
		    @"has been specified!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_insecure)
		_HTTPClient.allowsInsecureRedirects = true;







	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}

-    (void)client: (OFHTTPClient *)client
  didCreateSocket: (OFTCPSocket *)sock
	  request: (OFHTTPRequest *)request
{
	if (_insecure && [sock respondsToSelector:

	    @selector(setVerifiesCertificates:)])
		((id <OFTLSSocket>)sock).verifiesCertificates = false;
}

-     (void)client: (OFHTTPClient *)client
  wantsRequestBody: (OFStream *)body
	   request: (OFHTTPRequest *)request
{
	/* TODO: Do asynchronously and print status */
	while (!_body.atEndOfStream) {
		char buffer[4096];
		size_t length;

		length = [_body readIntoBuffer: buffer
					length: 4096];
		[body writeBuffer: buffer
			   length: length];
	}
}

-	  (bool)client: (OFHTTPClient *)client
  shouldFollowRedirect: (OFURL *)URL
	    statusCode: (short)statusCode
	       request: (OFHTTPRequest *)request
	      response: (OFHTTPResponse *)response
{
	if (_verbose) {
		void *pool = objc_autoreleasePoolPush();
		OFDictionary OF_GENERIC(OFString *, OFString *) *headers =
		    response.headers;
		OFEnumerator *keyEnumerator = [headers keyEnumerator];
		OFEnumerator *objectEnumerator = [headers objectEnumerator];
		OFString *key, *object;

		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil)
			[of_stdout writeFormat: @"  %@: %@\n",
						key, object];

		objc_autoreleasePoolPop(pool);
	}

	if (!_quiet)

		[of_stdout writeFormat: @"☇ %@", URL.string];




	_length = 0;

	return true;
}

-      (bool)stream: (OFStream *)response
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (id)exception
{
	if (exception != nil) {
		OFString *URL;

		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet) {
			[of_stdout writeString: @"\n  "];
			[of_stdout writeLine: OF_LOCALIZED(@"download_error",
			    @"Error!")];
		}

		URL = [_URLs objectAtIndex: _URLIndex - 1];
		[of_stderr writeLine: OF_LOCALIZED(
		    @"download_failed_exception",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", URL,
		    @"exception", exception)];

		_errorCode = 1;
		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return false;
	}

	_received += length;

	[_output writeBuffer: buffer
		      length: length];


	[_progressBar setReceived: _received];

	if (response.atEndOfStream) {
		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet) {
			[of_stdout writeString: @"\n  "];
			[of_stdout writeLine:
			    OF_LOCALIZED(@"download_done", @"Done!")];
		}

		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return false;
	}







|

|














|



|












|









|
















|














|















|












|









|










|









|








|












>
|
>
>
>
|
|
>
|
|
<


|






|


|







|








|










>
>
>
>
>
>
|
<


|
|
|

<
>
|
<









<
<
|
<
|
<



















|
<




|
>
|
>
>
>




















|
|




|













<
<
|
<

>









|
|







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
- (void)setMethod: (OFString *)method
{
	void *pool = objc_autoreleasePoolPush();

	method = method.uppercaseString;

	@try {
		_method = OFHTTPRequestMethodParseName(method);
	} @catch (OFInvalidArgumentException *e) {
		[OFStdErr writeLine: OF_LOCALIZED(@"invalid_input_method",
		    @"%[prog]: Invalid request method %[method]!",
		    @"prog", [OFApplication programName],
		    @"method", method)];
		[OFApplication terminateWithStatus: 1];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)setProxy: (OFString *)proxy
{
	@try {
		size_t pos = [proxy
		    rangeOfString: @":"
			  options: OFStringSearchBackwards].location;
		OFString *host;
		unsigned long long port;

		if (pos == OFNotFound)
			@throw [OFInvalidFormatException exception];

		host = [proxy substringToIndex: pos];
		port = [proxy substringFromIndex: pos + 1]
		    .unsignedLongLongValue;

		if (port > UINT16_MAX)
			@throw [OFOutOfRangeException exception];

		[OFTCPSocket setSOCKS5Host: host];
		[OFTCPSocket setSOCKS5Port: (uint16_t)port];
	} @catch (OFInvalidFormatException *e) {
		[OFStdErr writeLine: OF_LOCALIZED(@"invalid_input_proxy",
		    @"%[prog]: Proxy must to be in format host:port!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}
}

- (void)applicationDidFinishLaunching
{
	OFString *outputPath;
	const OFOptionsParserOption options[] = {
		{ 'b', @"body",	1, NULL, NULL },
		{ 'c', @"continue", 0, &_continue, NULL },
		{ 'f', @"force", 0, &_force, NULL },
		{ 'h', @"help",	0, NULL, NULL },
		{ 'H', @"header", 1, NULL, NULL },
		{ 'm', @"method", 1, NULL, NULL },
		{ 'o', @"output", 1, NULL, &outputPath },
		{ 'O', @"detect-filename", 0, &_detectFileName, NULL },
		{ 'P', @"socks5-proxy", 1, NULL, NULL },
		{ 'q', @"quiet", 0, &_quiet, NULL },
		{ 'v', @"verbose", 0, &_verbose, NULL },
		{ '\0', @"insecure", 0, &_insecure, NULL },
		{ '\0', @"ignore-status", 0, &_ignoreStatus, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFOptionsParser *optionsParser;
	OFUnichar option;

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [OFSandbox sandbox];
	sandbox.allowsStdIO = true;
	sandbox.allowsReadingFiles = true;
	sandbox.allowsWritingFiles = true;
	sandbox.allowsCreatingFiles = true;
	sandbox.allowsIPSockets = true;
	sandbox.allowsDNS = true;
	sandbox.allowsUserDatabaseReading = true;
	sandbox.allowsTTY = true;
	/* Dropped after parsing options */
	sandbox.allowsUnveil = true;

	[OFApplication of_activateSandbox: sandbox];
#endif

#ifndef OF_AMIGAOS
	[OFLocale addLanguageDirectory: @LANGUAGE_DIR];
#else
	[OFLocale addLanguageDirectory: @"PROGDIR:/share/ofhttp/lang"];
#endif

	optionsParser = [OFOptionsParser parserWithOptions: options];
	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case 'b':
			[self setBody: optionsParser.argument];
			break;
		case 'h':
			help(OFStdOut, true, 0);
			break;
		case 'H':
			[self addHeader: optionsParser.argument];
			break;
		case 'm':
			[self setMethod: optionsParser.argument];
			break;
		case 'P':
			[self setProxy: optionsParser.argument];
			break;
		case ':':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeLine:
				    OF_LOCALIZED(@"long_argument_missing",
				    @"%[prog]: Argument for option --%[opt] "
				    @"missing"
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%c",
				    optionsParser.lastOption];
				[OFStdErr writeLine:
				    OF_LOCALIZED(@"argument_missing",
				    @"%[prog]: Argument for option -%[opt] "
				    @"missing",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		case '=':
			[OFStdErr writeLine:
			    OF_LOCALIZED(@"option_takes_no_argument",
			    @"%[prog]: Option --%[opt] takes no argument",
			    @"prog", [OFApplication programName],
			    @"opt", optionsParser.lastLongOption)];

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeLine:
				    OF_LOCALIZED(@"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%c",
				    optionsParser.lastOption];
				[OFStdErr writeLine:
				    OF_LOCALIZED(@"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

#ifdef OF_HAVE_SANDBOX
	if (outputPath != nil)
		[sandbox unveilPath: outputPath
			permissions: (_continue ? @"rwc" : @"wc")];
	else
		[sandbox unveilPath: [[OFFileManager defaultManager]
					 currentDirectoryPath]
			permissions: (_continue ? @"rwc" : @"wc")];

	/* In case we use OpenSSL for HTTPS later */
	[sandbox unveilPath: @"/etc/ssl" permissions: @"r"];


	sandbox.allowsUnveil = false;
	[OFApplication of_activateSandbox: sandbox];
#endif

	_outputPath = [outputPath copy];
	_URLs = [optionsParser.remainingArguments copy];

	if (_URLs.count < 1)
		help(OFStdErr, false, 1);

	if (_quiet && _verbose) {
		[OFStdErr writeLine: OF_LOCALIZED(@"quiet_xor_verbose",
		    @"%[prog]: -q / --quiet and -v / --verbose are mutually "
		    @"exclusive!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_outputPath != nil && _detectFileName) {
		[OFStdErr writeLine: OF_LOCALIZED(
		    @"output_xor_detect_filename",
		    @"%[prog]: -o / --output and -O / --detect-filename are "
		    @"mutually exclusive!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_outputPath != nil && _URLs.count > 1) {
		[OFStdErr writeLine:
		    OF_LOCALIZED(@"output_only_with_one_url",
		    @"%[prog]: Cannot use -o / --output when more than one URL "
		    @"has been specified!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_insecure)
		_HTTPClient.allowsInsecureRedirects = true;

#ifdef OF_WINDOWS
	_useUnicode = [OFSystemInfo isWindowsNT];
#else
	_useUnicode = ([OFLocale encoding] == OFStringEncodingUTF8);
#endif

	[self performSelector: @selector(downloadNextURL) afterDelay: 0];

}

-	(void)client: (OFHTTPClient *)client
  didCreateTLSStream: (OFTLSStream *)stream
	     request: (OFHTTPRequest *)request
{

	/* Use setter instead of property access to work around GCC bug. */
	[stream setVerifiesCertificates: !_insecure];

}

-     (void)client: (OFHTTPClient *)client
  wantsRequestBody: (OFStream *)body
	   request: (OFHTTPRequest *)request
{
	/* TODO: Do asynchronously and print status */
	while (!_body.atEndOfStream) {
		char buffer[4096];


		size_t length = [_body readIntoBuffer: buffer length: 4096];

		[body writeBuffer: buffer length: length];

	}
}

-	  (bool)client: (OFHTTPClient *)client
  shouldFollowRedirect: (OFURL *)URL
	    statusCode: (short)statusCode
	       request: (OFHTTPRequest *)request
	      response: (OFHTTPResponse *)response
{
	if (_verbose) {
		void *pool = objc_autoreleasePoolPush();
		OFDictionary OF_GENERIC(OFString *, OFString *) *headers =
		    response.headers;
		OFEnumerator *keyEnumerator = [headers keyEnumerator];
		OFEnumerator *objectEnumerator = [headers objectEnumerator];
		OFString *key, *object;

		while ((key = [keyEnumerator nextObject]) != nil &&
		    (object = [objectEnumerator nextObject]) != nil)
			[OFStdOut writeFormat: @"  %@: %@\n", key, object];


		objc_autoreleasePoolPop(pool);
	}

	if (!_quiet) {
		if (_useUnicode)
			[OFStdOut writeFormat: @"☇ %@", URL.string];
		else
			[OFStdOut writeFormat: @"< %@", URL.string];
	}

	_length = 0;

	return true;
}

-      (bool)stream: (OFStream *)response
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (id)exception
{
	if (exception != nil) {
		OFString *URL;

		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet) {
			[OFStdOut writeString: @"\n  "];
			[OFStdOut writeLine: OF_LOCALIZED(@"download_error",
			    @"Error!")];
		}

		URL = [_URLs objectAtIndex: _URLIndex - 1];
		[OFStdErr writeLine: OF_LOCALIZED(
		    @"download_failed_exception",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", URL,
		    @"exception", exception)];

		_errorCode = 1;
		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return false;
	}



	[_output writeBuffer: buffer length: length];


	_received += length;
	[_progressBar setReceived: _received];

	if (response.atEndOfStream) {
		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet) {
			[OFStdOut writeString: @"\n  "];
			[OFStdOut writeLine:
			    OF_LOCALIZED(@"download_done", @"Done!")];
		}

		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return false;
	}
706
707
708
709
710
711
712

713


714
715
716
717
718
719
720
		_resumedFrom = 0;

	if (!_quiet) {
		OFString *lengthString =
		    [headers objectForKey: @"Content-Length"];
		OFString *type = [headers objectForKey: @"Content-Type"];


		[of_stdout writeFormat: @" ➜ %hd\n", statusCode];



		if (type == nil)
			type = OF_LOCALIZED(@"type_unknown", @"unknown");

		if (lengthString != nil) {
			_length = lengthString.unsignedLongLongValue;








>
|
>
>







709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
		_resumedFrom = 0;

	if (!_quiet) {
		OFString *lengthString =
		    [headers objectForKey: @"Content-Length"];
		OFString *type = [headers objectForKey: @"Content-Type"];

		if (_useUnicode)
			[OFStdOut writeFormat: @" ➜ %hd\n", statusCode];
		else
			[OFStdOut writeFormat: @" -> %hd\n", statusCode];

		if (type == nil)
			type = OF_LOCALIZED(@"type_unknown", @"unknown");

		if (lengthString != nil) {
			_length = lengthString.unsignedLongLongValue;

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
			OFEnumerator OF_GENERIC(OFString *) *keyEnumerator =
			    [headers keyEnumerator];
			OFEnumerator OF_GENERIC(OFString *) *objectEnumerator =
			    [headers objectEnumerator];
			OFString *key, *object;

			if (statusCode / 100 == 2 && _currentFileName != nil) {
				[of_stdout writeString: @"  "];
				[of_stdout writeLine: OF_LOCALIZED(
				    @"info_name_unaligned",
				    @"Name: %[name]",
				    @"name", _currentFileName)];
			}

			while ((key = [keyEnumerator nextObject]) != nil &&
			    (object = [objectEnumerator nextObject]) != nil)
				[of_stdout writeFormat: @"  %@: %@\n",
							key, object];

			objc_autoreleasePoolPop(pool);
		} else if (statusCode / 100 == 2 && !_detectFileNameRequest) {
			[of_stdout writeString: @"  "];


			[of_stdout writeLine: OF_LOCALIZED(@"info_name",
			    @"Name: %[name]",
			    @"name", _currentFileName)];

			[of_stdout writeString: @"  "];
			[of_stdout writeLine: OF_LOCALIZED(@"info_type",
			    @"Type: %[type]",
			    @"type", type)];
			[of_stdout writeString: @"  "];
			[of_stdout writeLine: OF_LOCALIZED(@"info_size",
			    @"Size: %[size]",
			    @"size", lengthString)];
		}
	}
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response
	  exception: (id)exception
{
	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFResolveHostFailedException class]]) {
			if (!_quiet)
				[of_stdout writeString: @"\n"];

			[of_stderr writeLine:
			    OF_LOCALIZED(@"download_resolve_host_failed",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  Failed to resolve host: %[exception]",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string,
			    @"exception", exception)];
		} else if ([exception isKindOfClass:
		    [OFConnectionFailedException class]]) {
			if (!_quiet)
				[of_stdout writeString: @"\n"];

			[of_stderr writeLine:
			    OF_LOCALIZED(@"download_failed_connection_failed",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  Connection failed: %[exception]",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string,
			    @"exception", exception)];
		} else if ([exception isKindOfClass:
		    [OFInvalidServerReplyException class]]) {
			if (!_quiet)
				[of_stdout writeString: @"\n"];

			[of_stderr writeLine: OF_LOCALIZED(
			    @"download_failed_invalid_server_reply",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  Invalid server reply!",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string)];
		} else if ([exception isKindOfClass:
		    [OFUnsupportedProtocolException class]]) {
			if (!_quiet)
				[of_stdout writeString: @"\n"];

			[of_stderr writeLine: OF_LOCALIZED(@"no_ssl_library",
			    @"%[prog]: No TLS library loaded!\n"
			    @"  In order to download via https, you need to "
			    @"preload an TLS library for ObjFW\n"
			    @"  such as ObjOpenSSL!",

			    @"prog", [OFApplication programName])];
		} else if ([exception isKindOfClass:
		    [OFReadOrWriteFailedException class]]) {
			OFString *error = OF_LOCALIZED(
			    @"download_failed_read_or_write_failed_any",
			    @"Read or write failed");

			if (!_quiet)
				[of_stdout writeString: @"\n"];

			if ([exception isKindOfClass:
			    [OFReadFailedException class]])
				error = OF_LOCALIZED(
				    @"download_failed_read_or_write_failed_"
				    @"read",
				    @"Read failed");
			else if ([exception isKindOfClass:
			    [OFWriteFailedException class]])
				error = OF_LOCALIZED(
				    @"download_failed_read_or_write_failed_"
				    @"write",
				    @"Write failed");

			[of_stderr writeLine: OF_LOCALIZED(
			    @"download_failed_read_or_write_failed",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  %[error]: %[exception]",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string,
			    @"error", error,
			    @"exception", exception)];
		} else if ([exception isKindOfClass:
		    [OFHTTPRequestFailedException class]]) {
			short statusCode;
			OFString *codeString;

			if (_ignoreStatus) {
				exception = nil;
				goto after_exception_handling;
			}

			statusCode = response.statusCode;
			codeString = [OFString stringWithFormat: @"%hd %@",
			    statusCode,
			    of_http_status_code_to_string(statusCode)];
			[of_stderr writeLine: OF_LOCALIZED(@"download_failed",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  HTTP status code: %[code]",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string,
			    @"code", codeString)];
		} else
			@throw exception;

		_errorCode = 1;
		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return;
	}

after_exception_handling:
	if (_method == OF_HTTP_REQUEST_METHOD_HEAD)
		goto next;

	if (_detectFileNameRequest) {
		_currentFileName = [fileNameFromContentDisposition(
		    [response.headers objectForKey: @"Content-Disposition"])
		    copy];
		_detectedFileName = true;

		/* Handle this URL on the next -[downloadNextURL] call */
		_URLIndex--;

		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return;
	}

	if ([_outputPath isEqual: @"-"])
		_output = of_stdout;
	else {
		if (!_continue && !_force && [[OFFileManager defaultManager]
		    fileExistsAtPath: _currentFileName]) {
			[of_stderr writeLine:
			    OF_LOCALIZED(@"output_already_exists",
			    @"%[prog]: File %[filename] already exists!",
			    @"prog", [OFApplication programName],
			    @"filename", _currentFileName)];

			_errorCode = 1;
			goto next;
		}

		@try {
			OFString *mode =
			    (response.statusCode == 206 ? @"a" : @"w");
			_output = [[OFFile alloc] initWithPath: _currentFileName
							  mode: mode];
		} @catch (OFOpenItemFailedException *e) {
			[of_stderr writeLine:
			    OF_LOCALIZED(@"failed_to_open_output",
			    @"%[prog]: Failed to open file %[filename]: "
			    @"%[exception]",
			    @"prog", [OFApplication programName],
			    @"filename", _currentFileName,
			    @"exception", e)];

			_errorCode = 1;
			goto next;
		}
	}

	if (!_quiet) {
		_progressBar = [[ProgressBar alloc]
		    initWithLength: _length
		       resumedFrom: _resumedFrom];

		[_progressBar setReceived: _received];
		[_progressBar draw];
	}

	[_currentFileName release];
	_currentFileName = nil;

	response.delegate = self;
	[response asyncReadIntoBuffer: _buffer
			       length: [OFSystemInfo pageSize]];
	return;

next:
	[_currentFileName release];
	_currentFileName = nil;

	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}

- (void)downloadNextURL
{
	OFString *URLString = nil;
	OFURL *URL;
	OFMutableDictionary *clientHeaders;
	OFHTTPRequest *request;

	_received = _length = _resumedFrom = 0;

	if (_output != of_stdout)
		[_output release];
	_output = nil;

	if (_URLIndex >= _URLs.count)
		[OFApplication terminateWithStatus: _errorCode];

	@try {
		URLString = [_URLs objectAtIndex: _URLIndex++];
		URL = [OFURL URLWithString: URLString];
	} @catch (OFInvalidFormatException *e) {
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_url",
		    @"%[prog]: Invalid URL: <%[url]>!",
		    @"prog", [OFApplication programName],
		    @"url", URLString)];

		_errorCode = 1;
		goto next;
	}

	if (![URL.scheme isEqual: @"http"] && ![URL.scheme isEqual: @"https"]) {
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_scheme",
		    @"%[prog]: Invalid scheme: <%[url]>!",
		    @"prog", [OFApplication programName],
		    @"url", URLString)];

		_errorCode = 1;
		goto next;
	}

	clientHeaders = [[_clientHeaders mutableCopy] autorelease];

	if (_detectFileName && !_detectedFileName) {
		if (!_quiet)

			[of_stdout writeFormat: @"⠒ %@", URL.string];




		request = [OFHTTPRequest requestWithURL: URL];
		request.headers = clientHeaders;
		request.method = OF_HTTP_REQUEST_METHOD_HEAD;

		_detectFileNameRequest = true;
		[_HTTPClient asyncPerformRequest: request];
		return;
	}

	if (!_detectedFileName) {
		[_currentFileName release];
		_currentFileName = nil;
	} else
		_detectedFileName = false;

	if (_currentFileName == nil)
		_currentFileName = [_outputPath copy];

	if (_currentFileName == nil)
		_currentFileName = [URL.path.lastPathComponent copy];









	if (_continue) {
		@try {
			unsigned long long size =
			    [[OFFileManager defaultManager]
			    attributesOfItemAtPath: _currentFileName].fileSize;
			OFString *range;

			if (size > ULLONG_MAX)
				@throw [OFOutOfRangeException exception];

			_resumedFrom = (unsigned long long)size;

			range = [OFString stringWithFormat: @"bytes=%jd-",
							    _resumedFrom];
			[clientHeaders setObject: range
					  forKey: @"Range"];
		} @catch (OFRetrieveItemAttributesFailedException *e) {
		}
	}

	if (!_quiet)

		[of_stdout writeFormat: @"⇣ %@", URL.string];




	request = [OFHTTPRequest requestWithURL: URL];
	request.headers = clientHeaders;
	request.method = _method;

	_detectFileNameRequest = false;
	[_HTTPClient asyncPerformRequest: request];
	return;

next:
	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}
@end







|
|







|
|



|
>
>
|
|
|
>
|
|


|
|















|

|









|

|









|

|








|

|
|
|
|
|
>








|














|



















|
<
|















|

















|



|















|















|
>








|
<






|
<











|










|









|











|
>
|
>
>
>



|


















>
>
>
>
>
>
>
>














|
<




|
>
|
>
>
>










|
<


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
			OFEnumerator OF_GENERIC(OFString *) *keyEnumerator =
			    [headers keyEnumerator];
			OFEnumerator OF_GENERIC(OFString *) *objectEnumerator =
			    [headers objectEnumerator];
			OFString *key, *object;

			if (statusCode / 100 == 2 && _currentFileName != nil) {
				[OFStdOut writeString: @"  "];
				[OFStdOut writeLine: OF_LOCALIZED(
				    @"info_name_unaligned",
				    @"Name: %[name]",
				    @"name", _currentFileName)];
			}

			while ((key = [keyEnumerator nextObject]) != nil &&
			    (object = [objectEnumerator nextObject]) != nil)
				[OFStdOut writeFormat: @"  %@: %@\n",
						       key, object];

			objc_autoreleasePoolPop(pool);
		} else if (statusCode / 100 == 2 && !_detectFileNameRequest) {
			[OFStdOut writeString: @"  "];

			if (_currentFileName != nil)
				[OFStdOut writeLine: OF_LOCALIZED(@"info_name",
				    @"Name: %[name]",
				    @"name", _currentFileName)];

			[OFStdOut writeString: @"  "];
			[OFStdOut writeLine: OF_LOCALIZED(@"info_type",
			    @"Type: %[type]",
			    @"type", type)];
			[OFStdOut writeString: @"  "];
			[OFStdOut writeLine: OF_LOCALIZED(@"info_size",
			    @"Size: %[size]",
			    @"size", lengthString)];
		}
	}
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response
	  exception: (id)exception
{
	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFResolveHostFailedException class]]) {
			if (!_quiet)
				[OFStdOut writeString: @"\n"];

			[OFStdErr writeLine:
			    OF_LOCALIZED(@"download_resolve_host_failed",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  Failed to resolve host: %[exception]",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string,
			    @"exception", exception)];
		} else if ([exception isKindOfClass:
		    [OFConnectionFailedException class]]) {
			if (!_quiet)
				[OFStdOut writeString: @"\n"];

			[OFStdErr writeLine:
			    OF_LOCALIZED(@"download_failed_connection_failed",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  Connection failed: %[exception]",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string,
			    @"exception", exception)];
		} else if ([exception isKindOfClass:
		    [OFInvalidServerReplyException class]]) {
			if (!_quiet)
				[OFStdOut writeString: @"\n"];

			[OFStdErr writeLine: OF_LOCALIZED(
			    @"download_failed_invalid_server_reply",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  Invalid server reply!",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string)];
		} else if ([exception isKindOfClass:
		    [OFUnsupportedProtocolException class]]) {
			if (!_quiet)
				[OFStdOut writeString: @"\n"];

			[OFStdErr writeLine: OF_LOCALIZED(@"no_tls_support",
			    @"%[prog]: No TLS support in ObjFW!\n"
			    @"  In order to download via HTTPS, you need to "
			    @"either build ObjFW with TLS\n"
			    @"  support or preload a library adding TLS "
			    @"support to ObjFW!",
			    @"prog", [OFApplication programName])];
		} else if ([exception isKindOfClass:
		    [OFReadOrWriteFailedException class]]) {
			OFString *error = OF_LOCALIZED(
			    @"download_failed_read_or_write_failed_any",
			    @"Read or write failed");

			if (!_quiet)
				[OFStdOut writeString: @"\n"];

			if ([exception isKindOfClass:
			    [OFReadFailedException class]])
				error = OF_LOCALIZED(
				    @"download_failed_read_or_write_failed_"
				    @"read",
				    @"Read failed");
			else if ([exception isKindOfClass:
			    [OFWriteFailedException class]])
				error = OF_LOCALIZED(
				    @"download_failed_read_or_write_failed_"
				    @"write",
				    @"Write failed");

			[OFStdErr writeLine: OF_LOCALIZED(
			    @"download_failed_read_or_write_failed",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  %[error]: %[exception]",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string,
			    @"error", error,
			    @"exception", exception)];
		} else if ([exception isKindOfClass:
		    [OFHTTPRequestFailedException class]]) {
			short statusCode;
			OFString *codeString;

			if (_ignoreStatus) {
				exception = nil;
				goto after_exception_handling;
			}

			statusCode = response.statusCode;
			codeString = [OFString stringWithFormat: @"%hd %@",
			    statusCode, OFHTTPStatusCodeString(statusCode)];

			[OFStdErr writeLine: OF_LOCALIZED(@"download_failed",
			    @"%[prog]: Failed to download <%[url]>!\n"
			    @"  HTTP status code: %[code]",
			    @"prog", [OFApplication programName],
			    @"url", request.URL.string,
			    @"code", codeString)];
		} else
			@throw exception;

		_errorCode = 1;
		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return;
	}

after_exception_handling:
	if (_method == OFHTTPRequestMethodHead)
		goto next;

	if (_detectFileNameRequest) {
		_currentFileName = [fileNameFromContentDisposition(
		    [response.headers objectForKey: @"Content-Disposition"])
		    copy];
		_detectedFileName = true;

		/* Handle this URL on the next -[downloadNextURL] call */
		_URLIndex--;

		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return;
	}

	if ([_outputPath isEqual: @"-"])
		_output = [OFStdOut copy];
	else {
		if (!_continue && !_force && [[OFFileManager defaultManager]
		    fileExistsAtPath: _currentFileName]) {
			[OFStdErr writeLine:
			    OF_LOCALIZED(@"output_already_exists",
			    @"%[prog]: File %[filename] already exists!",
			    @"prog", [OFApplication programName],
			    @"filename", _currentFileName)];

			_errorCode = 1;
			goto next;
		}

		@try {
			OFString *mode =
			    (response.statusCode == 206 ? @"a" : @"w");
			_output = [[OFFile alloc] initWithPath: _currentFileName
							  mode: mode];
		} @catch (OFOpenItemFailedException *e) {
			[OFStdErr writeLine:
			    OF_LOCALIZED(@"failed_to_open_output",
			    @"%[prog]: Failed to open file %[filename]: "
			    @"%[exception]",
			    @"prog", [OFApplication programName],
			    @"filename", _currentFileName,
			    @"exception", e)];

			_errorCode = 1;
			goto next;
		}
	}

	if (!_quiet) {
		_progressBar = [[ProgressBar alloc]
		    initWithLength: _length
		       resumedFrom: _resumedFrom
			useUnicode: _useUnicode];
		[_progressBar setReceived: _received];
		[_progressBar draw];
	}

	[_currentFileName release];
	_currentFileName = nil;

	response.delegate = self;
	[response asyncReadIntoBuffer: _buffer length: [OFSystemInfo pageSize]];

	return;

next:
	[_currentFileName release];
	_currentFileName = nil;

	[self performSelector: @selector(downloadNextURL) afterDelay: 0];

}

- (void)downloadNextURL
{
	OFString *URLString = nil;
	OFURL *URL;
	OFMutableDictionary *clientHeaders;
	OFHTTPRequest *request;

	_received = _length = _resumedFrom = 0;

	if (_output != OFStdOut)
		[_output release];
	_output = nil;

	if (_URLIndex >= _URLs.count)
		[OFApplication terminateWithStatus: _errorCode];

	@try {
		URLString = [_URLs objectAtIndex: _URLIndex++];
		URL = [OFURL URLWithString: URLString];
	} @catch (OFInvalidFormatException *e) {
		[OFStdErr writeLine: OF_LOCALIZED(@"invalid_url",
		    @"%[prog]: Invalid URL: <%[url]>!",
		    @"prog", [OFApplication programName],
		    @"url", URLString)];

		_errorCode = 1;
		goto next;
	}

	if (![URL.scheme isEqual: @"http"] && ![URL.scheme isEqual: @"https"]) {
		[OFStdErr writeLine: OF_LOCALIZED(@"invalid_scheme",
		    @"%[prog]: Invalid scheme: <%[url]>!",
		    @"prog", [OFApplication programName],
		    @"url", URLString)];

		_errorCode = 1;
		goto next;
	}

	clientHeaders = [[_clientHeaders mutableCopy] autorelease];

	if (_detectFileName && !_detectedFileName) {
		if (!_quiet) {
			if (_useUnicode)
				[OFStdOut writeFormat: @"⠒ %@", URL.string];
			else
				[OFStdOut writeFormat: @"? %@", URL.string];
		}

		request = [OFHTTPRequest requestWithURL: URL];
		request.headers = clientHeaders;
		request.method = OFHTTPRequestMethodHead;

		_detectFileNameRequest = true;
		[_HTTPClient asyncPerformRequest: request];
		return;
	}

	if (!_detectedFileName) {
		[_currentFileName release];
		_currentFileName = nil;
	} else
		_detectedFileName = false;

	if (_currentFileName == nil)
		_currentFileName = [_outputPath copy];

	if (_currentFileName == nil)
		_currentFileName = [URL.path.lastPathComponent copy];

	if ([_currentFileName isEqual: @"/"]) {
		[_currentFileName release];
		_currentFileName = nil;
	}

	if (_currentFileName == nil)
		_currentFileName = @"unnamed";

	if (_continue) {
		@try {
			unsigned long long size =
			    [[OFFileManager defaultManager]
			    attributesOfItemAtPath: _currentFileName].fileSize;
			OFString *range;

			if (size > ULLONG_MAX)
				@throw [OFOutOfRangeException exception];

			_resumedFrom = (unsigned long long)size;

			range = [OFString stringWithFormat: @"bytes=%jd-",
							    _resumedFrom];
			[clientHeaders setObject: range forKey: @"Range"];

		} @catch (OFRetrieveItemAttributesFailedException *e) {
		}
	}

	if (!_quiet) {
		if (_useUnicode)
			[OFStdOut writeFormat: @"⇣ %@", URL.string];
		else
			[OFStdOut writeFormat: @"< %@", URL.string];
	}

	request = [OFHTTPRequest requestWithURL: URL];
	request.headers = clientHeaders;
	request.method = _method;

	_detectFileNameRequest = false;
	[_HTTPClient asyncPerformRequest: request];
	return;

next:
	[self performSelector: @selector(downloadNextURL) afterDelay: 0];

}
@end

Modified utils/ofhttp/ProgressBar.h from [187685081a] to [be2c97fe71].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
@class OFDate;
@class OFTimer;

#define BPS_WINDOW_SIZE 10

@interface ProgressBar: OFObject
{

	unsigned long long _received, _lastReceived, _length, _resumedFrom;
	OFDate *_startDate, *_lastReceivedDate;
	OFTimer *_drawTimer, *_BPSTimer;
	bool _stopped;
	float _BPS;
	double _ETA;
	float _BPSWindow[BPS_WINDOW_SIZE];
	size_t _BPSWindowIndex, _BPSWindowLength;
}

- (instancetype)initWithLength: (unsigned long long)length
		   resumedFrom: (unsigned long long)resumedFrom;

- (void)setReceived: (unsigned long long)received;
- (void)draw;
- (void)calculateBPSAndETA;
- (void)stop;
@end







>











|
>





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
@class OFDate;
@class OFTimer;

#define BPS_WINDOW_SIZE 10

@interface ProgressBar: OFObject
{
	bool _useUnicode;
	unsigned long long _received, _lastReceived, _length, _resumedFrom;
	OFDate *_startDate, *_lastReceivedDate;
	OFTimer *_drawTimer, *_BPSTimer;
	bool _stopped;
	float _BPS;
	double _ETA;
	float _BPSWindow[BPS_WINDOW_SIZE];
	size_t _BPSWindowIndex, _BPSWindowLength;
}

- (instancetype)initWithLength: (unsigned long long)length
		   resumedFrom: (unsigned long long)resumedFrom
		    useUnicode: (bool)useUnicode OF_DESIGNATED_INITIALIZER;
- (void)setReceived: (unsigned long long)received;
- (void)draw;
- (void)calculateBPSAndETA;
- (void)stop;
@end

Modified utils/ofhttp/ProgressBar.m from [814de8c246] to [9a2768256d].

1
2
3
4
5
6
7
8
9
10
11
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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.
 *

<
<
|







1


2
3
4
5
6
7
8
9
/*


 * 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.
 *
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
#import "OFDate.h"
#import "OFStdIOStream.h"
#import "OFTimer.h"
#import "OFLocale.h"

#import "ProgressBar.h"



#define GIBIBYTE (1024 * 1024 * 1024)
#define MEBIBYTE (1024 * 1024)

#define KIBIBYTE (1024)






#define UPDATE_INTERVAL 0.1


@implementation ProgressBar
- (instancetype)initWithLength: (unsigned long long)length
		   resumedFrom: (unsigned long long)resumedFrom

{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();


		_length = length;
		_resumedFrom = resumedFrom;
		_startDate = [[OFDate alloc] init];
		_lastReceivedDate = [[OFDate alloc] init];
		_drawTimer = [[OFTimer
		    scheduledTimerWithTimeInterval: UPDATE_INTERVAL
					    target: self
					  selector: @selector(draw)
					   repeats: true] retain];
		_BPSTimer = [[OFTimer
		    scheduledTimerWithTimeInterval: 1.0
					    target: self
					  selector: @selector(







>
>
|
|
>
|
>
>
>
>

>
|
>




>






>





|







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
#import "OFDate.h"
#import "OFStdIOStream.h"
#import "OFTimer.h"
#import "OFLocale.h"

#import "ProgressBar.h"

static const float oneKibibyte = 1024;
static const float oneMebibyte = 1024 * 1024;
static const float oneGibibyte = 1024 * 1024 * 1024;

static const OFTimeInterval updateInterval = 0.1;

#ifdef OF_MINT
/* freemint-gcc does not have trunc() */
# define trunc(x) ((int64_t)(x))
#endif

#ifndef HAVE_TRUNCF
# define truncf(x) trunc(x)
#endif

@implementation ProgressBar
- (instancetype)initWithLength: (unsigned long long)length
		   resumedFrom: (unsigned long long)resumedFrom
		    useUnicode: (bool)useUnicode
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();

		_useUnicode = useUnicode;
		_length = length;
		_resumedFrom = resumedFrom;
		_startDate = [[OFDate alloc] init];
		_lastReceivedDate = [[OFDate alloc] init];
		_drawTimer = [[OFTimer
		    scheduledTimerWithTimeInterval: updateInterval
					    target: self
					  selector: @selector(draw)
					   repeats: true] retain];
		_BPSTimer = [[OFTimer
		    scheduledTimerWithTimeInterval: 1.0
					    target: self
					  selector: @selector(
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
}

- (void)_drawProgress
{
	float bars, percent;
	int columns, barWidth;

	if ((columns = of_stdout.columns) >= 0) {
		if (columns > 37)
			barWidth = columns - 37;
		else
			barWidth = 0;
	} else
		barWidth = 43;

	bars = (float)(_resumedFrom + _received) /
	    (float)(_resumedFrom + _length) * barWidth;
	percent = (float)(_resumedFrom + _received) /
	    (float)(_resumedFrom + _length) * 100;


	[of_stdout writeString: @"\r  ▕"];

	for (size_t i = 0; i < (size_t)bars; i++)
		[of_stdout writeString: @"█"];
	if (bars < barWidth) {
		float rem = bars - truncf(bars);

		if (rem >= 0.875)
			[of_stdout writeString: @"▉"];
		else if (rem >= 0.75)
			[of_stdout writeString: @"▊"];
		else if (rem >= 0.625)
			[of_stdout writeString: @"▋"];
		else if (rem >= 0.5)



















			[of_stdout writeString: @""];



		else if (rem >= 0.375)
			[of_stdout writeString: @""];
		else if (rem >= 0.25)
			[of_stdout writeString: @""];
		else if (rem >= 0.125)
			[of_stdout writeString: @""];
		else
			[of_stdout writeString: @" "];

		for (size_t i = 0; i < barWidth - (size_t)bars - 1; i++)
			[of_stdout writeString: @" "];
	}

	[of_stdout writeFormat: @" %,6.2f%% ", percent];


	if (percent == 100) {
		double timeInterval = -_startDate.timeIntervalSinceNow;

		_BPS = (float)_received / (float)timeInterval;
		_ETA = timeInterval;
	}

	if (isinf(_ETA))
		[of_stdout writeString: @"--:--:-- "];
	else if (_ETA >= 99 * 3600) {
		OFString *num = [OFString stringWithFormat:
		    @"%,4.2f", _ETA / (24 * 3600)];
		[of_stdout writeString: OF_LOCALIZED(@"eta_days",
		    @"%[num] d ",
		    @"num", num)];
	} else
		[of_stdout writeFormat: @"%2u:%02u:%02u ",
		    (uint8_t)(_ETA / 3600), (uint8_t)(_ETA / 60) % 60,
		    (uint8_t)_ETA % 60];

	if (_BPS >= GIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / GIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_gibs",
		    @"%[num] GiB/s",
		    @"num", num)];
	} else if (_BPS >= MEBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / MEBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_mibs",
		    @"%[num] MiB/s",
		    @"num", num)];
	} else if (_BPS >= KIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / KIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_kibs",
		    @"%[num] KiB/s",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS];
		[of_stdout writeString: OF_LOCALIZED(@"progress_bps",
		    @"%[num] B/s  ",
		    @"num", num)];
	}
}

- (void)_drawReceived
{
	[of_stdout writeString: @"\r  "];

	if (_resumedFrom + _received >= GIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", (float)(_resumedFrom + _received) / GIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_gib",
		    @"%[num] GiB",
		    @"num", num)];
	} else if (_resumedFrom + _received >= MEBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", (float)(_resumedFrom + _received) / MEBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_mib",
		    @"%[num] MiB",
		    @"num", num)];
	} else if (_resumedFrom + _received >= KIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", (float)(_resumedFrom + _received) / KIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_kib",
		    @"%[num] KiB",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%jd", _resumedFrom + _received];
		[of_stdout writeString: OF_LOCALIZED(@"progress_bytes",
		    @"["
		    @"    ["
		    @"        {'num == 1': '1 byte '},"
		    @"        {'': '%[num] bytes'}"
		    @"    ]"
		    @"]".objectByParsingJSON,
		    @"num", num)];
	}

	[of_stdout writeString: @" "];

	if (_stopped)
		_BPS = (float)_received /
		    -(float)_startDate.timeIntervalSinceNow;

	if (_BPS >= GIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / GIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_gibs",
		    @"%[num] GiB/s",
		    @"num", num)];
	} else if (_BPS >= MEBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / MEBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_mibs",
		    @"%[num] MiB/s",
		    @"num", num)];
	} else if (_BPS >= KIBIBYTE) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / KIBIBYTE];
		[of_stdout writeString: OF_LOCALIZED(@"progress_kibs",
		    @"%[num] KiB/s",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS];
		[of_stdout writeString: OF_LOCALIZED(@"progress_bps",
		    @"%[num] B/s  ",
		    @"num", num)];
	}
}

- (void)draw
{







|












>
|

|
|
|
|

|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
|
|
|
|
|
|
|
|

|
|
|

|
>









|



|



|



|

|
|


|

|
|


|

|
|





|







|

|

|
|


|

|
|


|

|
|





|









|





|

|
|


|

|
|


|

|
|





|







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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
}

- (void)_drawProgress
{
	float bars, percent;
	int columns, barWidth;

	if ((columns = OFStdOut.columns) >= 0) {
		if (columns > 37)
			barWidth = columns - 37;
		else
			barWidth = 0;
	} else
		barWidth = 43;

	bars = (float)(_resumedFrom + _received) /
	    (float)(_resumedFrom + _length) * barWidth;
	percent = (float)(_resumedFrom + _received) /
	    (float)(_resumedFrom + _length) * 100;

	if (_useUnicode) {
		[OFStdOut writeString: @"\r  ▕"];

		for (size_t i = 0; i < (size_t)bars; i++)
			[OFStdOut writeString: @"█"];
		if (bars < barWidth) {
			float rem = bars - truncf(bars);

			if (rem >= 0.875)
				[OFStdOut writeString: @"▉"];
			else if (rem >= 0.75)
				[OFStdOut writeString: @"▊"];
			else if (rem >= 0.625)
				[OFStdOut writeString: @"▋"];
			else if (rem >= 0.5)
				[OFStdOut writeString: @"▌"];
			else if (rem >= 0.375)
				[OFStdOut writeString: @"▍"];
			else if (rem >= 0.25)
				[OFStdOut writeString: @"▎"];
			else if (rem >= 0.125)
				[OFStdOut writeString: @"▏"];
			else
				[OFStdOut writeString: @" "];

			for (size_t i = 0; i < barWidth - (size_t)bars - 1; i++)
				[OFStdOut writeString: @" "];
		}

		[OFStdOut writeFormat: @"▏ %,6.2f%% ", percent];
	} else {
		[OFStdOut writeString: @"\r  ["];

		for (size_t i = 0; i < (size_t)bars; i++)
			[OFStdOut writeString: @"#"];
		if (bars < barWidth) {
			float rem = bars - truncf(bars);

			if (rem >= 0.75)
				[OFStdOut writeString: @"O"];
			else if (rem >= 0.5)
				[OFStdOut writeString: @"o"];
			else if (rem >= 0.25)
				[OFStdOut writeString: @"."];
			else
				[OFStdOut writeString: @" "];

			for (size_t i = 0; i < barWidth - (size_t)bars - 1; i++)
				[OFStdOut writeString: @" "];
		}

		[OFStdOut writeFormat: @"] %,6.2f%% ", percent];
	}

	if (percent == 100) {
		double timeInterval = -_startDate.timeIntervalSinceNow;

		_BPS = (float)_received / (float)timeInterval;
		_ETA = timeInterval;
	}

	if (isinf(_ETA))
		[OFStdOut writeString: @"--:--:-- "];
	else if (_ETA >= 99 * 3600) {
		OFString *num = [OFString stringWithFormat:
		    @"%,4.2f", _ETA / (24 * 3600)];
		[OFStdOut writeString: OF_LOCALIZED(@"eta_days",
		    @"%[num] d ",
		    @"num", num)];
	} else
		[OFStdOut writeFormat: @"%2u:%02u:%02u ",
		    (uint8_t)(_ETA / 3600), (uint8_t)(_ETA / 60) % 60,
		    (uint8_t)_ETA % 60];

	if (_BPS >= oneGibibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / oneGibibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_gibs",
		    @"%[num] GiB/s",
		    @"num", num)];
	} else if (_BPS >= oneMebibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / oneMebibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_mibs",
		    @"%[num] MiB/s",
		    @"num", num)];
	} else if (_BPS >= oneKibibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / oneKibibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_kibs",
		    @"%[num] KiB/s",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_bps",
		    @"%[num] B/s  ",
		    @"num", num)];
	}
}

- (void)_drawReceived
{
	[OFStdOut writeString: @"\r  "];

	if (_resumedFrom + _received >= oneGibibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", (float)(_resumedFrom + _received) / oneGibibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_gib",
		    @"%[num] GiB",
		    @"num", num)];
	} else if (_resumedFrom + _received >= oneMebibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", (float)(_resumedFrom + _received) / oneMebibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_mib",
		    @"%[num] MiB",
		    @"num", num)];
	} else if (_resumedFrom + _received >= oneKibibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", (float)(_resumedFrom + _received) / oneKibibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_kib",
		    @"%[num] KiB",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%jd", _resumedFrom + _received];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_bytes",
		    @"["
		    @"    ["
		    @"        {'num == 1': '1 byte '},"
		    @"        {'': '%[num] bytes'}"
		    @"    ]"
		    @"]".objectByParsingJSON,
		    @"num", num)];
	}

	[OFStdOut writeString: @" "];

	if (_stopped)
		_BPS = (float)_received /
		    -(float)_startDate.timeIntervalSinceNow;

	if (_BPS >= oneGibibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / oneGibibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_gibs",
		    @"%[num] GiB/s",
		    @"num", num)];
	} else if (_BPS >= oneMebibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / oneMebibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_mibs",
		    @"%[num] MiB/s",
		    @"num", num)];
	} else if (_BPS >= oneKibibyte) {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS / oneKibibyte];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_kibs",
		    @"%[num] KiB/s",
		    @"num", num)];
	} else {
		OFString *num = [OFString stringWithFormat:
		    @"%,7.2f", _BPS];
		[OFStdOut writeString: OF_LOCALIZED(@"progress_bps",
		    @"%[num] B/s  ",
		    @"num", num)];
	}
}

- (void)draw
{

Modified utils/ofhttp/lang/de.json from [ad71562e8d] to [027af9bd4c].

48
49
50
51
52
53
54
55
56
57
58
59


60
61
62
63
64
65
66
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  Verbindung fehlgeschlagen: %[exception]"
    ],
    "download_failed_invalid_server_reply": [
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  Ungültige Antwort vom Server!"
    ],
    "no_ssl_library": [
        "%[prog]: Keine TLS-Bibliothek geladen!\n",
        "  Um Dateien über https zu laden, müssen Sie eine TLS-Bibliothek für ",
        "ObjFW,\n",
        "  wie z.B. ObjOpenSSL, mittels LD_PRELOAD laden."


    ],
    "download_failed_read_or_write_failed_any": "Lesen oder Schreiben",
    "download_failed_read_or_write_failed_read": "Lesen",
    "download_failed_read_or_write_failed_write": "Schreiben",
    "download_failed_read_or_write_failed": [
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  %[error]: %[exception]"







|
|
|
|
|
>
>







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  Verbindung fehlgeschlagen: %[exception]"
    ],
    "download_failed_invalid_server_reply": [
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  Ungültige Antwort vom Server!"
    ],
    "no_tls_support": [
        "%[prog]: Keine TLS-Unterstützung in ObjFW!\n",
        "  Um via HTTPS runterzuladen, müssen Sie entweder ObjFW mit TLS-",
        "Unterstützung\n",
        "  kompilieren oder eine Bibliothek mittels „preoad” laden, welche ",
        "TLS-Support\n",
        "  zu ObjFW hinzufügt!"
    ],
    "download_failed_read_or_write_failed_any": "Lesen oder Schreiben",
    "download_failed_read_or_write_failed_read": "Lesen",
    "download_failed_read_or_write_failed_write": "Schreiben",
    "download_failed_read_or_write_failed": [
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  %[error]: %[exception]"

Deleted utils/ofsock/Makefile version [0438ba0691].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
include ../../extra.mk

PROG = ofsock${PROG_SUFFIX}
SRCS = OFSock.m

include ../../buildsys.mk

PACKAGE_NAME = ofsock

${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2}

CPPFLAGS += -I../../src					\
	    -I../../src/runtime				\
	    -I../../src/exceptions			\
	    -I../..
LIBS := -L../../src -lobjfw						\
	-L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS}	\
	${LIBS}
LD = ${OBJC}
LDFLAGS += ${LDFLAGS_RPATH}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































Deleted utils/ofsock/OFSock.m version [9ce3b460c5].

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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   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"

#import "OFApplication.h"
#import "OFArray.h"
#import "OFNumber.h"
#import "OFPair.h"
#import "OFStdIOStream.h"
#import "OFStream.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFURL.h"

#define BUFFER_LEN 4096

@interface OFSock: OFObject <OFApplicationDelegate, OFStreamDelegate>
{
	char _buffer[BUFFER_LEN];
	OFMutableArray OF_GENERIC(OFPair OF_GENERIC(OFStream *, OFStream *) *)
	    *_streams;
	int _errors;
}
@end

OF_APPLICATION_DELEGATE(OFSock)

static OFPair OF_GENERIC(OFStream *, OFStream *) *
streamFromString(OFString *string)
{
	OFURL *URL;
	OFString *scheme;

	if ([string isEqual: @"-"])
		return [OFPair pairWithFirstObject: of_stdin
				      secondObject: of_stdout];

	URL = [OFURL URLWithString: string];
	scheme = URL.scheme;

	if ([scheme isEqual: @"tcp"]) {
		OFTCPSocket *sock = [OFTCPSocket socket];

		if (URL.port == nil) {
			[of_stderr writeLine: @"Need a port!"];
			[OFApplication terminateWithStatus: 1];
		}

		[sock connectToHost: URL.host
			       port: URL.port.shortValue];

		return [OFPair pairWithFirstObject: sock
				      secondObject: sock];
	}

	[of_stderr writeFormat: @"Invalid protocol: %@\n", scheme];
	[OFApplication terminateWithStatus: 1];
	abort();
}

@implementation OFSock
- (void)applicationDidFinishLaunching
{
	OFArray OF_GENERIC(OFString *) *arguments = [OFApplication arguments];

	if (arguments.count < 1) {
		[of_stderr writeLine: @"Need at least one argument!"];
		[OFApplication terminateWithStatus: 1];
	}

	_streams = [[OFMutableArray alloc] init];

	for (OFString *argument in arguments) {
		OFPair *pair = streamFromString(argument);

		[pair.firstObject setDelegate: self];

		[_streams addObject: pair];
	}

	if (arguments.count == 1) {
		of_stdin.delegate = self;

		[_streams addObject:
		    [OFPair pairWithFirstObject: of_stdin
				   secondObject: of_stdout]];
	}

	for (OFPair *pair in _streams)
		[pair.firstObject asyncReadIntoBuffer: _buffer
					       length: BUFFER_LEN];
}

- (void)removeDeadStream: (OFStream *)stream
{
	size_t count = _streams.count;

	for (size_t i = 0; i < count; i++) {
		if ([[_streams objectAtIndex: i] firstObject] == stream) {
			[_streams removeObjectAtIndex: i];
			break;
		}
	}

	if (_streams.count < 2)
		[OFApplication terminateWithStatus: _errors];
}

-      (bool)stream: (OFStream *)stream
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	  exception: (id)exception
{
	if (exception != nil) {
		[of_stderr writeFormat: @"Exception on stream %@: %@\n",
					stream, exception];
		_errors++;
		[self removeDeadStream: stream];
		return false;
	}

	if (stream.atEndOfStream) {
		[self removeDeadStream: stream];
		return false;
	}

	for (OFPair *pair in _streams) {
		if (pair.firstObject == stream)
			continue;

		[pair.secondObject writeBuffer: buffer
					length: length];
	}

	return true;
}
@end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<