ObjFW  Check-in [370ec3dc84]

Overview
Comment:Merge trunk into 1.0 branch
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | 1.0
Files: files | file ages | folders
SHA3-256: 370ec3dc84fc8fffcf0c8daf7d90e0dcde9fecb866fdf71662199eff93de950e
User & Date: js on 2020-05-28 23:31:27
Other Links: branch diff | manifest | tags
Context
2020-05-28
23:45
Remove utils/ofsock, as it is not ready for 1.0 check-in: cc7caa19be user: js tags: 1.0
23:31
Merge trunk into 1.0 branch check-in: 370ec3dc84 user: js tags: 1.0
23:22
OFIPXSocket: Minor documentation fix check-in: 2d409b0c35 user: js tags: trunk
2020-03-02
20:24
Merge branch 'master' into 1.0 check-in: e3bbb35784 user: js tags: 1.0
Changes

Added .fossil-settings/clean-glob version [02ac598dea].

















































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*.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
DerivedData
docs
extra.mk
generators/gen_tables
src/Info.plist
src/bridge/Info.plist
src/objfw-defs.h
src/runtime/amiga-library-functable.inc
src/runtime/inline.h
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

Added .fossil-settings/ignore-glob version [fea53f5b92].






















































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*.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
DerivedData
docs
extra.mk
generators/gen_tables
src/Info.plist
src/bridge/Info.plist
src/objfw-defs.h
src/runtime/amiga-library-functable.inc
src/runtime/inline.h
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

Added .fossil-settings/keep-glob version [421e4b3d79].


1
+
.git

Modified .gitignore from [dbd7d8e2dc] to [79c7b883a0].

1
2
3
4
5


6

7
8
9
10
11
12


13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

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

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

14
15
16
17
18
19

20
21
22
23
24
25
26
27
28
29

30

31

32

33
34

35
36
37
38
39
40
41
42
43
44
45
46

47
48
49

50

51

52

53





+
+

+




-

+
+



-










-

-

-

-


-








+



-



-

-

-

-
+
*.a
*.bundle
*.dep
*.dll
*.dylib
*.exe
*.framework
*.library
*.map
*.o
*.orig
*.so
*.so.*
*~
.deps
.fslckout
_FOSSIL_
aclocal.m4
autom4te.cache
boot.dol
build
buildsys.mk
config.h
config.h.in
config.log
config.status
configure
DerivedData
docs
extra.mk
generators/gen_tables
generators/gen_tables.exe
src/Info.plist
src/ObjFW.framework
src/bridge/Info.plist
src/bridge/ObjFWBridge.framework
src/objfw-defs.h
src/runtime/ObjFWRT.framework
src/runtime/amiga-library-functable.inc
src/runtime/inline.h
tests/*.map
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.exe
tests/tests.nds
utils/objfw-config
utils/ofarc/ofarc
utils/ofarc/ofarc.exe
utils/ofdns/ofdns
utils/ofdns/ofdns.exe
utils/ofhash/ofhash
utils/ofhash/ofhash.exe
utils/ofhttp/ofhttp
utils/ofhttp/ofhttp.exe
utils/ofsock/ofsock

Modified .travis.yml from [a4ba9a887a] to [53acd6323f].

1
2
3
4
5
6





7
8

9

10

11
12

13

14

15
16

17

18

19
20

21

























22
23
24
25

26
27
28
















29
30
31
32
33
34
35
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18

19
20
21
22
23
24

25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87






+
+
+
+
+

-
+

+

+

-
+

+

+

-
+

+

+

-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







language: c

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

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

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

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

    - os: linux
      arch: ppc64le
      compiler: clang
      dist: xenial
      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: trusty
      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:
187
188
189
190
191
192
193
194
195
196













197

198

199
200
201




202
203
204
205
206
207
208
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







-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
-


+
+
+
+







    - os: linux
      dist: trusty
      env:
        - config=wii

before_install:
  - if [ "$TRAVIS_OS_NAME" = "linux" -a -z "$config" ]; then
            if ! sudo apt-get -qq update >/tmp/apt_log 2>&1; then
                    cat /tmp/apt_log;
                    exit 1;
            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;

            if ! sudo apt-get -qq install -y gobjc-multilib >/tmp/apt_log 2>&1;
            if ! sudo apt-get -qq install -y $pkgs >/tmp/apt_log 2>&1; then
            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"
        -o "$config" = "wii" ]; then
            wget https://github.com/devkitPro/pacman/releases/download/devkitpro-pacman-1.0.1/devkitpro-pacman.deb;
            sudo dpkg -i devkitpro-pacman.deb;
222
223
224
225
226
227
228



229




230
231
232
233
234
235
236
288
289
290
291
292
293
294
295
296
297

298
299
300
301
302
303
304
305
306
307
308







+
+
+
-
+
+
+
+








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

script:
  # This needs to use ed on macOS, as it has no GNU sed, and sed on Linux, as
  # some Travis hosts have no ed.
  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then
  - echo -e '%s/-DSTDOUT$/&_SIMPLE/\nwq' | ed -s tests/Makefile
            echo -e '%s/-DSTDOUT$/&_SIMPLE/\nwq' | ed -s tests/Makefile;
    else
            sed -i 's/-DSTDOUT$/&_SIMPLE/' tests/Makefile;
    fi

  - build() {
            if ! git clean -fxd >/tmp/clean_log 2>&1; then
                    cat /tmp/clean_log;
                    exit 1;
            fi;
            echo ">> Configuring with $@";
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
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







+
+
+
-
+
+
+
+
+
+
+
+




+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+







                    exit 1;
            fi;
    }

  - 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 $@;
                                    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;

            true 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-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;
            build_32_64 --disable-compiler-tls --disable-threads;
                    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 $@;

Modified Makefile from [8220802d40] to [c0716d54d0].

14
15
16
17
18
19
20
21

22
23
24


25
26


27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
14
15
16
17
18
19
20

21



22
23
24

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

39
40
41
42
43







-
+
-
-
-
+
+

-
+
+












-
+





utils tests: src

tarball:
	echo "Generating tarball for version ${PACKAGE_VERSION}..."
	rm -fr objfw-${PACKAGE_VERSION} objfw-${PACKAGE_VERSION}.tar \
		objfw-${PACKAGE_VERSION}.tar.gz
	mkdir objfw-${PACKAGE_VERSION}
	fossil tarball --name objfw-${PACKAGE_VERSION} current - \
	git --work-tree=objfw-${PACKAGE_VERSION} checkout .
	rm objfw-${PACKAGE_VERSION}/.gitignore \
		objfw-${PACKAGE_VERSION}/.travis.yml
		--exclude '.fossil-settings/*,.gitignore,.travis.yml' | \
		ofarc -ttgz -xq -
	cp configure config.h.in objfw-${PACKAGE_VERSION}/
	ofarc -cq objfw-${PACKAGE_VERSION}.tar $$(find objfw-${PACKAGE_VERSION})
	ofarc -cq objfw-${PACKAGE_VERSION}.tar \
		$$(find objfw-${PACKAGE_VERSION} | sort)
	rm -fr objfw-${PACKAGE_VERSION}
	gzip -9 objfw-${PACKAGE_VERSION}.tar
	rm -f objfw-${PACKAGE_VERSION}.tar
	gpg -b objfw-${PACKAGE_VERSION}.tar.gz || true
	echo "Generating documentation..."
	rm -fr docs
	doxygen >/dev/null
	rm -fr objfw-docs-${PACKAGE_VERSION} objfw-docs-${PACKAGE_VERSION}.tar \
		objfw-docs-${PACKAGE_VERSION}.tar.gz
	mv docs objfw-docs-${PACKAGE_VERSION}
	echo "Generating docs tarball for version ${PACKAGE_VERSION}..."
	ofarc -cq objfw-docs-${PACKAGE_VERSION}.tar \
		$$(find objfw-docs-${PACKAGE_VERSION})
		$$(find objfw-docs-${PACKAGE_VERSION} | sort)
	rm -fr objfw-docs-${PACKAGE_VERSION}
	gzip -9 objfw-docs-${PACKAGE_VERSION}.tar
	rm -f objfw-docs-${PACKAGE_VERSION}.tar
	gpg -b objfw-docs-${PACKAGE_VERSION}.tar.gz || true

Modified PLATFORMS.md from [d342907c41] to [98e6767a77].

76
77
78
79
80
81
82
83
84


85
86
87
88
89
90
91
76
77
78
79
80
81
82


83
84
85
86
87
88
89
90
91







-
-
+
+







  * Runtimes: Apple


Linux
-----

  * Architectures: Alpha, ARMv6, ARM64, Itanium, m68k, MIPS (O32), RISC-V 64,
		   PowerPC, SH4, x86, x86_64
  * Compilers: Clang 3.0-7.0, GCC 4.6-8.2
		   PowerPC, S390x, SH4, x86, x86_64
  * Compilers: Clang 3.0-9.0, GCC 4.6-8.2
  * Runtimes: ObjFW


macOS
-----

  * OS Versions: 10.5, 10.7-10.14, Darling

Modified README.md from [826335256c] to [c2eecac5f0].

10
11
12
13
14
15
16

17
18
19
20
21
22
23
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24







+







Table of Contents
=================

 * [Installation](#installation)
   * [macOS and iOS](#macos-and-ios)
     * [Building as a framework](#building-as-a-framework)
     * [Using the macOS or iOS framework in Xcode](#using-the-macos-or-ios-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-using-msys2)
     * [Getting, building and installing ObjFW](#getting-building-and-installing-objfw)
   * [Nintendo DS, Nintendo 3DS and Wii](#nintendo-ds-nintendo-3ds-and-wii)
     * [Nintendo DS](#nintendo-ds)
69
70
71
72
73
74
75























76
77
78
79
80
81
82
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








### Using the macOS or iOS framework in Xcode

  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

### Broken Xcode versions

  Some versions of Xcode shipped with a version of Clang that ignores
  `-fconstant-string-class=OFConstantString`. This will manifest in an error
  like this:

    OFAllocFailedException.m:94:10: error: cannot find interface declaration for
          'NSConstantString'
            return @"Allocating an object failed!";
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1 error generated.

  Unfortunately, there is no workaround for this other than to
  upgrade/downgrade Xcode or to build upstream Clang yourself.

  In particular, Xcode 11 Beta 1 to Beta 3 are known to be affected. While
  Xcode 11 Beta 4 to Xcode 11.3 work, the bug was unfortunately reintroduced in
  Xcode 11.4.1 and a fix is not expected before Xcode 11.6.

  You can get older versions of Xcode
  [here](https://developer.apple.com/download) by clicking on "More" in the
  top-right corner.

Windows
-------

  Windows is only officially supported when following these instructions, as
  there are many MinGW versions that behave slightly differently and often
  cause problems.

Modified build-aux/m4/buildsys.m4 from [a231c1e9f4] to [90f081db1f].

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







-
+

-
+






-
-
+
+







			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,${SHARED_LIB}.a'
			LIB_LDFLAGS='-shared -Wl,--export-all-symbols,--out-implib,lib${SHARED_LIB}.a'
			LIB_LDFLAGS_INSTALL_NAME=''
			LIB_PREFIX='lib'
			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 $$i.a ${DESTDIR}${libdir}/$$i.a'
			UNINSTALL_LIB='&& rm -f ${DESTDIR}${bindir}/$$i ${DESTDIR}${libdir}/$$i.a'
			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'

Modified buildsys.mk.in from [4053db3b86] to [32c1d977cd].

123
124
125
126
127
128
129
130
131
132
133
134
135






136
137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
123
124
125
126
127
128
129






130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152







-
-
-
-
-
-
+
+
+
+
+
+









-
+








.SILENT:
.SUFFIXES:
.SUFFIXES: .amigalib.o .beam .c .cc .class .cxx .d .erl .lib.o .java .mo .m .mm .o .plugin.o .po .py .pyc .rc .S .xpm
.PHONY: all subdirs subdirs-after pre-depend depend install install-extra uninstall uninstall-extra clean distclean locales copy-headers-into-framework ${SUBDIRS} ${SUBDIRS_AFTER}

all:
	${MAKE} pre-all
	${MAKE} subdirs
	${MAKE} depend
	${MAKE} ${STATIC_LIB} ${STATIC_LIB_NOINST} ${STATIC_PIC_LIB} ${STATIC_PIC_LIB_NOINST} ${SHARED_LIB} ${SHARED_LIB_NOINST} ${FRAMEWORK} ${FRAMEWORK_NOINST} ${AMIGA_LIB} ${AMIGA_LIB_NOINST} ${PLUGIN} ${PLUGIN_NOINST} ${PROG} ${PROG_NOINST} ${JARFILE} locales
	${MAKE} subdirs-after
	${MAKE} post-all
	${MAKE} -s pre-all
	${MAKE} -s subdirs
	${MAKE} -s depend
	${MAKE} -s ${STATIC_LIB} ${STATIC_LIB_NOINST} ${STATIC_PIC_LIB} ${STATIC_PIC_LIB_NOINST} ${SHARED_LIB} ${SHARED_LIB_NOINST} ${FRAMEWORK} ${FRAMEWORK_NOINST} ${AMIGA_LIB} ${AMIGA_LIB_NOINST} ${PLUGIN} ${PLUGIN_NOINST} ${PROG} ${PROG_NOINST} ${JARFILE} locales
	${MAKE} -s subdirs-after
	${MAKE} -s post-all

pre-all post-all:

subdirs: ${SUBDIRS}
subdirs-after: ${SUBDIRS_AFTER}

${SUBDIRS} ${SUBDIRS_AFTER}:
	for i in $@; do \
		${DIR_ENTER}; \
		${MAKE} || exit $$?; \
		${MAKE} -s || exit $$?; \
		${DIR_LEAVE}; \
	done

depend: pre-depend
	: >.deps
	for i in ${DEPS}; do \
		echo "-include \$${.CURDIR}/$$i" >>.deps; \
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
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







-
+










-
+







	else \
		${LINK_FAILED}; \
	fi

${FRAMEWORK} ${FRAMEWORK_NOINST}: ${EXT_DEPS} ${LIB_OBJS} ${LIB_OBJS_EXTRA}
	${LINK_STATUS}
	out="$@"; \
	if rm -fr $$out && ${MKDIR_P} $$out && ${MAKE} COPY_HEADERS_IF_SUBDIR=${includesubdir} COPY_HEADERS_DESTINATION=$$PWD/$@/Headers copy-headers-into-framework && if test -f Info.plist; then ${INSTALL} -m 644 Info.plist $$out/Info.plist; fi && if test -f module.modulemap; then ${MKDIR_P} $$out/Modules && ${INSTALL} -m 644 module.modulemap $$out/Modules/module.modulemap; fi && ${LD} -o $$out/$${out%.framework} ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${FRAMEWORK_LDFLAGS} ${FRAMEWORK_LDFLAGS_INSTALL_NAME} ${LDFLAGS} ${FRAMEWORK_LIBS} && ${CODESIGN} -fs ${CODESIGN_IDENTITY} --timestamp=none $$out; then \
	if rm -fr $$out && ${MKDIR_P} $$out && ${MAKE} -s COPY_HEADERS_IF_SUBDIR=${includesubdir} COPY_HEADERS_DESTINATION=$$PWD/$@/Headers copy-headers-into-framework && if test -f Info.plist; then ${INSTALL} -m 644 Info.plist $$out/Info.plist; fi && if test -f module.modulemap; then ${MKDIR_P} $$out/Modules && ${INSTALL} -m 644 module.modulemap $$out/Modules/module.modulemap; fi && ${LD} -o $$out/$${out%.framework} ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${FRAMEWORK_LDFLAGS} ${FRAMEWORK_LDFLAGS_INSTALL_NAME} ${LDFLAGS} ${FRAMEWORK_LIBS} && ${CODESIGN} -fs ${CODESIGN_IDENTITY} --timestamp=none $$out; then \
		${LINK_OK}; \
	else \
		rm -fr $$out; false; \
		${LINK_FAILED}; \
	fi

copy-headers-into-framework:
	for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \
		test x"$$i" = x"" && continue; \
		cd $$i || exit 1; \
		${MAKE} copy-headers-into-framework || exit $$?; \
		${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 $$?; \
609
610
611
612
613
614
615
616

617
618
619
620
621
622
623
609
610
611
612
613
614
615

616
617
618
619
620
621
622
623







-
+







		${COMPILE_PLUGIN_FAILED}; \
	fi

install: all install-extra
	for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \
		test x"$$i" = x"" && continue; \
		${DIR_ENTER}; \
		${MAKE} install || exit $$?; \
		${MAKE} -s install || exit $$?; \
		${DIR_LEAVE}; \
	done

	for i in "" ${SHARED_LIB}; do \
		test x"$$i" = x"" && continue; \
		${INSTALL_STATUS}; \
		if ${MKDIR_P} ${DESTDIR}${libdir} @INSTALL_LIB@; then \
722
723
724
725
726
727
728
729

730
731
732
733
734
735
736
722
723
724
725
726
727
728

729
730
731
732
733
734
735
736







-
+








install-extra:

uninstall: uninstall-extra
	for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \
		test x"$$i" = x"" && continue; \
		${DIR_ENTER}; \
		${MAKE} uninstall || exit $$?; \
		${MAKE} -s uninstall || exit $$?; \
		${DIR_LEAVE}; \
	done

	for i in "" ${SHARED_LIB}; do \
		test x"$$i" = x"" && continue; \
		if test -f ${DESTDIR}${libdir}/$$i; then \
			if : @UNINSTALL_LIB@; then \
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
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







-
+




















-
+



















-
+








uninstall-extra:

clean:
	for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \
		test x"$$i" = x"" && continue; \
		${DIR_ENTER}; \
		${MAKE} clean || exit $$?; \
		${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} ${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 \
		fi \
	done

distclean: clean
	for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \
		test x"$$i" = x"" && continue; \
		${DIR_ENTER}; \
		${MAKE} distclean || exit $$?; \
		${MAKE} -s distclean || exit $$?; \
		${DIR_LEAVE}; \
	done

	for i in "" ${DISTCLEAN} .deps *~; 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 \
		fi \
	done

print-hierarchy:
	for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \
		test x"$$i" = x"" && continue; \
		echo ${PRINT_HIERARCHY_PREFIX}$$i; \
		cd $$i || exit $$?; \
		${MAKE} PRINT_HIERARCHY_PREFIX=$$i/ print-hierarchy || exit $$?; \
		${MAKE} -s PRINT_HIERARCHY_PREFIX=$$i/ print-hierarchy || exit $$?; \
		cd .. || exit $$?; \
	done

print-var:
	printf '%s\n' '${${VAR}}'

DIR_ENTER = printf "@TERM_EL@@TERM_SETAF6@Entering directory @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF6@.@TERM_SGR0@\n" "$$i"; cd $$i || exit $$?

Modified configure.ac from [3226ef7f8e] to [59df486ed4].

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61







-













-







		OBJFW_OBJCFLAGS="$OBJFW_OBJCFLAGS $flags"
		LDFLAGS="$LDFLAGS $flags"

		enable_shared="no"
		enable_threads="no"
		enable_sockets="no"
		enable_files="no"
		ac_cv_snprintf_useful_ret="yes"
		;;
	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"
		ac_cv_snprintf_useful_ret="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
420
421
422
423
424
425
426

427
428

429
430
431
432
433
434
435
418
419
420
421
422
423
424
425
426

427
428
429
430
431
432
433
434







+

-
+







	#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"
	AC_MSG_RESULT(yes)
	gnu_source="yes"
], [
	AC_MSG_RESULT(no)
])

case "$host_os" in
	solaris*)
		CPPFLAGS="-D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS $CPPFLAGS"
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
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







-
-





+
+
+
+
+

-
+

-









-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-







			AC_MSG_RESULT(yes)
		], [
			AC_MSG_RESULT(no)
		])
		;;
esac

AC_CHECK_FUNCS(strerror_r)

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, but snprintf works.
			dnl asprintf is broken on the PSP
			have_asprintf="no"
			ac_cv_snprintf_useful_ret="yes"
			;;
		*)
			have_asprintf="yes"
			AC_DEFINE(HAVE_ASPRINTF, 1,
				[Whether we have asprintf()])
		;;
	esac
], [
	have_asprintf="no"

	AC_MSG_CHECKING(whether snprintf returns something useful)
	AC_CACHE_VAL(ac_cv_snprintf_useful_ret, [
		AC_TRY_RUN([
			#include <stdio.h>

			int
			main()
			{
				return (snprintf(NULL, 0, "asd") == 3 ? 0 : 1);
			}
		], [
			ac_cv_snprintf_useful_ret="yes"
		], [
			ac_cv_snprintf_useful_ret="no"
		], [
			ac_cv_snprintf_useful_ret="no"
		])
])
	])
	AC_MSG_RESULT($ac_cv_snprintf_useful_ret)
])
test x"$have_asprintf" != x"yes" -a x"$ac_cv_snprintf_useful_ret" != x"yes" && \
	AC_MSG_ERROR(No asprintf and no snprintf returning required space!)

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")
1226
1227
1228
1229
1230
1231
1232

















































1233
1234
1235
1236
1237
1238
1239
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







])

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*)
		;;
1272
1273
1274
1275
1276
1277
1278






1279




1280
1281
1282
1283
1284
1285
1286
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







+
+
+
+
+
+

+
+
+
+







		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

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








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+





-
+






-
+





-
+








-
+







-
+






-
+






+







		#   include <winsock2.h>
		#  endif
		# endif
		# include <windows.h>
		# include <ws2tcpip.h>
		#endif
	])

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

			#ifdef OF_HAVE_NETIPX_IPX_H
			# include <netipx/ipx.h>
			#endif

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

		#ifdef OF_HAVE_NETIPX_IPX_H
		# include <netipx/ipx.h>
		#endif

		#ifdef _WIN32
		# ifdef __MINGW32__
		#  include <_mingw.h>
		#  ifdef __MINGW64_VERSION_MAJOR
		#   include <winsock2.h>
		#  endif
		# endif
		# include <windows.h>
		# include <wsipx.h>
		#endif
	])
	AS_IF([test x"$ac_cv_member_struct_sockaddr_ipx_sipx_network" = x"yes" \
	    -o x"$ac_cv_member_struct_sockaddr_ipx_sa_netnum" = x"yes"], [
		AC_EGREP_CPP(egrep_cpp_yes, [
			#ifdef _WIN32
			typedef int BOOL;
			#endif

			#ifdef OF_HAVE_SYS_SOCKET_H
			# include <sys/socket.h>
			#endif

			#ifdef _WIN32
			# ifdef __MINGW32__
			#  include <_mingw.h>
			#  ifdef __MINGW64_VERSION_MAJOR
			#   include <winsock2.h>
			#  endif
			# endif
			# include <windows.h>
			# include <wsipx.h>
			#endif

			#ifdef AF_IPX
			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(OFKQUEUEKERNELEVENTOBSERVER_M,
		AC_SUBST(OF_KQUEUE_KERNEL_EVENT_OBSERVER_M,
			"OFKqueueKernelEventObserver.m")
		break
	])
	AC_CHECK_FUNCS(epoll_create1 epoll_create, [
		AC_DEFINE(HAVE_EPOLL, 1, [Whether we have epoll])
		AC_SUBST(OFEPOLLKERNELEVENTOBSERVER_M,
		AC_SUBST(OF_EPOLL_KERNEL_EVENT_OBSERVER_M,
			"OFEpollKernelEventObserver.m")
		break
	])

	AS_IF([test x"$with_wii" = x"yes"], [
		AC_DEFINE(HAVE_POLL, 1, [Whether we have poll()])
		AC_SUBST(OFPOLLKERNELEVENTOBSERVER_M,
		AC_SUBST(OF_POLL_KERNEL_EVENT_OBSERVER_M,
			"OFPollKernelEventObserver.m")
	], [
		AC_CHECK_HEADERS(poll.h)
		AC_CHECK_FUNC(poll, [
			AC_DEFINE(HAVE_POLL, 1, [Whether we have poll()])
			AC_SUBST(OFPOLLKERNELEVENTOBSERVER_M,
			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(OFSELECTKERNELEVENTOBSERVER_M,
			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(OFSELECTKERNELEVENTOBSERVER_M,
				AC_SUBST(OF_SELECT_KERNEL_EVENT_OBSERVER_M,
					"OFSelectKernelEventObserver.m")
			])
			;;
	esac

	AS_IF([test x"$enable_threads" != x"no"], [
		AC_SUBST(OFHTTPCLIENTTESTS_M, "OFHTTPClientTests.m")
		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>
1437
1438
1439
1440
1441
1442
1443
1444

1445
1446
1447
1448

1449
1450
1451
1452
1453
1454
1455
1548
1549
1550
1551
1552
1553
1554

1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567







-
+




+







			], [
				break
			])
		])
		;;
esac
AS_IF([test x"$have_processes" = x"yes"], [
	AC_SUBST(OFPROCESS_M, "OFProcess.m")
	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()])
])

AS_IF([test x"$objc_runtime" = x"Apple runtime"], [
	AC_CHECK_HEADER(Foundation/NSObject.h, [
1477
1478
1479
1480
1481
1482
1483
1484

1485
1486
1487
1488
1489
1490
1491
1589
1590
1591
1592
1593
1594
1595

1596
1597
1598
1599
1600
1601
1602
1603







-
+







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(OFBLOCKTESTS_M, "OFBlockTests.m")
	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"], [

Modified extra.mk.in from [0ce90d018a] to [45f41009ef].

50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67








68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

84

85
86
87
88
50
51
52
53
54
55
56

57

58
59
60






61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91







-

-


+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
















+

+




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@
OFBLOCKTESTS_M = @OFBLOCKTESTS_M@
OFDNS = @OFDNS@
OFEPOLLKERNELEVENTOBSERVER_M = @OFEPOLLKERNELEVENTOBSERVER_M@
OFHASH = @OFHASH@
OFHTTP = @OFHTTP@
OFSOCK = @OFSOCK@
OFHTTPCLIENTTESTS_M = @OFHTTPCLIENTTESTS_M@
OFKQUEUEKERNELEVENTOBSERVER_M = @OFKQUEUEKERNELEVENTOBSERVER_M@
OFPOLLKERNELEVENTOBSERVER_M = @OFPOLLKERNELEVENTOBSERVER_M@
OFPROCESS_M = @OFPROCESS_M@
OFSELECTKERNELEVENTOBSERVER_M = @OFSELECTKERNELEVENTOBSERVER_M@
OFSTDIOSTREAM_WIN32CONSOLE_M = @OFSTDIOSTREAM_WIN32CONSOLE_M@
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_FRAMEWORK_LIBS = @RUNTIME_FRAMEWORK_LIBS@
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@

Modified generators/Makefile from [3ca3993514] to [0e25880b06].

1
2
3
4
5
6
7
8
9
10

11
12
13

14
15
16
17
18
19
20
21
22
23
24


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


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

57
58
59

60
61
62
63
64
65
66
67
1
2
3
4
5
6
7
8
9

10
11
12

13
14
15
16
17
18
19
20
21
22


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


38
39
40
41
42
43
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 libobjfw.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	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 libobjfwrt.dll libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	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/libobjfw.dll; then \
		${LN_S} ../src/libobjfw.dll libobjfw.dll; \
	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/libobjfwrt.dll; then \
		${LN_S} ../src/runtime/libobjfwrt.dll libobjfwrt.dll; \
	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} libobjfw.dll; \
	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 libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.dll; \
	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}

Modified src/Makefile from [d8e8488c9d] to [e69692586b].

51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65







-
+







       OFNull.m				\
       OFNumber.m			\
       OFObject.m			\
       OFObject+KeyValueCoding.m	\
       OFObject+Serialization.m		\
       OFOptionsParser.m		\
       OFPair.m				\
       ${OFPROCESS_M}			\
       ${OF_PROCESS_M}			\
       OFRIPEMD160Hash.m		\
       OFRunLoop.m			\
       OFSandbox.m			\
       OFSecureData.m			\
       OFSeekableStream.m		\
       OFSet.m				\
       OFSHA1Hash.m			\
117
118
119
120
121
122
123



124

125
126
127
128

129
130
131
132
133
134

135
136
137
138



139
140
141
142
143
144
145
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151
152
153







+
+
+

+




+






+



-
+
+
+







       ${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
	       socket.m				\
	       ${USE_SRCS_IPX}			\
	       ${USE_SRCS_SCTP}
SRCS_THREADS = OFCondition.m		\
	       OFMutex.m		\
	       OFRecursiveMutex.m	\
	       condition.m		\
	       mutex.m			\
	       thread.m			\
	       tlskey.m
194
195
196
197
198
199
200

201
202
203
204
205





206
207
208
209
210
211
212
202
203
204
205
206
207
208
209
210




211
212
213
214
215
216
217
218
219
220
221
222







+

-
-
-
-
+
+
+
+
+







	OFUTF8String.m			\
	${LIBBASES_M}
SRCS_FILES += OFFileURLHandler.m	\
	      OFINIFileSettings.m
SRCS_SOCKETS += OFDNSResolverSettings.m			\
		OFHTTPURLHandler.m			\
		OFHostAddressResolver.m			\
		OFIPSocketAsyncConnector.m		\
		OFKernelEventObserver.m			\
		${OFEPOLLKERNELEVENTOBSERVER_M}		\
		${OFKQUEUEKERNELEVENTOBSERVER_M}	\
		${OFPOLLKERNELEVENTOBSERVER_M}		\
		${OFSELECTKERNELEVENTOBSERVER_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}
LIB_OBJS_EXTRA = ${RUNTIME_RUNTIME_LIB_A}	\
		 ${EXCEPTIONS_EXCEPTIONS_LIB_A}	\

Modified src/OFApplication.h from [d0ae1ccee1] to [ae508719d0].

16
17
18
19
20
21
22


23
24
25
26
27
28
29
30
























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







+
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 */

#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:
 *
 * @code
 * // In MyAppDelegate.h:
 * @interface MyAppDelegate: OFObject <OFApplicationDelegate>
 * @end
 *
 * // In MyAppDelegate.m:
 * OF_APPLICATION_DELEGATE(MyAppDelegate)
 *
 * @implementation MyAppDelegate
 * - (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]);			\
	}

Modified src/OFApplication.m from [7b2e9f847b] to [2e614ff495].

35
36
37
38
39
40
41

42
43
44
45
46
47
48
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49







+







#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"
#import "OFThread.h"

#import "OFInvalidArgumentException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFSandboxActivationFailedException.h"
108
109
110
111
112
113
114
115
116
117
118

119
120
121




122


123
124
125
126
127
128
129
109
110
111
112
113
114
115



116
117



118
119
120
121
122
123
124
125
126
127
128
129
130
131







-
-
-

+
-
-
-
+
+
+
+

+
+







	int wargc, si = 0;
#endif

	[[OFLocale alloc] init];

	app = [[OFApplication alloc] of_init];

	[app of_setArgumentCount: argc
	       andArgumentValues: argv];

#ifdef OF_WINDOWS
	if ([OFSystemInfo isWindowsNT]) {
	__wgetmainargs(&wargc, &wargv, &wenvp, _CRT_glob, &si);
	[app of_setArgumentCount: wargc
	   andWideArgumentValues: wargv];
		__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];

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







+
-
-
+
+

-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
-
+
+
+
+

-
-
+
+

-
-
+
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








	@try {
		_environment = [[OFMutableDictionary alloc] init];

		atexit(atexitHandler);

#if defined(OF_WINDOWS)
		if ([OFSystemInfo isWindowsNT]) {
		of_char16_t *env, *env0;
		env = env0 = GetEnvironmentStringsW();
			of_char16_t *env, *env0;
			env = env0 = GetEnvironmentStringsW();

		while (*env != 0) {
			void *pool = objc_autoreleasePoolPush();
			OFString *tmp, *key, *value;
			size_t length, pos;
			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;
				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;
			}
				/*
				 * 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;
			}
				pos = [tmp rangeOfString: @"="].location;
				if (pos == OF_NOT_FOUND) {
					fprintf(stderr,
					    "Warning: Invalid environment "
					    "variable: %s\n", tmp.UTF8String);
					continue;
				}

			key = [tmp substringWithRange: of_range(0, pos)];
			value = [tmp substringWithRange:
			    of_range(pos + 1, tmp.length - pos - 1)];
				key = [tmp substringWithRange:
				    of_range(0, pos)];
				value = [tmp substringWithRange:
				    of_range(pos + 1, tmp.length - pos - 1)];

			[_environment setObject: value
					 forKey: key];
				[_environment setObject: value
						 forKey: key];

			objc_autoreleasePoolPop(pool);
		}
				objc_autoreleasePoolPop(pool);
			}

		FreeEnvironmentStringsW(env0);
			FreeEnvironmentStringsW(env0);
		} else {
			char *env, *env0;
			env = env0 = GetEnvironmentStringsA();

			while (*env != 0) {
				void *pool = objc_autoreleasePoolPush();
				OFString *tmp, *key, *value;
				size_t length, pos;

				length = strlen(env);
				tmp = [OFString
				    stringWithCString: env
					     encoding: [OFLocale encoding]
					       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 substringWithRange:
				    of_range(0, pos)];
				value = [tmp substringWithRange:
				    of_range(pos + 1, tmp.length - 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;
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
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







-









-
+

-
-
+
+
-
-
+














-
-
-
-








	[super dealloc];
}

- (void)of_setArgumentCount: (int *)argc
	  andArgumentValues: (char ***)argv
{
#ifndef OF_WINDOWS
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray *arguments;
	of_string_encoding_t encoding;

	_argc = argc;
	_argv = argv;

	encoding = [OFLocale encoding];

# ifndef OF_NINTENDO_DS
#ifndef OF_NINTENDO_DS
	if (*argc > 0) {
# else
	if (__system_argv->argvMagic == ARGV_MAGIC &&
#else
	if (__system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc > 0) {
	    __system_argv->argc > 0) {
# endif
#endif
		_programName = [[OFString alloc] initWithCString: (*argv)[0]
							encoding: encoding];
		arguments = [[OFMutableArray alloc] init];
		_arguments = arguments;

		for (int i = 1; i < *argc; i++)
			[arguments addObject:
			    [OFString stringWithCString: (*argv)[i]
					       encoding: encoding]];

		[arguments makeImmutable];
	}

	objc_autoreleasePoolPop(pool);
#else
	_argc = argc;
	_argv = argv;
#endif
}

#ifdef OF_WINDOWS
- (void)of_setArgumentCount: (int)argc
      andWideArgumentValues: (wchar_t **)argv
{
	void *pool = objc_autoreleasePoolPush();

Modified src/OFArray.h from [fea61523da] to [82b5ea76b0].

384
385
386
387
388
389
390
391

392
393
394
395
396
397
398
384
385
386
387
388
389
390

391
392
393
394
395
396
397
398







-
+







			withObject: (nullable id)object;

/*!
 * @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 @ref OFComparing#compare:.
 *		   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
 */

Modified src/OFCharacterSet.m from [6367b81058] to [7a7a3d4551].

17
18
19
20
21
22
23








24
25
26
27
28
29
30

31

32
33
34


35
36

37
38
39
40
41
42
43
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

40



41
42


43
44
45
46
47
48
49
50







+
+
+
+
+
+
+
+







+
-
+
-
-
-
+
+
-
-
+








#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 {
	Class isa;
} placeholder;

static OFCharacterSet *whitespaceCharacterSet = nil;

static void
@interface OFPlaceholderCharacterSet: OFCharacterSet
initWhitespaceCharacterSet(void)
@end

@interface OFWhitespaceCharacterSet: OFCharacterSet
{
	whitespaceCharacterSet = [[OFWhitespaceCharacterSet alloc] init];
- (instancetype)of_init;
@end
}

@implementation OFPlaceholderCharacterSet
- (instancetype)init
{
	return (id)[[OFBitSetCharacterSet alloc] init];
}

98
99
100
101
102
103
104



105

106
107
108
109
110
111
112
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122







+
+
+
-
+







+ (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 [OFWhitespaceCharacterSet whitespaceCharacterSet];
	return whitespaceCharacterSet;
}

- (instancetype)init
{
	if ([self isMemberOfClass: [OFCharacterSet class]]) {
		@try {
			[self doesNotRecognizeSelector: _cmd];
135
136
137
138
139
140
141
142

143
144
145
146
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
145
146
147
148
149
150
151

152
153
154
155
156























157
158
159
160
161
162
163







-
+




-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







{
	OF_UNRECOGNIZED_SELECTOR
}

- (OFCharacterSet *)invertedSet
{
	return [[[OFInvertedCharacterSet alloc]
	    of_initWithCharacterSet: self] autorelease];
	    initWithCharacterSet: self] autorelease];
}
@end

@implementation OFWhitespaceCharacterSet
+ (void)initialize
{
	if (self != [OFWhitespaceCharacterSet class])
		return;

	whitespaceCharacterSet = [[OFWhitespaceCharacterSet alloc] of_init];
}

+ (OFCharacterSet *)whitespaceCharacterSet
{
	return whitespaceCharacterSet;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_init
{
	return [super init];
}

- (instancetype)autorelease
{
	return self;
}

- (instancetype)retain
{

Modified src/OFColor.h from [53dfa6424a] to [59ecd06578].

26
27
28
29
30
31
32



















33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
















































































































































48
49
50
51
52
53
54
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 */
@interface OFColor: OFObject
{
	float _red, _green, _blue, _alpha;
	OF_RESERVE_IVARS(4)
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic) OFColor *black;
@property (class, readonly, nonatomic) OFColor *silver;
@property (class, readonly, nonatomic) OFColor *grey;
@property (class, readonly, nonatomic) OFColor *white;
@property (class, readonly, nonatomic) OFColor *maroon;
@property (class, readonly, nonatomic) OFColor *red;
@property (class, readonly, nonatomic) OFColor *purple;
@property (class, readonly, nonatomic) OFColor *fuchsia;
@property (class, readonly, nonatomic) OFColor *green;
@property (class, readonly, nonatomic) OFColor *lime;
@property (class, readonly, nonatomic) OFColor *olive;
@property (class, readonly, nonatomic) OFColor *yellow;
@property (class, readonly, nonatomic) OFColor *navy;
@property (class, readonly, nonatomic) OFColor *blue;
@property (class, readonly, nonatomic) OFColor *teal;
@property (class, readonly, nonatomic) OFColor *aqua;
#endif

/*!
 * @brief Creates a new color with the specified red, green, blue and alpha
 *	  value.
 *
 * @param red The red value of the color, between 0.0 and 1.0
 * @param green The green value of the color, between 0.0 and 1.0
 * @param blue The blue value of the color, between 0.0 and 1.0
 * @param alpha The alpha value of the color, between 0.0 and 1.0
 * @return A new color with the specified red, green, blue and alpha value
 */
+ (instancetype)colorWithRed: (float)red
		       green: (float)green
			blue: (float)blue
		       alpha: (float)alpha;

/*!
 * @brief Returns the HTML color `black`.
 *
 * The RGBA value is (0, 0, 0, 1).
 *
 * @return The HTML color `black`
 */
+ (OFColor *)black;

/*!
 * @brief Returns the HTML color `silver`.
 *
 * The RGBA value is (0.75, 0.75, 0.75, 1).
 *
 * @return The HTML color `silver`
 */
+ (OFColor *)silver;

/*!
 * @brief Returns the HTML color `grey`.
 *
 * The RGBA value is (0.5, 0.5, 0.5, 1).
 *
 * @return The HTML color `grey`
 */
+ (OFColor *)grey;

/*!
 * @brief Returns the HTML color `white`.
 *
 * The RGBA value is (1, 1, 1, 1).
 *
 * @return The HTML color `white`
 */
+ (OFColor *)white;

/*!
 * @brief Returns the HTML color `maroon`.
 *
 * The RGBA value is (0.5, 0, 0, 1).
 *
 * @return The HTML color `maroon`
 */
+ (OFColor *)maroon;

/*!
 * @brief Returns the HTML color `red`.
 *
 * The RGBA value is (1, 0, 0, 1).
 *
 * @return The HTML color `red`
 */
+ (OFColor *)red;

/*!
 * @brief Returns the HTML color `purple`.
 *
 * The RGBA value is (0.5, 0, 0.5, 1).
 *
 * @return The HTML color `purple`
 */
+ (OFColor *)purple;

/*!
 * @brief Returns the HTML color `fuchsia`.
 *
 * The RGBA value is (1, 0, 1, 1).
 *
 * @return The HTML color `fuchsia`
 */
+ (OFColor *)fuchsia;

/*!
 * @brief Returns the HTML color `green`.
 *
 * The RGBA value is (0, 0.5, 0, 1).
 *
 * @return The HTML color `green`
 */
+ (OFColor *)green;

/*!
 * @brief Returns the HTML color `lime`.
 *
 * The RGBA value is (0, 1, 0, 1).
 *
 * @return The HTML color `lime`
 */
+ (OFColor *)lime;

/*!
 * @brief Returns the HTML color `olive`.
 *
 * The RGBA value is (0.5, 0.5, 0, 1).
 *
 * @return The HTML color `olive`
 */
+ (OFColor *)olive;

/*!
 * @brief Returns the HTML color `yellow`.
 *
 * The RGBA value is (1, 1, 0, 1).
 *
 * @return The HTML color `yellow`
 */
+ (OFColor *)yellow;

/*!
 * @brief Returns the HTML color `navy`.
 *
 * The RGBA value is (0, 0, 0.5, 1).
 *
 * @return The HTML color `navy`
 */
+ (OFColor *)navy;

/*!
 * @brief Returns the HTML color `blue`.
 *
 * The RGBA value is (0, 0, 1, 1).
 *
 * @return The HTML color `blue`
 */
+ (OFColor *)blue;

/*!
 * @brief Returns the HTML color `teal`.
 *
 * The RGBA value is (0, 0.5, 0.5, 1).
 *
 * @return The HTML color `teal`
 */
+ (OFColor *)teal;

/*!
 * @brief Returns the HTML color `aqua`.
 *
 * The RGBA value is (0, 1, 1, 1).
 *
 * @return The HTML color `aqua`
 */
+ (OFColor *)aqua;

/*!
 * @brief Initializes an already allocated color with the specified red, green,
 *	  blue and alpha value.
 *
 * @param red The red value of the color, between 0.0 and 1.0
 * @param green The green value of the color, between 0.0 and 1.0
 * @param blue The blue value of the color, between 0.0 and 1.0

Modified src/OFColor.m from [c54ddadacf] to [eeffafbeab].

14
15
16
17
18
19
20
21


22
23
24





































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








+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 * 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.00, 0.00, 0.00)
PREDEFINED_COLOR(silver,  0.75, 0.75, 0.75)
PREDEFINED_COLOR(grey,    0.50, 0.50, 0.50)
PREDEFINED_COLOR(white,   1.00, 1.00, 1.00)
PREDEFINED_COLOR(maroon,  0.50, 0.00, 0.00)
PREDEFINED_COLOR(red,     1.00, 0.00, 0.00)
PREDEFINED_COLOR(purple,  0.50, 0.00, 0.50)
PREDEFINED_COLOR(fuchsia, 1.00, 0.00, 1.00)
PREDEFINED_COLOR(green,   0.00, 0.50, 0.00)
PREDEFINED_COLOR(lime,    0.00, 1.00, 0.00)
PREDEFINED_COLOR(olive,   0.50, 0.50, 0.00)
PREDEFINED_COLOR(yellow,  1.00, 1.00, 0.00)
PREDEFINED_COLOR(navy,    0.00, 0.00, 0.50)
PREDEFINED_COLOR(blue,    0.00, 0.00, 1.00)
PREDEFINED_COLOR(teal,    0.00, 0.50, 0.50)
PREDEFINED_COLOR(aqua,    0.00, 1.00, 1.00)

+ (instancetype)colorWithRed: (float)red
		       green: (float)green
			blue: (float)blue
		       alpha: (float)alpha
{
	return [[[self alloc] initWithRed: red
				    green: green
82
83
84
85
86
87
88
89
90

91
92
93
94
95
96

97
98

99
100

101
102

103
104

105
106

107
108

109
110

111
112
113
114
115
116
117
121
122
123
124
125
126
127


128


129
130
131

132
133

134
135

136
137

138
139

140
141

142
143

144
145

146
147
148
149
150
151
152
153







-
-
+
-
-



-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+








	return true;
}

- (uint32_t)hash
{
	uint32_t hash;
	union {
		float f;
	float tmp;
		unsigned char b[sizeof(float)];
	} f;

	OF_HASH_INIT(hash);

	f.f = OF_BSWAP_FLOAT_IF_LE(_red);
	tmp = OF_BSWAP_FLOAT_IF_LE(_red);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OF_HASH_ADD(hash, f.b[i]);
		OF_HASH_ADD(hash, ((char *)&tmp)[i]);

	f.f = OF_BSWAP_FLOAT_IF_LE(_green);
	tmp = OF_BSWAP_FLOAT_IF_LE(_green);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OF_HASH_ADD(hash, f.b[i]);
		OF_HASH_ADD(hash, ((char *)&tmp)[i]);

	f.f = OF_BSWAP_FLOAT_IF_LE(_blue);
	tmp = OF_BSWAP_FLOAT_IF_LE(_blue);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OF_HASH_ADD(hash, f.b[i]);
		OF_HASH_ADD(hash, ((char *)&tmp)[i]);

	f.f = OF_BSWAP_FLOAT_IF_LE(_alpha);
	tmp = OF_BSWAP_FLOAT_IF_LE(_alpha);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OF_HASH_ADD(hash, f.b[i]);
		OF_HASH_ADD(hash, ((char *)&tmp)[i]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (void)getRed: (float *)red

Modified src/OFConstantString.h from [a28a2410f5] to [b16809eb56].

11
12
13
14
15
16
17



18

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

33
34
35
36
37
38
39
40
41
42
43
44

45
46


11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53







+
+
+
-
+














+












+


+
+
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either 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_OF_CONSTANT_STRING_H
#define OBJFW_OF_CONSTANT_STRING_H

#import "OFString.h"
#include "OFString.h"

OF_ASSUME_NONNULL_BEGIN

#if !defined(OF_CONSTANT_STRING_M) && \
    defined(OF_APPLE_RUNTIME) && !defined(__OBJC2__)
# ifdef __cplusplus
extern "C" {
# endif
extern void *_OFConstantStringClassReference;
# ifdef __cplusplus
}
# endif
#endif

#ifdef __OBJC__
/*!
 * @class OFConstantString OFConstantString.h ObjFW/OFConstantString.h
 *
 * @brief A class for storing constant strings using the `@""` literal.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFConstantString: OFString
{
	char *_cString;
	unsigned int _cStringLength;
}
@end
#endif

OF_ASSUME_NONNULL_END

#endif

Modified src/OFConstantString.m from [743f51b49a] to [1a9a1e4686].

670
671
672
673
674
675
676









677
678
679
680
681
682
683
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692







+
+
+
+
+
+
+
+
+







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

Modified src/OFDNSResolver.h from [8e72b091f6] to [074bf54ace].

30
31
32
33
34
35
36

37
38
39
40
41
42
43
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44







+







@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.
 */
127
128
129
130
131
132
133


134
135
136
137
138
139
140
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143







+
+







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

/*!
 * @brief A dictionary of static hosts.
 *
 * This dictionary is checked before actually looking up a host.
 */

Modified src/OFDNSResolver.m from [8f5030468c] to [5d30f91158].

27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76
77
78
79
80
81
82




83
84
85
86
87
88
89
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89







+

















+











-
-
-
-
-
-
-
+














+
+
+
+







#import "OFData.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFHostAddressResolver.h"
#import "OFNumber.h"
#import "OFPair.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFTimer.h"
#import "OFUDPSocket.h"
#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

/*
 * TODO:
 *
 *  - Fallback to TCP
 */

@interface OFDNSResolver () <OFUDPSocketDelegate>
@interface OFDNSResolver () <OFUDPSocketDelegate, OFTCPSocketDelegate>
- (void)of_contextTimedOut: (OFDNSResolverContext *)context;
@end

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

- (instancetype)initWithQuery: (OFDNSQuery *)query
			   ID: (OFNumber *)ID
		     settings: (OFDNSResolverSettings *)settings
		     delegate: (id <OFDNSResolverQueryDelegate>)delegate;
309
310
311
312
313
314
315











316
317








318
319
320
321
322

323
324
325
326
327
328
329
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







+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+




-
+







		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)
				@throw [OFInvalidServerReplyException
				    exception];

			[textStrings addObject:
		OFData *textData = [OFData dataWithItems: &buffer[i]
						   count: dataLength];
			    [OFData dataWithItems: buffer + i
					    count: stringLength]];

			i += stringLength;
			dataLength -= stringLength;
		}

		[textStrings makeImmutable];

		return [[[OFTXTDNSResourceRecord alloc]
		    initWithName: name
			DNSClass: DNSClass
			textData: textData
		     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;

535
536
537
538
539
540
541


542
543
544
545
546
547
548
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567







+
+







- (void)dealloc
{
	[_query release];
	[_ID release];
	[_settings release];
	[_delegate release];
	[_queryData release];
	[_TCPSocket release];
	[_TCPQueryData release];
	[_cancelTimer release];

	[super dealloc];
}
@end

@implementation OFDNSResolver
566
567
568
569
570
571
572

573
574
575
576
577
578
579
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599







+







- (instancetype)init
{
	self = [super init];

	@try {
		_settings = [[OFDNSResolverSettings alloc] init];
		_queries = [[OFMutableDictionary alloc] init];
		_TCPQueries = [[OFMutableDictionary alloc] init];

		[_settings reload];
	} @catch (id e) {
		[self release];
		@throw e;
	}

588
589
590
591
592
593
594

595
596
597
598
599
600
601
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622







+







	[_IPv4Socket cancelAsyncRequests];
	[_IPv4Socket release];
#ifdef OF_HAVE_IPV6
	[_IPv6Socket cancelAsyncRequests];
	[_IPv6Socket release];
#endif
	[_queries release];
	[_TCPQueries release];

	[super dealloc];
}

- (OFDictionary *)staticHosts
{
	return _settings->_staticHosts;
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
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







+
+
+

















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












-
+














-
+







}

- (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.blocking = false;
			_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.blocking = false;
			_IPv4Socket.canBlock = false;
			_IPv4Socket.delegate = self;
		}

		sock = _IPv4Socket;
		break;
	default:
		@throw [OFInvalidArgumentException exception];
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
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







-
-
-










+
+
+
+
+
+
+
+
+
+









-
+
-







		@throw [OFOutOfRangeException exception];

	context = [[[OFDNSResolverContext alloc]
	    initWithQuery: query
		       ID: ID
		 settings: _settings
		 delegate: delegate] autorelease];
	[_queries setObject: context
		     forKey: ID];

	[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) {
	if (++context->_attempt < context->_settings->_maxAttempts) {
		context->_attempt++;
		context->_nameServersIndex = 0;
		[self of_sendQueryForContext: context
				 runLoopMode: runLoopMode];
		return;
	}

	context = [[context retain] autorelease];
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
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







-
-
-
-
+
+
+
-

-



+



-
-
-










+
+
+
-
+







	    @selector(resolver:didPerformQuery:response:exception:)])
		[context->_delegate resolver: self
			     didPerformQuery: context->_query
				    response: nil
				   exception: exception];
}

-	  (bool)socket: (OFUDPSocket *)sock
  didReceiveIntoBuffer: (void *)buffer_
		length: (size_t)length
		sender: (const of_socket_address_t *)sender
- (bool)of_handleResponseBuffer: (unsigned char *)buffer
			 length: (size_t)length
			 sender: (const of_socket_address_t *)sender
	     exception: (id)exception
{
	unsigned char *buffer = buffer_;
	OFDictionary *answerRecords = nil, *authorityRecords = nil;
	OFDictionary *additionalRecords = nil;
	OFDNSResponse *response = nil;
	id exception = nil;
	OFNumber *ID;
	OFDNSResolverContext *context;

	if (exception != nil)
		return true;

	if (length < 2)
		/* We can't get the ID to get the context. Ignore packet. */
		return true;

	ID = [OFNumber numberWithUInt16: (buffer[0] << 8) | buffer[1]];
	context = [[[_queries objectForKey: ID] retain] autorelease];

	if (context == nil)
		return true;

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

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

899
900
901
902
903
904
905
906
907












908
909
910
911
912
913
914
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







-
-
+
+
+
+
+
+
+
+
+
+
+
+







			@throw [OFInvalidServerReplyException exception];

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

		/* TC */
		if (buffer[2] & 0x02)
			@throw [OFTruncatedDataException exception];
		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;
991
992
993
994
995
996
997














































































































































998
999
1000
1001
1002
1003
1004
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	if ([context->_delegate respondsToSelector:
	    @selector(resolver:didPerformQuery:response:exception:)])
		[context->_delegate resolver: self
			     didPerformQuery: context->_query
				    response: response
				   exception: exception];

	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];
		[context->_TCPSocket release];
		context->_TCPSocket = nil;
		context->_responseLength = 0;
		return;
	}

	if (context->_TCPQueryData == nil) {
		size_t queryDataCount = context->_queryData.count;
		uint16_t tmp;

		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

Modified src/OFDNSResolverSettings.m from [67fa5dd9c3] to [995314850c].

38
39
40
41
42
43
44


45

46
47
48
49
50
51
52
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55







+
+

+







#ifdef OF_WINDOWS
# define interface struct
# include <iphlpapi.h>
# undef interface
#endif

#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"
481
482
483
484
485
486
487
488

489
490
491
492






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

491




492
493
494
495
496
497
498
499
500
501
502
503
504







-
+
-
-
-
-
+
+
+
+
+
+








	pool = objc_autoreleasePoolPush();

	[self setDefaults];

#if defined(OF_WINDOWS)
# ifdef OF_HAVE_FILES
	path = [[OFWindowsRegistryKey localMachineKey]
	OFWindowsRegistryKey *key = [[OFWindowsRegistryKey localMachineKey]
	    stringForValue: @"DataBasePath"
		subkeyPath: @"SYSTEM\\CurrentControlSet\\Services\\"
			    @"Tcpip\\Parameters"];
	path = [path stringByAppendingPathComponent: @"hosts"];
		   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)

Modified src/OFDNSResourceRecord.h from [eacf6b96e8] to [279eea4da6].

20
21
22
23
24
25
26

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







+








#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/*! @file */

@class OFArray OF_GENERIC(ObjectType);
@class OFData;

/*!
 * @brief The DNS class.
 */
typedef enum {
	/*! IN */
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
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







-
+





-
+












-
+





-
+







 *	  OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class representing a TXT DNS resource record.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFTXTDNSResourceRecord: OFDNSResourceRecord
{
	OFData *_textData;
	OFArray OF_GENERIC(OFData *) *_textStrings;
}

/*!
 * @brief The text of the resource record.
 */
@property (readonly, nonatomic) OFData *textData;
@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 textData The data 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
		    textData: (OFData *)textData
		 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);

Modified src/OFDNSResourceRecord.m from [95ccb6d631] to [054747ac72].

14
15
16
17
18
19
20

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







+







 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

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

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

OFString *
of_dns_class_to_string(of_dns_class_t DNSClass)
76
77
78
79
80
81
82
83

84
85
86
87
88
89
90
77
78
79
80
81
82
83

84
85
86
87
88
89
90
91







-
+








	string = string.uppercaseString;

	if ([string isEqual: @"IN"])
		DNSClass = OF_DNS_CLASS_IN;
	else {
		@try {
			DNSClass = (of_dns_class_t)[string decimalValue];
			DNSClass = (of_dns_class_t)string.decimalValue;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidArgumentException exception];
		}
	}

	objc_autoreleasePoolPop(pool);

116
117
118
119
120
121
122


123
124
125
126

127
128
129
130
131
132
133
117
118
119
120
121
122
123
124
125
126
127
128

129
130
131
132
133
134
135
136







+
+



-
+







		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 decimalValue];
			    (of_dns_record_type_t)string.decimalValue;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidArgumentException exception];
		}
	}

	objc_autoreleasePoolPop(pool);

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







-
+











-
+








-
+










-
+







	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, _priority, _weight, _target, _port, _TTL];
}
@end

@implementation OFTXTDNSResourceRecord
@synthesize textData = _textData;
@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
		    textData: (OFData *)textData
		 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 {
		_textData = [textData copy];
		_textStrings = [textStrings copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_textData release];
	[_textStrings release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFTXTDNSResourceRecord *record;
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
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







-
-
+
+
















-
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+



-
+


-
+

-
+
+
+
+
+
+
+


	if (record->_DNSClass != _DNSClass)
		return false;

	if (record->_recordType != _recordType)
		return false;

	if (record->_textData != _textData &&
	    ![record->_textData isEqual: _textData])
	if (record->_textStrings != _textStrings &&
	    ![record->_textStrings isEqual: _textStrings])
		return false;

	return true;
}

- (uint32_t)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, _textData.hash);
	OF_HASH_ADD_HASH(hash, _textStrings.hash);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableString *text = [OFMutableString string];
	bool first = true;
	OFString *ret;

	for (OFData *string in _textStrings) {
		const unsigned char *stringItems = string.items;
		size_t stringCount = string.count;

		if (first) {
			first = false;
			[text appendString: @"\""];
		} else
			[text appendString: @" \""];

		for (size_t i = 0; i < stringCount; i++) {
			if (stringItems[i] == '\\')
				[text appendString: @"\\\\"];
			else if (stringItems[i] == '"')
				[text appendString: @"\\\""];
			else if (stringItems[i] < 0x20)
				[text appendFormat: @"\\x%02X", stringItems[i]];
			else if (stringItems[i] < 0x7F)
				[text appendFormat: @"%c", stringItems[i]];
			else
				[text appendFormat: @"\\x%02X", stringItems[i]];
		}

		[text appendString: @"\""];
	}

	return [OFString stringWithFormat:
	ret = [OFString stringWithFormat:
	    @"<%@:\n"
	    @"\tName = %@\n"
	    @"\tClass = %@\n"
	    @"\tText Data = %@\n"
	    @"\tText strings = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    self.className, _name, of_dns_class_to_string(_DNSClass), _textData,
	    self.className, _name, of_dns_class_to_string(_DNSClass), text,
	    _TTL];
}

	[ret retain];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

Modified src/OFDNSResponse.h from [156cc97081] to [12af35aca6].

68
69
70
71
72
73
74

75
76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97







+














+







 * 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

Modified src/OFData+MessagePackValue.m from [95748cc907] to [caaaf51219].

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
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 (length < 9)
			@throw [OFTruncatedDataException exception];

		*object = [OFNumber numberWithInt64: readUInt64(buffer + 1)];
		return 9;
	/* Floating point */
	case 0xCA:; /* float 32 */
		union {
			unsigned char u8[4];
			float f;
		float f;
		} f;

		if (length < 5)
			@throw [OFTruncatedDataException exception];

		memcpy(&f.u8, buffer + 1, 4);
		memcpy(&f, buffer + 1, 4);

		*object = [OFNumber numberWithFloat: OF_BSWAP_FLOAT_IF_LE(f.f)];
		*object = [OFNumber numberWithFloat: OF_BSWAP_FLOAT_IF_LE(f)];
		return 5;
	case 0xCB:; /* float 64 */
		union {
			unsigned char u8[8];
			double d;
		double d;
		} d;

		if (length < 9)
			@throw [OFTruncatedDataException exception];

		memcpy(&d.u8, buffer + 1, 8);
		memcpy(&d, buffer + 1, 8);

		*object = [OFNumber numberWithDouble:
		*object = [OFNumber numberWithDouble: OF_BSWAP_DOUBLE_IF_LE(d)];
		    OF_BSWAP_DOUBLE_IF_LE(d.d)];
		return 9;
	/* nil */
	case 0xC0:
		*object = [OFNull null];
		return 1;
	/* false */
	case 0xC2:

Added src/OFDatagramSocket.h version [cb9e65f15c].

























































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 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
 *
 * @brief A delegate for OFDatagramSocket.
 */
@protocol OFDatagramSocketDelegate <OFObject>
@optional
/*!
 * @brief This method is called when a packet has been received.
 *
 * @param socket The datagram socket which received a packet
 * @param buffer The buffer the packet has been written to
 * @param length The length of the packet
 * @param sender The address of the sender of the packet
 * @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(4)
}

/*!
 * @brief Whether the socket can block.
 *
 * By default, a socket can block.
 */
@property (nonatomic) bool canBlock;

/*!
 * @brief Whether the socket can send to broadcast addresses.
 */
@property (nonatomic) bool canSendToBroadcastAddresses;

/*!
 * @brief The delegate for asynchronous operations on the socket.
 *
 * @note The delegate is retained for as long as asynchronous operations are
 *	 still ongoing.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFDatagramSocketDelegate> delegate;

/*!
 * @brief Returns a new, autoreleased OFDatagramSocket.
 *
 * @return A new, autoreleased OFDatagramSocket
 */
+ (instancetype)socket;

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

/*!
 * @brief Closes the socket so that it can neither receive nor send any more
 *	  datagrams.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END

Added src/OFDatagramSocket.m version [3479c8733b].















































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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
{
	return [self retain];
}

- (bool)canBlock
{
	return _canBlock;
}

- (void)setCanBlock: (bool)canBlock
{
#if defined(HAVE_FCNTL)
	int flags = fcntl(_socket, F_GETFL, 0);

	if (flags == -1)
		@throw [OFSetOptionFailedException exceptionWithObject: self
								 errNo: errno];

	if (canBlock)
		flags &= ~O_NONBLOCK;
	else
		flags |= O_NONBLOCK;

	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

Modified src/OFDate.m from [ab9874f31d] to [1c4647d564].

46
47
48
49
50
51
52




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







+
+
+
+







# define trunc(x) ((int64_t)(x))
#endif

#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)						\
	time_t seconds = (time_t)_seconds;				\
	struct tm tm;							\
									\
	if (seconds != trunc(_seconds))					\
182
183
184
185
186
187
188
189
190
191
192




193
194
195












196

197
198
199
200
201
202
203
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







-
-


+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

+







	/* Time zone */
	seconds += -(double)*tz * 60;

	return seconds;
}

@implementation OFDate
#if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \
    defined(OF_HAVE_THREADS)
+ (void)initialize
{
#ifdef OF_WINDOWS
	HMODULE module;
#endif

	if (self == [OFDate class])
		mutex = [[OFMutex alloc] init];
}
	if (self != [OFDate class])
		return;

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

+ (instancetype)date
{
	return [[[self alloc] init] autorelease];
}

+ (instancetype)dateWithTimeIntervalSince1970: (of_time_interval_t)seconds
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
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







-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

















-
-
-
-





-
-
-
+
+







		tm.tm_isdst = -1;

		if (of_strptime(UTF8String, format.UTF8String,
		    &tm, &tz) != UTF8String + string.UTF8StringLength)
			@throw [OFInvalidFormatException exception];

		if (tz == INT16_MAX) {
#ifndef OF_WINDOWS
			if ((_seconds = mktime(&tm)) == -1)
				@throw [OFInvalidFormatException exception];
#else
			if ((_seconds = _mktime64(&tm)) == -1)
				@throw [OFInvalidFormatException exception];
#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);
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		union {
			double d;
			uint64_t u;
		} d;

		if (![element.name isEqual: self.className] ||
		    ![element.namespace isEqual: OF_SERIALIZATION_NS])
			@throw [OFInvalidArgumentException exception];

		d.u = (uint64_t)element.hexadecimalValue;
		d.u = OF_BSWAP64_IF_LE(d.u);
		_seconds = OF_BSWAP_DOUBLE_IF_LE(d.d);
		_seconds = OF_BSWAP_DOUBLE_IF_LE(OF_INT_TO_DOUBLE_RAW(
		    OF_BSWAP64_IF_LE(element.hexadecimalValue)));

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	}

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
396
397
398
399
400
401
402


403


404

405
406

407
408
409

410
411
412
413
414
415
416
417







-
-
+
-
-

-
+

-
+


-
+








	return true;
}

- (uint32_t)hash
{
	uint32_t hash;
	union {
		double d;
	double tmp;
		uint8_t b[sizeof(double)];
	} d;

	d.d = OF_BSWAP_DOUBLE_IF_BE(_seconds);
	OF_HASH_INIT(hash);

	OF_HASH_INIT(hash);
	tmp = OF_BSWAP_DOUBLE_IF_BE(_seconds);

	for (size_t i = 0; i < sizeof(double); i++)
		OF_HASH_ADD(hash, d.b[i]);
		OF_HASH_ADD(hash, ((char *)&tmp)[i]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
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
441
442
443
444
445
446
447




448
449
450
451



452
453
454
455
456
457
458
459
460
461







-
-
-
-




-
-
-
+
+
+







	return [self dateStringWithFormat: @"%Y-%m-%dT%H:%M:%SZ"];
}

- (OFXMLElement *)XMLElementBySerializing
{
	void *pool = objc_autoreleasePoolPush();
	OFXMLElement *element;
	union {
		double d;
		uint64_t u;
	} d;

	element = [OFXMLElement elementWithName: self.className
				      namespace: OF_SERIALIZATION_NS];

	d.d = OF_BSWAP_DOUBLE_IF_LE(_seconds);
	element.stringValue =
	    [OFString stringWithFormat: @"%016" PRIx64, OF_BSWAP64_IF_LE(d.u)];
	element.stringValue = [OFString stringWithFormat: @"%016" PRIx64,
	    OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE(
	    _seconds)))];

	[element retain];

	objc_autoreleasePoolPop(pool);

	return [element autorelease];
}

Modified src/OFFile.m from [1f08d5d8ea] to [14d95d3e71].

28
29
30
31
32
33
34

35
36
37
38
39
40
41
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42







+







#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif

#import "OFFile.h"
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"
#import "OFURL.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotOpenException.h"
#import "OFOpenItemFailedException.h"
#import "OFOutOfMemoryException.h"
216
217
218
219
220
221
222
223
224
225
226
227
228










229

230
231


232


233
234
235
236
237
238
239
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







-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
-
-
+
+

+
+








#ifndef OF_AMIGAOS
		if ((flags = parseMode(mode.UTF8String)) == -1)
			@throw [OFInvalidArgumentException exception];

		flags |= O_BINARY | O_CLOEXEC;

# if defined(OF_WINDOWS)
		if ((handle = _wopen(path.UTF16String, flags,
		    _S_IREAD | _S_IWRITE)) == -1)
# elif defined(HAVE_OPEN64)
		if ((handle = open64([path cStringWithEncoding:
		    [OFLocale encoding]], flags, 0666)) == -1)
# ifdef OF_WINDOWS
		if ([OFSystemInfo isWindowsNT])
			handle = _wopen(path.UTF16String, flags,
			    _S_IREAD | _S_IWRITE);
		else
# endif
# ifdef HAVE_OPEN64
			handle = open64(
			    [path cStringWithEncoding: [OFLocale encoding]],
			    flags, 0666);
# else
			handle = open(
		if ((handle = open([path cStringWithEncoding:
		    [OFLocale encoding]], flags, 0666)) == -1)
			    [path cStringWithEncoding: [OFLocale encoding]],
			    flags, 0666);
# endif

		if (handle == -1)
			@throw [OFOpenItemFailedException
			    exceptionWithPath: path
					 mode: mode
					errNo: errno];
#else
		if ((handle = malloc(sizeof(*handle))) == NULL)
			@throw [OFOutOfMemoryException

Modified src/OFFileManager.h from [3b9bb8eaec] to [832085a76a].

138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153

154
155
156
157
158
159
160
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160







-
+







-
+







 *
 * 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 @ref OFString.
 * @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 @ref OFString.
 * @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;

/*!
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200







-
+







 *
 * 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 @ref OFString.
 * @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;

Modified src/OFFileManager.m from [6f997251ad] to [c5306f3b5d].

160
161
162
163
164
165
166


167

168
169
170
171
172














173
174
175
176
177
178
179
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







+
+
-
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







}

#ifdef OF_HAVE_FILES
- (OFString *)currentDirectoryPath
{
# if defined(OF_WINDOWS)
	OFString *ret;

	if ([OFSystemInfo isWindowsNT]) {
	wchar_t *buffer = _wgetcwd(NULL, 0);
		wchar_t *buffer = _wgetcwd(NULL, 0);

	@try {
		ret = [OFString stringWithUTF16String: buffer];
	} @finally {
		free(buffer);
		@try {
			ret = [OFString stringWithUTF16String: buffer];
		} @finally {
			free(buffer);
		}
	} else {
		char *buffer = _getcwd(NULL, 0);

		@try {
			ret = [OFString stringWithCString: buffer
						 encoding: [OFLocale encoding]];
		} @finally {
			free(buffer);
		}
	}

	return ret;
# elif defined(OF_AMIGAOS)
	char buffer[512];

	if (!NameFromLock(((struct Process *)FindTask(NULL))->pr_CurrentDir,
478
479
480
481
482
483
484
485
486
487
488
489
490

491
492
493
494
495
496
497
490
491
492
493
494
495
496






497
498
499
500
501
502
503
504







-
-
-
-
-
-
+







}

- (void)changeCurrentDirectoryPath: (OFString *)path
{
	if (path == nil)
		@throw [OFInvalidArgumentException exception];

# if defined(OF_WINDOWS)
	if (_wchdir(path.UTF16String) != 0)
		@throw [OFChangeCurrentDirectoryPathFailedException
		    exceptionWithPath: path
				errNo: errno];
# elif defined(OF_AMIGAOS)
# ifdef OF_AMIGAOS
	BPTR lock, oldLock;

	if ((lock = Lock([path cStringWithEncoding: [OFLocale encoding]],
	    SHARED_LOCK)) == 0) {
		int errNo;

		switch (IoErr()) {
517
518
519
520
521
522
523








524



525
526
527
528
529
530
531
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







+
+
+
+
+
+
+
+
-
+
+
+







	if (!dirChanged)
		originalDirLock = oldLock;
	else
		UnLock(oldLock);

	dirChanged = true;
# else
	int status;

#  ifdef OF_WINDOWS
	if ([OFSystemInfo isWindowsNT])
		status = _wchdir(path.UTF16String);
	else
#  endif
		status = chdir(
	if (chdir([path cStringWithEncoding: [OFLocale encoding]]) != 0)
		    [path cStringWithEncoding: [OFLocale encoding]]);

	if (status != 0)
		@throw [OFChangeCurrentDirectoryPathFailedException
		    exceptionWithPath: path
				errNo: errno];
# endif
}

- (void)changeCurrentDirectoryURL: (OFURL *)URL

Modified src/OFFileURLHandler.m from [f538dd0518] to [b6ef19d4e0].

38
39
40
41
42
43
44

45
46
47
48
49
50
51
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52







+







#import "OFFileURLHandler.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFFile.h"
#import "OFFileManager.h"
#import "OFLocale.h"
#import "OFNumber.h"
#import "OFSystemInfo.h"
#import "OFURL.h"

#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif

#import "OFCreateDirectoryFailedException.h"
105
106
107
108
109
110
111


112
113
114
115
116
117
118
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121







+
+







#endif
#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS)
static OFMutex *readdirMutex;
#endif

#ifdef OF_WINDOWS
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 |
144
145
146
147
148
149
150

151

152
153








154
155
156
157
158
159
160
161
162
163




164
165
166
167
168
169
170
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







+

+
-
-
+
+
+
+
+
+
+
+










+
+
+
+







#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])
	if (!GetFileAttributesExW(path.UTF16String, GetFileExInfoStandard,
	    &data)) {
		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;
494
495
496
497
498
499
500
501

502
503
504





505
506
507
508
509
510
511
509
510
511
512
513
514
515

516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531







-
+



+
+
+
+
+







	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("kernel32.dll")) != NULL)
	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];
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)of_setPOSIXPermissions: (OFNumber *)permissions
		   ofItemAtURL: (OFURL *)URL
		    attributes: (of_file_attributes_t)attributes
{
#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS
	uint16_t mode = permissions.uInt16Value & 0777;
	OFString *path = URL.fileSystemRepresentation;
	int status;

# ifndef OF_WINDOWS
	if (chmod([path cStringWithEncoding: [OFLocale encoding]], mode) != 0)
# else
# ifdef OF_WINDOWS
	if ([OFSystemInfo isWindowsNT])
		status = _wchmod(path.UTF16String, mode);
	else
	if (_wchmod(path.UTF16String, mode) != 0)
# 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
769
770
771
772
773
774
775



776






777
778
779
780
781
782
783
794
795
796
797
798
799
800
801
802
803

804
805
806
807
808
809
810
811
812
813
814
815
816







+
+
+
-
+
+
+
+
+
+








	if (![URL.scheme isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	path = URL.fileSystemRepresentation;

#if defined(OF_WINDOWS)
	int status;

	if ([OFSystemInfo isWindowsNT])
	if (_wmkdir(path.UTF16String) != 0)
		status = _wmkdir(path.UTF16String);
	else
		status = _mkdir(
		    [path cStringWithEncoding: [OFLocale encoding]]);

	if (status != 0)
		@throw [OFCreateDirectoryFailedException
		    exceptionWithURL: URL
			       errNo: errno];
#elif defined(OF_AMIGAOS)
	BPTR lock;

	if ((lock = CreateDir(
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
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







-



+
+
+
-
-
-
+
+
+

-
-
+
+

-
-
-
-
+
+
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	if (![URL.scheme isEqual: _scheme])
		@throw [OFInvalidArgumentException exception];

	path = URL.fileSystemRepresentation;

#if defined(OF_WINDOWS)
	HANDLE handle;
	WIN32_FIND_DATAW fd;

	path = [path stringByAppendingString: @"\\*"];

	if ([OFSystemInfo isWindowsNT]) {
		WIN32_FIND_DATAW fd;

	if ((handle = FindFirstFileW(path.UTF16String,
	    &fd)) == INVALID_HANDLE_VALUE) {
		int errNo = 0;
		if ((handle = FindFirstFileW(path.UTF16String,
		    &fd)) == INVALID_HANDLE_VALUE) {
			int errNo = 0;

		if (GetLastError() == ERROR_FILE_NOT_FOUND)
			errNo = ENOENT;
			if (GetLastError() == ERROR_FILE_NOT_FOUND)
				errNo = ENOENT;

		@throw [OFOpenItemFailedException exceptionWithURL: URL
							      mode: nil
							     errNo: errNo];
	}
			@throw [OFOpenItemFailedException
			    exceptionWithURL: URL
					mode: nil
				       errNo: errNo];
		}

	@try {
		do {
			OFString *file;
		@try {
			do {
				OFString *file;

			if (!wcscmp(fd.cFileName, L".") ||
			    !wcscmp(fd.cFileName, L".."))
				continue;
				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));
				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);
			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) {
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
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







+
+
-
-
-
+
+
+
+
-

+
+
+
+




+
+
-
-
-
-
+
+
+
+
-

+
+
+
+







			[self removeItemAtURL: [OFURL fileURLWithPath:
			    [path stringByAppendingPathComponent: item]]];

			objc_autoreleasePoolPop(pool2);
		}

#ifndef OF_AMIGAOS
		int status;

# ifndef OF_WINDOWS
		if (rmdir([path cStringWithEncoding: [OFLocale encoding]]) != 0)
# else
# ifdef OF_WINDOWS
		if ([OFSystemInfo isWindowsNT])
			status = _wrmdir(path.UTF16String);
		else
		if (_wrmdir(path.UTF16String) != 0)
# endif
			status = rmdir(
			    [path cStringWithEncoding: [OFLocale encoding]]);

		if (status != 0)
			@throw [OFRemoveItemFailedException
				exceptionWithURL: URL
					   errNo: errno];
	} else {
		int status;

# ifndef OF_WINDOWS
		if (unlink([path cStringWithEncoding:
		    [OFLocale encoding]]) != 0)
# else
# ifdef OF_WINDOWS
		if ([OFSystemInfo isWindowsNT])
			status = _wunlink(path.UTF16String);
		else
		if (_wunlink(path.UTF16String) != 0)
# endif
			status = unlink(
			    [path cStringWithEncoding: [OFLocale encoding]]);

		if (status != 0)
			@throw [OFRemoveItemFailedException
			    exceptionWithURL: URL
				       errNo: errno];
#endif
	}

#ifdef OF_AMIGAOS
1162
1163
1164
1165
1166
1167
1168




1169

1170
1171
1172
1173
1174
1175
1176
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265

1266
1267
1268
1269
1270
1271
1272
1273







+
+
+
+
-
+







	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 (!CreateHardLinkW(destinationPath.UTF16String,
	if (!func_CreateHardLinkW(destinationPath.UTF16String,
	    sourcePath.UTF16String, NULL))
		@throw [OFLinkFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: 0];
# endif

1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245

1246
1247
1248
1249
1250
1251
1252
1328
1329
1330
1331
1332
1333
1334








1335
1336
1337
1338
1339
1340
1341
1342







-
-
-
-
-
-
-
-
+







		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: EEXIST];

	pool = objc_autoreleasePoolPush();

#if defined(OF_WINDOWS)
	if (_wrename(source.fileSystemRepresentation.UTF16String,
	    destination.fileSystemRepresentation.UTF16String) != 0)
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: errno];
#elif defined(OF_AMIGAOS)
#ifdef OF_AMIGAOS
	of_string_encoding_t encoding = [OFLocale encoding];

	if (!Rename([source.fileSystemRepresentation
	    cStringWithEncoding: encoding],
	    [destination.fileSystemRepresentation
	    cStringWithEncoding: encoding])) {
		int errNo;
1275
1276
1277
1278
1279
1280
1281








1282

1283
1284
1285
1286
1287









1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
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







+
+
+
+
+
+
+
+
-
+

-
-
-
-
+
+
+
+
+
+
+
+
+












		@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];
		of_string_encoding_t encoding = [OFLocale encoding];

	if (rename([source.fileSystemRepresentation
	    cStringWithEncoding: encoding],
	    [destination.fileSystemRepresentation
	    cStringWithEncoding: encoding]) != 0)
		status = rename([source.fileSystemRepresentation
		    cStringWithEncoding: encoding],
		    [destination.fileSystemRepresentation
		    cStringWithEncoding: encoding]);
# ifdef OF_WINDOWS
	}
# endif

	if (status != 0)
		@throw [OFMoveItemFailedException
		    exceptionWithSourceURL: source
			    destinationURL: destination
				     errNo: errno];
#endif

	objc_autoreleasePoolPop(pool);

	return true;
}
@end

Modified src/OFHTTPClient.h from [1c7acee4b8] to [a28aee5e52].

143
144
145
146
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
143
144
145
146
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







-
+













-
+

-
+







OF_SUBCLASSING_RESTRICTED
@interface OFHTTPClient: OFObject
{
#ifdef OF_HTTPCLIENT_M
@public
#endif
	OFObject <OFHTTPClientDelegate> *_Nullable _delegate;
	bool _insecureRedirectsAllowed, _inProgress;
	bool _allowsInsecureRedirects, _inProgress;
	OFTCPSocket *_Nullable _socket;
	OFURL *_Nullable _lastURL;
	bool _lastWasHEAD;
	OFHTTPResponse *_Nullable _lastResponse;
}

/*!
 * @brief The delegate of the HTTP request.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    OFObject <OFHTTPClientDelegate> *delegate;

/*!
 * @brief Whether redirects from HTTPS to HTTP will be allowed.
 * @brief Whether the HTTP client allows redirects from HTTPS to HTTP.
 */
@property (nonatomic) bool insecureRedirectsAllowed;
@property (nonatomic) bool allowsInsecureRedirects;

/*!
 * @brief Creates a new OFHTTPClient.
 *
 * @return A new, autoreleased OFHTTPClient
 */
+ (instancetype)client;

Modified src/OFHTTPClient.m from [36ad6829ee] to [7d20d55912].

72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87
88
89
90
91



92
93
94
95
96
97
98
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90


91
92
93
94
95
96
97
98
99
100







+











-
-
+
+
+







- (void)closeAndReconnect;
@end

@interface OFHTTPClientRequestBodyStream: OFStream <OFReadyForWritingObserving>
{
	OFHTTPClientRequestHandler *_handler;
	OFTCPSocket *_socket;
	bool _chunked;
	uintmax_t _toWrite;
	bool _atEndOfStream;
}

- (instancetype)initWithHandler: (OFHTTPClientRequestHandler *)handler
			 socket: (OFTCPSocket *)sock;
@end

@interface OFHTTPClientResponse: OFHTTPResponse <OFReadyForReadingObserving>
{
	OFTCPSocket *_socket;
	bool _hasContentLength, _chunked, _keepAlive, _atEndOfStream;
	uintmax_t _toRead;
	bool _hasContentLength, _chunked, _keepAlive;
	bool _atEndOfStream, _setAtEndOfStream;
	intmax_t _toRead;
}

@property (nonatomic, setter=of_setKeepAlive:) bool of_keepAlive;

- (instancetype)initWithSocket: (OFTCPSocket *)sock;
@end

114
115
116
117
118
119
120

121
122
123
124
125
126
127
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130







+







	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 = @"/";
181
182
183
184
185
186
187
188





189
190
191
192
193
194
195
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198
199
200
201
202







-
+
+
+
+
+








	if (request.protocolVersion.major == 1 &&
	    request.protocolVersion.minor == 0 &&
	    [headers objectForKey: @"Connection"] == nil)
		[headers setObject: @"keep-alive"
			    forKey: @"Connection"];

	if ([headers objectForKey: @"Content-Length"] != nil &&
	hasContentLength = ([headers objectForKey: @"Content-Length"] != nil);
	chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	if ((hasContentLength || chunked) &&
	    [headers objectForKey: @"Content-Type"] == nil)
		[headers setObject: @"application/x-www-form-"
				    @"urlencoded; charset=UTF-8"
			    forKey: @"Content-Type"];

	keyEnumerator = [headers keyEnumerator];
	objectEnumerator = [headers objectEnumerator];
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
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







-
+
-




















-


-
-
+
+
-
-

-
+



+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+





-
+







	response.protocolVersionString = _version;
	response.statusCode = _status;
	response.headers = _serverHeaders;

	connectionHeader = [_serverHeaders objectForKey: @"Connection"];
	if ([_version isEqual: @"1.1"]) {
		if (connectionHeader != nil)
			keepAlive = ([connectionHeader caseInsensitiveCompare:
			keepAlive = [connectionHeader isEqual: @"close"];
			    @"close"] != OF_ORDERED_SAME);
		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];
	}

	/* FIXME: Case-insensitive check of redirect's scheme */
	if (_redirects > 0 && (_status == 301 || _status == 302 ||
	    _status == 303 || _status == 307) &&
	    (location = [_serverHeaders objectForKey: @"Location"]) != nil &&
	    (_client->_insecureRedirectsAllowed ||
	    (location = [_serverHeaders objectForKey: @"Location"]) != nil) {
		bool follow = true;
	    [URL.scheme isEqual: @"http"] ||
	    [location hasPrefix: @"https://"])) {
		OFURL *newURL;
		bool follow;
		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 ([_client->_delegate respondsToSelector: @selector(client:
		    shouldFollowRedirect:statusCode:request:response:)])
		if (follow && [_client->_delegate respondsToSelector: @selector(
		    client:shouldFollowRedirect:statusCode:request:response:)])
			follow = [_client->_delegate client: _client
				       shouldFollowRedirect: newURL
						 statusCode: _status
						    request: _request
						   response: response];
		else
		else if (follow)
			follow = defaultShouldFollow(_request.method, _status);

		if (follow) {
			OFDictionary OF_GENERIC(OFString *, OFString *)
			    *headers = _request.headers;
			OFHTTPRequest *newRequest =
			    [[_request copy] autorelease];
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
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







+
+
+















+
+
+
+
-
+








- (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) {
		if ([exception isKindOfClass: [OFWriteFailedException class]] &&
		    ([exception errNo] == ECONNRESET ||
		    [exception errNo] == EPIPE)) {
			/* In case a keep-alive connection timed out */
			[self closeAndReconnect];
			return nil;
		}

		[self raiseException: exception];
		return nil;
	}

	_firstLine = true;

	headers = _request.headers;
	chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	if ([_request.headers objectForKey: @"Content-Length"] != nil) {
	if (chunked || [headers objectForKey: @"Content-Length"] != nil) {
		stream.delegate = nil;

		OFStream *requestBody = [[[OFHTTPClientRequestBodyStream alloc]
		    initWithHandler: self
			     socket: (OFTCPSocket *)stream] autorelease];

		if ([_client->_delegate respondsToSelector:
666
667
668
669
670
671
672
673


674
675
676
677
678
679
680
690
691
692
693
694
695
696

697
698
699
700
701
702
703
704
705







-
+
+







		OFURL *URL = _request.URL;
		OFTCPSocket *sock;
		uint16_t port;
		OFNumber *URLPort;

		[_client close];

		if ([URL.scheme isEqual: @"https"]) {
		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 {
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
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







-
+






+
+
+

-
-
+
+
+
+

-
-
-
+
+
+

-
-
+
+
-







			 socket: (OFTCPSocket *)sock
{
	self = [super init];

	@try {
		OFDictionary OF_GENERIC(OFString *, OFString *) *headers;
		intmax_t contentLength;
		OFString *contentLengthString;
		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"];
		if (contentLengthString == nil)
			@throw [OFInvalidArgumentException exception];
		if (contentLengthString != nil) {
			if (_chunked || contentLengthString.length == 0)
				@throw [OFInvalidArgumentException
				    exception];

		contentLength = contentLengthString.decimalValue;
		if (contentLength < 0)
			@throw [OFOutOfRangeException exception];
			contentLength = contentLengthString.decimalValue;
			if (contentLength < 0)
				@throw [OFOutOfRangeException exception];

		_toWrite = contentLength;

			_toWrite = contentLength;
		} else if (!_chunked)
		if ([headers objectForKey: @"Transfer-Encoding"] != nil)
			@throw [OFInvalidArgumentException exception];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
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
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







+
+
+
+
+
+
+
+







+
+
-
+




+
+




+
-
+

-
-
+
+
+







{
	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];
	if (length > _toWrite)
	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;
		_toWrite -= ret;

	if (_toWrite == 0)
		_atEndOfStream = true;
		if (_toWrite == 0)
			_atEndOfStream = true;
	}

	if (requestedLength > length)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: requestedLength
			   bytesWritten: ret
				  errNo: 0];
787
788
789
790
791
792
793


794

795
796
797
798
799
800
801
830
831
832
833
834
835
836
837
838

839
840
841
842
843
844
845
846







+
+
-
+







}

- (void)close
{
	if (_socket == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_chunked)
		[_socket writeString: @"0\r\n\r\n"];
	if (_toWrite > 0)
	else if (_toWrite > 0)
		@throw [OFTruncatedDataException exception];

	_socket.delegate = _handler;
	[_socket asyncReadLine];

	[_socket release];
	_socket = nil;
836
837
838
839
840
841
842



843
844
845
846

847
848

849
850
851
852
853
854
855
856
857
858
859
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







+
+
+



-
+

-
+


-
-







	super.headers = headers;

	_chunked = [[headers objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	contentLength = [headers objectForKey: @"Content-Length"];
	if (contentLength != nil) {
		if (_chunked || contentLength.length == 0)
			@throw [OFInvalidServerReplyException exception];

		_hasContentLength = true;

		@try {
			intmax_t toRead = contentLength.decimalValue;
			_toRead = contentLength.decimalValue;

			if (toRead < 0)
			if (_toRead < 0)
				@throw [OFInvalidServerReplyException
				    exception];

			_toRead = toRead;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidServerReplyException exception];
		}
	}
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
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
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







-
+

















-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+
-
-








-
+



+
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-

-
-





-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-







	if (_socket.atEndOfStream)
		@throw [OFTruncatedDataException exception];

	/* Content-Length */
	if (!_chunked) {
		size_t ret;

		if (length > _toRead)
		if (length > (uintmax_t)_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 > 0) {
		if (length > _toRead)
	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 > (uintmax_t)_toRead)
			length = (size_t)_toRead;

		length = [_socket readIntoBuffer: buffer
					  length: length];

		_toRead -= length;

		if (_toRead == 0)
			if ([_socket readLine].length > 0)
			_toRead = -2;
				@throw [OFInvalidServerReplyException
				    exception];

		return length;
	} else {
		void *pool = objc_autoreleasePoolPush();
		OFString *line;
		of_range_t range;

		@try {
			line = [_socket readLine];
			line = [_socket tryReadLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidServerReplyException exception];
		}

		if (line == nil)
			return 0;

		range = [line rangeOfString: @";"];
		if (range.location != OF_NOT_FOUND)
			line = [line substringWithRange:
			    of_range(0, range.location)];

		if (line.length < 1) {
			/*
			 * We have read the empty string because the socket is
			 * at end of stream.
			 */
			if (_socket.atEndOfStream &&
			    range.location == OF_NOT_FOUND)
				@throw [OFTruncatedDataException exception];
			else
				@throw [OFInvalidServerReplyException
				    exception];
		}

		@try {
			intmax_t toRead = line.hexadecimalValue;
			if ((_toRead = line.hexadecimalValue) < 0)

			if (toRead < 0)
				@throw [OFOutOfRangeException exception];

			_toRead = toRead;
		} @catch (OFInvalidFormatException *e) {
			@throw [OFInvalidServerReplyException exception];
		}

		if (_toRead == 0) {
			_atEndOfStream = true;

			_setAtEndOfStream = true;
			_toRead = -2;
			if (_keepAlive) {
				@try {
					line = [_socket readLine];
				} @catch (OFInvalidEncodingException *e) {
					@throw [OFInvalidServerReplyException
					    exception];
				}

				if (line.length > 0)
					@throw [OFInvalidServerReplyException
					    exception];
			}
		}

		objc_autoreleasePoolPop(pool);

		return 0;
	}
}
1116
1117
1118
1119
1120
1121
1122
1123

1124
1125
1126
1127
1128
1129
1130
1196
1197
1198
1199
1200
1201
1202

1203
1204
1205
1206
1207
1208
1209
1210







-
+







	else
		return defaultShouldFollow(request.method, statusCode);
}
@end

@implementation OFHTTPClient
@synthesize delegate = _delegate;
@synthesize insecureRedirectsAllowed = _insecureRedirectsAllowed;
@synthesize allowsInsecureRedirects = _allowsInsecureRedirects;

+ (instancetype)client
{
	return [[[self alloc] init] autorelease];
}

- (void)dealloc
1166
1167
1168
1169
1170
1171
1172

1173

1174
1175
1176
1177
1178
1179
1180
1246
1247
1248
1249
1250
1251
1252
1253

1254
1255
1256
1257
1258
1259
1260
1261







+
-
+







- (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 &&
	if (![scheme isEqual: @"http"] && ![scheme isEqual: @"https"])
	    [scheme caseInsensitiveCompare: @"https"] != OF_ORDERED_SAME)
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	if (_inProgress)
		/* TODO: Find a better exception */
		@throw [OFAlreadyConnectedException exception];

	_inProgress = true;

Modified src/OFHTTPCookieManager.m from [b42d33b466] to [0f44cd343b].

61
62
63
64
65
66
67
68


69
70
71
72
73
74
75
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76







-
+
+







	void *pool = objc_autoreleasePoolPush();
	OFString *cookieDomain, *URLHost;
	size_t i;

	if (![cookie.path hasPrefix: @"/"])
		cookie.path = @"/";

	if (cookie.secure && ![URL.scheme isEqual: @"https"]) {
	if (cookie.secure &&
	    [URL.scheme caseInsensitiveCompare: @"https"] != OF_ORDERED_SAME) {
		objc_autoreleasePoolPop(pool);
		return;
	}

	cookieDomain = cookie.domain.lowercaseString;
	cookie.domain = cookieDomain;

121
122
123
124
125
126
127
128


129
130
131
132
133
134
135
122
123
124
125
126
127
128

129
130
131
132
133
134
135
136
137







-
+
+







		OFString *cookieDomain, *URLHost, *cookiePath, *URLPath;
		bool match;

		expires = cookie.expires;
		if (expires != nil && expires.timeIntervalSinceNow <= 0)
			continue;

		if (cookie.secure && ![URL.scheme isEqual: @"https"])
		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: @"."]) {

Modified src/OFHTTPRequest.h from [4385937a7a] to [e39f7af9d0].

53
54
55
56
57
58
59
60

61
62
63
64


65

66
67
68
69
70
71
72
53
54
55
56
57
58
59

60
61
62
63
64
65
66

67
68
69
70
71
72
73
74







-
+




+
+
-
+








/*!
 * @struct of_http_request_protocol_version_t \
 *	   OFHTTPRequest.h ObjFW/OFHTTPRequest.h
 *
 * @brief The HTTP version of the HTTP request.
 */
typedef struct OF_BOXABLE {
struct OF_BOXABLE of_http_request_protocol_version_t {
	/*! The major of the HTTP version */
	uint8_t major;
	/*! The minor of the HTTP version */
	uint8_t minor;
};
typedef struct of_http_request_protocol_version_t
} 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>
152
153
154
155
156
157
158
159

160
161
162
163
164
154
155
156
157
158
159
160

161
162
163
164
165
166







-
+





/*!
 * @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(
    const char *string);
    OFString *string);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFHTTPRequest.m from [db732e6dff] to [2d1b69a80f].

22
23
24
25
26
27
28

29
30
31
32
33
34
35
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36







+







#import "OFHTTPRequest.h"
#import "OFString.h"
#import "OFURL.h"
#import "OFDictionary.h"
#import "OFData.h"
#import "OFArray.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFOutOfRangeException.h"
#import "OFUnsupportedVersionException.h"

const char *
of_http_request_method_to_string(of_http_request_method_t method)
{
52
53
54
55
56
57
58
59

60
61

62
63

64
65

66
67

68
69

70
71

72
73

74
75

76
77
78

79
80
81
82
83
84
85
53
54
55
56
57
58
59

60
61

62
63

64
65

66
67

68
69

70
71

72
73

74
75

76
77
78

79
80
81
82
83
84
85
86







-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+


-
+







		return "CONNECT";
	}

	return NULL;
}

of_http_request_method_t
of_http_request_method_from_string(const char *string)
of_http_request_method_from_string(OFString *string)
{
	if (strcmp(string, "OPTIONS") == 0)
	if ([string isEqual: @"OPTIONS"])
		return OF_HTTP_REQUEST_METHOD_OPTIONS;
	if (strcmp(string, "GET") == 0)
	if ([string isEqual: @"GET"])
		return OF_HTTP_REQUEST_METHOD_GET;
	if (strcmp(string, "HEAD") == 0)
	if ([string isEqual: @"HEAD"])
		return OF_HTTP_REQUEST_METHOD_HEAD;
	if (strcmp(string, "POST") == 0)
	if ([string isEqual: @"POST"])
		return OF_HTTP_REQUEST_METHOD_POST;
	if (strcmp(string, "PUT") == 0)
	if ([string isEqual: @"PUT"])
		return OF_HTTP_REQUEST_METHOD_PUT;
	if (strcmp(string, "DELETE") == 0)
	if ([string isEqual: @"DELETE"])
		return OF_HTTP_REQUEST_METHOD_DELETE;
	if (strcmp(string, "TRACE") == 0)
	if ([string isEqual: @"TRACE"])
		return OF_HTTP_REQUEST_METHOD_TRACE;
	if (strcmp(string, "CONNECT") == 0)
	if ([string isEqual: @"CONNECT"])
		return OF_HTTP_REQUEST_METHOD_CONNECT;

	@throw [OFInvalidFormatException exception];
	@throw [OFInvalidArgumentException exception];
}

@implementation OFHTTPRequest
@synthesize URL = _URL, method = _method, headers = _headers;

+ (instancetype)request
{

Modified src/OFHTTPResponse.h from [f70ef56750] to [453ae309af].

66
67
68
69
70
71
72
73








74
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82








+
+
+
+
+
+
+
+

 * @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
extern OFString *_Nonnull of_http_status_code_to_string(short code);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFHTTPResponse.m from [2d50b8bc91] to [1abecd6ce8].

19
20
21
22
23
24
25
26

27
28
29
30

























































































31
32
33
34
35
36
37
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126







-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








#import "OFHTTPResponse.h"
#import "OFString.h"
#import "OFDictionary.h"
#import "OFArray.h"
#import "OFData.h"

#import "OFInvalidEncodingException.h"
#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:
		return @"OK";
	case 201:
		return @"Created";
	case 202:
		return @"Accepted";
	case 203:
		return @"Non-Authoritative Information";
	case 204:
		return @"No Content";
	case 205:
		return @"Reset Content";
	case 206:
		return @"Partial Content";
	case 300:
		return @"Multiple Choices";
	case 301:
		return @"Moved Permanently";
	case 302:
		return @"Found";
	case 303:
		return @"See Other";
	case 304:
		return @"Not Modified";
	case 305:
		return @"Use Proxy";
	case 307:
		return @"Temporary Redirect";
	case 400:
		return @"Bad Request";
	case 401:
		return @"Unauthorized";
	case 402:
		return @"Payment Required";
	case 403:
		return @"Forbidden";
	case 404:
		return @"Not Found";
	case 405:
		return @"Method Not Allowed";
	case 406:
		return @"Not Acceptable";
	case 407:
		return @"Proxy Authentication Required";
	case 408:
		return @"Request Timeout";
	case 409:
		return @"Conflict";
	case 410:
		return @"Gone";
	case 411:
		return @"Length Required";
	case 412:
		return @"Precondition Failed";
	case 413:
		return @"Request Entity Too Large";
	case 414:
		return @"Request-URI Too Long";
	case 415:
		return @"Unsupported Media Type";
	case 416:
		return @"Requested Range Not Satisfiable";
	case 417:
		return @"Expectation Failed";
	case 500:
		return @"Internal Server Error";
	case 501:
		return @"Not Implemented";
	case 502:
		return @"Bad Gateway";
	case 503:
		return @"Service Unavailable";
	case 504:
		return @"Gateway Timeout";
	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 {
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228







-
+








		if ([name isEqual: @"charset"])
			charset = value;
	}

	@try {
		ret = of_string_parse_encoding(charset);
	} @catch (OFInvalidEncodingException *e) {
	} @catch (OFInvalidArgumentException *e) {
		ret = OF_STRING_ENCODING_AUTODETECT;
	}

	return ret;
}

@implementation OFHTTPResponse

Modified src/OFHTTPServer.m from [e03c4b06ca] to [9e1f23cc19].

32
33
34
35
36
37
38

39
40
41
42

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

61
62
63
64
65
66

67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
103




104
105
106


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67

68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

93
94
95
96
97
98
99
100
101
102



103
104
105
106
107
108

109
110
111
112
113
114
115
116
117
118
119

























































































120
121
122
123
124
125
126







+




+

















-
+





-
+







-
+
















-
+









-
-
-
+
+
+
+


-
+
+









-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







#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

@interface OFHTTPServerResponse: OFHTTPResponse <OFReadyForWritingObserving>
{
	OFTCPSocket *_socket;
	OFStreamSocket *_socket;
	OFHTTPServer *_server;
	OFHTTPRequest *_request;
	bool _chunked, _headersSent;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
- (instancetype)initWithSocket: (OFStreamSocket *)sock
			server: (OFHTTPServer *)server
		       request: (OFHTTPRequest *)request;
@end

@interface OFHTTPServerConnection: OFObject <OFTCPSocketDelegate>
{
@public
	OFTCPSocket *_socket;
	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;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
- (instancetype)initWithSocket: (OFStreamSocket *)sock
			server: (OFHTTPServer *)server;
- (bool)parseProlog: (OFString *)line;
- (bool)parseHeaders: (OFString *)line;
- (bool)sendErrorAndClose: (short)statusCode;
- (void)createResponse;
@end

@interface OFHTTPServerRequestBodyStream: OFStream <OFReadyForReadingObserving>
{
	OFTCPSocket *_socket;
	uintmax_t _toRead;
	bool _atEndOfStream;
	OFStreamSocket *_socket;
	bool _chunked;
	intmax_t _toRead;
	bool _atEndOfStream, _setAtEndOfStream;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
- (instancetype)initWithSocket: (OFStreamSocket *)sock
		       chunked: (bool)chunked
		 contentLength: (uintmax_t)contentLength;
@end

#ifdef OF_HAVE_THREADS
@interface OFHTTPServerThread: OFThread
- (void)stop;
@end
#endif

static const char *
statusCodeToString(short code)
{
	switch (code) {
	case 100:
		return "Continue";
	case 101:
		return "Switching Protocols";
	case 200:
		return "OK";
	case 201:
		return "Created";
	case 202:
		return "Accepted";
	case 203:
		return "Non-Authoritative Information";
	case 204:
		return "No Content";
	case 205:
		return "Reset Content";
	case 206:
		return "Partial Content";
	case 300:
		return "Multiple Choices";
	case 301:
		return "Moved Permanently";
	case 302:
		return "Found";
	case 303:
		return "See Other";
	case 304:
		return "Not Modified";
	case 305:
		return "Use Proxy";
	case 307:
		return "Temporary Redirect";
	case 400:
		return "Bad Request";
	case 401:
		return "Unauthorized";
	case 402:
		return "Payment Required";
	case 403:
		return "Forbidden";
	case 404:
		return "Not Found";
	case 405:
		return "Method Not Allowed";
	case 406:
		return "Not Acceptable";
	case 407:
		return "Proxy Authentication Required";
	case 408:
		return "Request Timeout";
	case 409:
		return "Conflict";
	case 410:
		return "Gone";
	case 411:
		return "Length Required";
	case 412:
		return "Precondition Failed";
	case 413:
		return "Request Entity Too Large";
	case 414:
		return "Request-URI Too Long";
	case 415:
		return "Unsupported Media Type";
	case 416:
		return "Requested Range Not Satisfiable";
	case 417:
		return "Expectation Failed";
	case 500:
		return "Internal Server Error";
	case 501:
		return "Not Implemented";
	case 502:
		return "Bad Gateway";
	case 503:
		return "Service Unavailable";
	case 504:
		return "Gateway Timeout";
	case 505:
		return "HTTP Version Not Supported";
	default:
		return NULL;
	}
}

static OF_INLINE OFString *
normalizedKey(OFString *key)
{
	char *cString = of_strdup(key.UTF8String);
	unsigned char *tmp = (unsigned char *)cString;
	bool firstLetter = true;

229
230
231
232
233
234
235
236

237
238
239
240
241
242
243
144
145
146
147
148
149
150

151
152
153
154
155
156
157
158







-
+







	}

	return [OFString stringWithUTF8StringNoCopy: cString
				       freeWhenDone: true];
}

@implementation OFHTTPServerResponse
- (instancetype)initWithSocket: (OFTCPSocket *)sock
- (instancetype)initWithSocket: (OFStreamSocket *)sock
			server: (OFHTTPServer *)server
		       request: (OFHTTPRequest *)request
{
	self = [super init];

	_statusCode = 500;
	_socket = [sock retain];
261
262
263
264
265
266
267
268

269
270

271
272
273
274
275
276
277
176
177
178
179
180
181
182

183
184

185
186
187
188
189
190
191
192







-
+

-
+







- (void)of_sendHeaders
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers;
	OFEnumerator *keyEnumerator, *valueEnumerator;
	OFString *key, *value;

	[_socket writeFormat: @"HTTP/%@ %d %s\r\n",
	[_socket writeFormat: @"HTTP/%@ %d %@\r\n",
			      self.protocolVersionString, _statusCode,
			      statusCodeToString(_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"];

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







-
+




-
+
-














-
+
-







		[self of_sendHeaders];

	if (!_chunked)
		return [_socket writeBuffer: buffer
				     length: length];

	pool = objc_autoreleasePoolPush();
	[_socket writeString: [OFString stringWithFormat: @"%zx\r\n", length]];
	[_socket writeString: [OFString stringWithFormat: @"%zX\r\n", length]];
	objc_autoreleasePoolPop(pool);

	[_socket writeBuffer: buffer
		      length: length];
	[_socket writeBuffer: "\r\n"
	[_socket writeString: @"\r\n"];
		      length: 2];

	return length;
}

- (void)close
{
	if (_socket == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	@try {
		if (!_headersSent)
			[self of_sendHeaders];

		if (_chunked)
			[_socket writeBuffer: "0\r\n\r\n"
			[_socket writeString: @"0\r\n\r\n"];
				      length: 5];
	} @catch (OFWriteFailedException *e) {
		id <OFHTTPServerDelegate> delegate = _server.delegate;

		if ([delegate respondsToSelector: @selector(server:
		  didReceiveExceptionForResponse:request:exception:)])
			[delegate		    server: _server
			    didReceiveExceptionForResponse: self
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293







-
+







		return -1;

	return _socket.fileDescriptorForWriting;
}
@end

@implementation OFHTTPServerConnection
- (instancetype)initWithSocket: (OFTCPSocket *)sock
- (instancetype)initWithSocket: (OFStreamSocket *)sock
			server: (OFHTTPServer *)server
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_server = [server retain];
457
458
459
460
461
462
463
464
465


466
467
468
469
470
471
472
370
371
372
373
374
375
376


377
378
379
380
381
382
383
384
385







-
-
+
+








	pos = [line rangeOfString: @" "].location;
	if (pos == OF_NOT_FOUND)
		return [self sendErrorAndClose: 400];

	method = [line substringWithRange: of_range(0, pos)];
	@try {
		_method = of_http_request_method_from_string(method.UTF8String);
	} @catch (OFInvalidFormatException *e) {
		_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:
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
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







+
+
-
+
+
+

-
+
-
-
+
+




-






-
+
+
+




+








- (bool)parseHeaders: (OFString *)line
{
	OFString *key, *value, *old;
	size_t pos;

	if (line.length == 0) {
		bool chunked = [[_headers objectForKey: @"Transfer-Encoding"]
		    isEqual: @"chunked"];
		OFString *contentLengthString;
		OFString *contentLengthString =
		    [_headers objectForKey: @"Content-Length"];
		intmax_t contentLength = 0;

		if ((contentLengthString =
		if (contentLengthString != nil) {
		    [_headers objectForKey: @"Content-Length"]) != nil) {
			intmax_t contentLength;
			if (chunked || contentLengthString.length == 0)
				return [self sendErrorAndClose: 400];

			@try {
				contentLength =
				    contentLengthString.decimalValue;

			} @catch (OFInvalidFormatException *e) {
				return [self sendErrorAndClose: 400];
			}

			if (contentLength < 0)
				return [self sendErrorAndClose: 400];

		}

		if (chunked || contentLengthString != nil) {
			[_requestBody release];
			_requestBody = nil;
			_requestBody = [[OFHTTPServerRequestBodyStream alloc]
			    initWithSocket: _socket
				   chunked: chunked
			     contentLength: contentLength];

			[_timer invalidate];
			[_timer release];
			_timer = nil;
		}

580
581
582
583
584
585
586
587

588
589
590
591


592
593
594
595
596
597
598
499
500
501
502
503
504
505

506
507
508
509

510
511
512
513
514
515
516
517
518







-
+



-
+
+







}

- (bool)sendErrorAndClose: (short)statusCode
{
	OFString *date = [[OFDate date]
	    dateStringWithFormat: @"%a, %d %b %Y %H:%M:%S GMT"];

	[_socket writeFormat: @"HTTP/1.1 %d %s\r\n"
	[_socket writeFormat: @"HTTP/1.1 %d %@\r\n"
			      @"Date: %@\r\n"
			      @"Server: %@\r\n"
			      @"\r\n",
			      statusCode, statusCodeToString(statusCode),
			      statusCode,
			      of_http_status_code_to_string(statusCode),
			      date, _server.name];

	return false;
}

- (void)createResponse
{
655
656
657
658
659
660
661
662


663
664
665
666
667
668

669



670
671
672
673
674
675
676
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







-
+
+






+

+
+
+







			response: response];

	objc_autoreleasePoolPop(pool);
}
@end

@implementation OFHTTPServerRequestBodyStream
- (instancetype)initWithSocket: (OFTCPSocket *)sock
- (instancetype)initWithSocket: (OFStreamSocket *)sock
		       chunked: (bool)chunked
		 contentLength: (uintmax_t)contentLength
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_chunked = chunked;
		_toRead = contentLength;

		if (_chunked && _toRead > 0)
			@throw [OFInvalidArgumentException exception];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
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
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







-
+
+

-
-
+

-
+
+
+

+
+
+
+
-
-
+
+

-
-
+
+

-
+

+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







{
	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer
			  length: (size_t)length
{
	size_t ret;
	if (_socket == nil)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_toRead == 0) {
		_atEndOfStream = true;
	if (_atEndOfStream)
		return 0;
	}

	if (_socket.atEndOfStream)
		@throw [OFTruncatedDataException exception];

	/* Content-Length */
	if (!_chunked) {
		size_t ret;

	if (length > _toRead)
		length = (size_t)_toRead;
		if (length > (uintmax_t)_toRead)
			length = (size_t)_toRead;

	ret = [_socket readIntoBuffer: buffer
			       length: length];
		ret = [_socket readIntoBuffer: buffer
				       length: length];

	_toRead -= ret;
		_toRead -= ret;

		if (_toRead == 0)
			_atEndOfStream = true;

	return ret;
		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 > (uintmax_t)_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;
		of_range_t range;

		@try {
			line = [_socket tryReadLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidFormatException exception];
		}

		if (line == nil)
			return 0;

		range = [line rangeOfString: @";"];
		if (range.location != OF_NOT_FOUND)
			line = [line substringWithRange:
			    of_range(0, range.location)];

		if (line.length < 1) {
			/*
			 * We have read the empty string because the socket is
			 * at end of stream.
			 */
			if (_socket.atEndOfStream &&
			    range.location == OF_NOT_FOUND)
				@throw [OFTruncatedDataException exception];
			else
				@throw [OFInvalidFormatException exception];
		}

		if ((_toRead = line.hexadecimalValue) < 0)
			@throw [OFOutOfRangeException exception];

		if (_toRead == 0) {
			_setAtEndOfStream = true;
			_toRead = -2;
		}

		objc_autoreleasePoolPop(pool);

		return 0;
	}
}

- (bool)hasDataInReadBuffer
{
	return (super.hasDataInReadBuffer || _socket.hasDataInReadBuffer);
}

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







-
+









-
-
+
+







		[thread stop];

	[_threadPool release];
	_threadPool = nil;
#endif
}

- (void)of_handleAcceptedSocket: (OFTCPSocket *)acceptedSocket
- (void)of_handleAcceptedSocket: (OFStreamSocket *)acceptedSocket
{
	OFHTTPServerConnection *connection = [[[OFHTTPServerConnection alloc]
	    initWithSocket: acceptedSocket
		    server: self] autorelease];

	acceptedSocket.delegate = connection;
	[acceptedSocket asyncReadLine];
}

-    (bool)socket: (OFTCPSocket *)sock
  didAcceptSocket: (OFTCPSocket *)acceptedSocket
-    (bool)socket: (OFStreamSocket *)sock
  didAcceptSocket: (OFStreamSocket *)acceptedSocket
	exception: (id)exception
{
	if (exception != nil) {
		if (![_delegate respondsToSelector:
		    @selector(server:didReceiveExceptionOnListeningSocket:)])
			return false;

Added src/OFIPSocketAsyncConnector.h version [36d029e68c].
























































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

#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>
{
	id _socket;
	OFString *_host;
	uint16_t _port;
	id _Nullable _delegate;
	id _Nullable _block;
	id _Nullable _exception;
	OFData *_Nullable _socketAddresses;
	size_t _socketAddressesIndex;
}

- (instancetype)initWithSocket: (id)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (nullable id)delegate
			 block: (nullable id)block;
- (void)didConnect;
- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
@end

OF_ASSUME_NONNULL_END

Added src/OFIPSocketAsyncConnector.m version [6a448fb9d2].








































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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"

@implementation OFIPSocketAsyncConnector
- (instancetype)initWithSocket: (id)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (id)delegate
			 block: (id)block
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_host = [host copy];
		_port = port;
		_delegate = [delegate retain];
		_block = [block copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_socket release];
	[_host release];
	[_delegate release];
	[_block release];
	[_exception release];
	[_socketAddresses release];

	[super dealloc];
}

- (void)didConnect
{
	if (_exception == nil)
		[_socket setCanBlock: true];

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		if ([_socket isKindOfClass: [OFTCPSocket class]])
			((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];

		[sock cancelAsyncRequests];
		[sock of_closeSocket];

		if (_socketAddressesIndex >= _socketAddresses.count) {
			_exception = [exception retain];
			[self didConnect];
		} else {
			/*
			 * We must not call it before returning, as otherwise
			 * the new socket would be removed from the queue upon
			 * return.
			 */
			OFRunLoop *runLoop = [OFRunLoop currentRunLoop];
			SEL selector =
			    @selector(tryNextAddressWithRunLoopMode:);
			OFTimer *timer = [OFTimer
			    timerWithTimeInterval: 0
					   target: self
					 selector: selector
					   object: runLoop.currentMode
					  repeats: false];
			[runLoop addTimer: timer
				  forMode: runLoop.currentMode];
		}

		return;
	}

	[self didConnect];
}

- (id)of_connectionFailedExceptionForErrNo: (int)errNo
{
	return [OFConnectionFailedException exceptionWithHost: _host
							 port: _port
						       socket: _socket
							errNo: errNo];
}

- (void)tryNextAddressWithRunLoopMode: (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];
			return;
		}

		[self tryNextAddressWithRunLoopMode: runLoopMode];
		return;
	}

#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
	/*
	 * On Wii and 3DS, connect() fails if non-blocking is enabled.
	 *
	 * Additionally, on Wii, there is no getsockopt(), so it would not be
	 * possible to get the error (or success) after connecting anyway.
	 *
	 * So for now, connecting is blocking on Wii and 3DS.
	 *
	 * FIXME: Use a different thread as a work around.
	 */
	[_socket setCanBlock: true];
#else
	[_socket setCanBlock: false];
#endif

	if (![_socket of_connectSocketToAddress: &address
					  errNo: &errNo]) {
#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
		if (errNo == EINPROGRESS) {
			[OFRunLoop of_addAsyncConnectForSocket: _socket
							  mode: runLoopMode
						      delegate: self];
			return;
		} else {
#endif
			[_socket of_closeSocket];

			if (_socketAddressesIndex >= _socketAddresses.count) {
				_exception = [[OFConnectionFailedException
				    alloc] initWithHost: _host
						   port: _port
						 socket: _socket
						  errNo: errNo];
				[self didConnect];
				return;
			}

			[self tryNextAddressWithRunLoopMode: runLoopMode];
			return;
#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
		}
#endif
	}

#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
	[_socket setCanBlock: false];
#endif

	[self didConnect];
}

- (void)resolver: (OFDNSResolver *)resolver
  didResolveHost: (OFString *)host
       addresses: (OFData *)addresses
       exception: (id)exception
{
	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return;
	}

	_socketAddresses = [addresses copy];

	[self tryNextAddressWithRunLoopMode:
	    [OFRunLoop currentRunLoop].currentMode];
}

- (void)startWithRunLoopMode: (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

Added src/OFIPXSocket.h version [5fdb0d34da].


















































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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.
 */

#import "OFDatagramSocket.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;

/*!
 * @protocol OFIPXSocketDelegate OFIPXSocket.h ObjFW/OFIPXSocket.h
 *
 * @brief A delegate for OFIPXSocket.
 */
@protocol OFIPXSocketDelegate <OFDatagramSocketDelegate>
@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
 *	    was called to create one "instance" for every thread!
 */
@interface OFIPXSocket: OFDatagramSocket
{
#ifndef OF_WINDOWS
	uint8_t _packetType;
#endif
	OF_RESERVE_IVARS(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 <OFIPXSocketDelegate> delegate;

/*!
 * @brief Bind the socket to the specified network, node and port with the
 *	  specified packet type.
 *
 * @param port The port (sometimes called socket number) to bind to. 0 means to
 *	       pick one and return it.
 * @param packetType The packet type to use on the socket
 * @return The address on which this socket can be reached
 */
- (of_socket_address_t)bindToPort: (uint16_t)port
		       packetType: (uint8_t)packetType;
@end

OF_ASSUME_NONNULL_END

Added src/OFIPXSocket.m version [06e0564b9c].






































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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 "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

Modified src/OFInvertedCharacterSet.h from [3c2c08c41b] to [08af99fb50].

21
22
23
24
25
26
27
28

29
30
31
32
21
22
23
24
25
26
27

28

29
30
31







-
+
-




@interface OFInvertedCharacterSet: OFCharacterSet
{
	OFCharacterSet *_characterSet;
	bool (*_characterIsMember)(id, SEL, of_unichar_t);
}

- (instancetype)of_initWithCharacterSet: (OFCharacterSet *)characterSet
- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet;
    OF_METHOD_FAMILY(init);
@end

OF_ASSUME_NONNULL_END

Modified src/OFInvertedCharacterSet.m from [8e068ed400] to [33cd491c87].

24
25
26
27
28
29
30
31

32
33
34

35
36
37








38
39
40
41
42
43
44
24
25
26
27
28
29
30

31
32
33
34
35



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50







-
+



+
-
-
-
+
+
+
+
+
+
+
+








@implementation OFInvertedCharacterSet
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithCharacterSet: (OFCharacterSet *)characterSet
- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet
{
	self = [super init];

	@try {
	_characterSet = [characterSet retain];
	_characterIsMember = (bool (*)(id, SEL, of_unichar_t))
	    [_characterSet methodForSelector: @selector(characterIsMember:)];
		_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];

Modified src/OFLocale.h from [6fa1140bc7] to [dfcb090c6e].

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







-
+
+



-
-
+
















-
+
+






-
+




 *	 the localized string.
 *
 * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes
 *	 care of the `nil` sentinel automatically.
 *
 * @param ID The ID for the localized string
 * @param fallback The fallback to use in case the localized string cannot be
 *		   looked up or is missing
 *		   looked up or is missing. This can also be an array and use
 *		   plural scripting, just like with the JSON language files.
 * @return The localized string
 */
- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (OFConstantString *)fallback, ...
				    OF_SENTINEL;
			  fallback: (id)fallback, ... OF_SENTINEL;

/*!
 * @brief Returns the localized string for the specified ID, using the fallback
 *	  string if it cannot be looked up or is missing.
 *
 * @note This takes a variadic argument, terminated by `nil` and passed as
 *	 va_list, that consists of pairs of variable names and variable values,
 *	 which will be replaced inside the localized string. For example, you
 *	 can pass `@"name", @"foo", nil`, causing `%[name]` to be replaced with
 *	 `foo` in the localized string.
 *
 * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes
 *	 care of the `nil` sentinel automatically.
 *
 * @param ID The ID for the localized string
 * @param fallback The fallback to use in case the localized string cannot be
 *		   looked up or is missing
 *		   looked up or is missing. This can also be an array and use
 *		   plural scripting, just like with the JSON language files.
 * @param arguments A va_list of arguments, consisting of pairs of variable
 *		    names and values to replace in the localized string,
 *		    terminated with `nil`
 * @return The localized string
 */
- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (OFConstantString *)fallback
			  fallback: (id)fallback
			 arguments: (va_list)arguments;
@end

OF_ASSUME_NONNULL_END

Modified src/OFLocale.m from [36d525a782] to [2f0ab9defe].

19
20
21
22
23
24
25

26
27
28
29

30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
19
20
21
22
23
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 <locale.h>

#import "OFLocale.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFDictionary.h"
#import "OFNumber.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"

#ifdef OF_AMIGAOS
# include <proto/dos.h>
# include <proto/exec.h>
# include <proto/locale.h>
#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)
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86



























































































































































































































87
88
89





























90
91
92
93
94
95
96
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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







-
+





















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







			*tmp++ = '\0';

			@try {
				if (encoding != NULL)
					*encoding = of_string_parse_encoding(
					    [OFString stringWithCString: tmp
							       encoding: enc]);
			} @catch (OFInvalidEncodingException *e) {
			} @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: @")"]) {
			for (;;) {
				OFString *operator = operators.lastObject;
				if (operator == nil)
					@throw [OFInvalidFormatException
					    exception];

				if ([operator isEqual: @"("]) {
					[operators removeLastObject];
					break;
				}

				[tokens addObject: operator];
				[operators removeLastObject];
			}
			continue;
		}

		precedence = [[operatorPrecedences objectForKey: token]
		    unsignedIntValue];
		if (precedence > 0) {
			for (;;) {
				OFNumber *operator = operators.lastObject;
				unsigned otherPrecedence;

				if (operator == nil || [operator isEqual: @"("])
					break;

				otherPrecedence = [[operatorPrecedences
				    objectForKey: operator] unsignedIntValue];
				if (otherPrecedence >= precedence)
					break;

				[tokens addObject: operator];
				[operators removeLastObject];
			}

			[operators addObject: token];
			continue;
		}

		c = [token characterAtIndex: 0];
		if ((c < '0' || c > '9') && c != '-')
			if ((token = [variables objectForKey: token]) == nil)
				@throw [OFInvalidFormatException exception];

		[tokens addObject:
		    [OFNumber numberWithDouble: token.doubleValue]];
	}
	for (size_t i = operators.count; i > 0; i--) {
		OFString *operator = [operators objectAtIndex: i - 1];

		if ([operator isEqual: @"("])
			@throw [OFInvalidFormatException exception];

		[tokens addObject: operator];
	}

	/* Evaluate RPN */
	stack = [OFMutableArray array];
	for (id token in tokens) {
		unsigned precedence = [[operatorPrecedences
		    objectForKey: token] unsignedIntValue];
		id var, first, second;
		size_t stackSize;

		/* Only unary operators have precedence 1 */
		if (precedence > 1) {
			stackSize = stack.count;
			first = [stack objectAtIndex: stackSize - 2];
			second = [stack objectAtIndex: stackSize - 1];

			if ([token isEqual: @"=="])
				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 numberWithIntMax:
				    [first intMaxValue] % [second intMaxValue]];
			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 intMaxValue]];
			else
				OF_ENSURE(0);

			[stack replaceObjectAtIndex: stackSize - 1
					 withObject: var];
		} else
			[stack addObject: token];
	}

	if (stack.count != 1)
		@throw [OFInvalidFormatException exception];

	return [stack.firstObject boolValue];
}

static OFString *
evaluateConditionals(OFArray *conditions, OFDictionary *variables)
{
	for (OFDictionary *dictionary in conditions) {
		OFString *condition, *value;
		bool found = false;

		for (OFString *key in dictionary) {
			if (found)
				@throw [OFInvalidFormatException exception];

			condition = key;
			value = [dictionary objectForKey: key];

			if (![condition isKindOfClass: [OFString class]] ||
			    ![value isKindOfClass: [OFString class]])
				@throw [OFInvalidFormatException exception];

			found = true;
		}
		if (!found)
			@throw [OFInvalidFormatException exception];

		if (evaluateCondition(condition, variables))
			return value;
	}

	/* Need to have a fallback as the last one. */
	@throw [OFInvalidFormatException exception];
}

static OFString *
evaluateArray(OFArray *array, OFDictionary *variables)
{
	OFMutableString *string = [OFMutableString string];

	for (id object in array) {
		if ([object isKindOfClass: [OFString class]])
			[string appendString: object];
		else if ([object isKindOfClass: [OFArray class]])
			[string appendString:
			    evaluateConditionals(object, variables)];
		else
			@throw [OFInvalidFormatException exception];
	}

	[string makeImmutable];

	return string;
}

@implementation OFLocale
@synthesize language = _language, territory = _territory, encoding = _encoding;
@synthesize decimalPoint = _decimalPoint;

+ (void)initialize
{
	OFNumber *one, *two, *three, *four;

	if (self != [OFLocale class])
		return;

	/* 1 is also used to denote a unary operator. */
	one = [OFNumber numberWithUnsignedInt: 1];
	two = [OFNumber numberWithUnsignedInt: 2];
	three = [OFNumber numberWithUnsignedInt: 3];
	four = [OFNumber numberWithUnsignedInt: 4];

	operatorPrecedences = [[OFDictionary alloc] initWithKeysAndObjects:
	    @"==", two,
	    @"!=", two,
	    @"<", two,
	    @"<=", two,
	    @">", two,
	    @">=", two,
	    @"+", two,
	    @"%", two,
	    @"&&", three,
	    @"||", four,
	    @"!", one,
	    @"is_real", one,
	    nil];
}

+ (OFLocale *)currentLocale
{
	return currentLocale;
}

+ (OFString *)language
178
179
180
181
182
183
184
185

186
187
188
189
190
191
192
428
429
430
431
432
433
434

435
436
437
438
439
440
441
442







-
+







# endif
			of_string_encoding_t ASCII = OF_STRING_ENCODING_ASCII;

			@try {
				_encoding = of_string_parse_encoding(
				    [OFString stringWithCString: buffer
						       encoding: ASCII]);
			} @catch (OFInvalidEncodingException *e) {
			} @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,
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
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







-
-
+
-
-


-
+



-
+



-
+







		if (GetVar("Language", buffer, sizeof(buffer), 0) > 0)
			_language = [[OFString alloc]
			    initWithCString: buffer
				   encoding: _encoding];

		if ((locale = OpenLocale(NULL)) != NULL) {
			@try {
				union {
					uint32_t u32;
				uint32_t territory;
					char c[4];
				} territory;
				size_t length;

				territory.u32 =
				territory =
				    OF_BSWAP32_IF_LE(locale->loc_CountryCode);

				for (length = 0; length < 4; length++)
					if (territory.c[length] == 0)
					if (((char *)&territory)[length] == 0)
						break;

				_territory = [[OFString alloc]
				    initWithCString: territory.c
				    initWithCString: (char *)&territory
					   encoding: _encoding
					     length: length];
			} @finally {
				CloseLocale(locale);
			}
		}

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







-
+














-
+




+
+



+
+
+
+
+








-
+







+
+
+
-
-
+
+







	    [[OFString stringWithContentsOfFile: languageFile] JSONValue]];

	objc_autoreleasePoolPop(pool);
}
#endif

- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (OFConstantString *)fallback, ...
			  fallback: (id)fallback, ...
{
	OFString *ret;
	va_list args;

	va_start(args, fallback);
	ret = [self localizedStringForID: ID
				fallback: fallback
			       arguments: args];
	va_end(args);

	return ret;
}

- (OFString *)localizedStringForID: (OFConstantString *)ID
			  fallback: (OFConstantString *)fallback
			  fallback: (id)fallback
			 arguments: (va_list)arguments
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();
	OFMutableDictionary *variables;
	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;

		if ([string isKindOfClass: [OFArray class]])
			string = [string componentsJoinedByString: @""];
			string = evaluateArray(string, variables);

		UTF8String = [string UTF8String];
		UTF8StringLength = [string UTF8StringLength];
		break;
	}

	if (UTF8String == NULL) {
		if ([fallback isKindOfClass: [OFArray class]])
			fallback = evaluateArray(fallback, variables);

		UTF8String = fallback.UTF8String;
		UTF8StringLength = fallback.UTF8StringLength;
		UTF8String = [fallback UTF8String];
		UTF8StringLength = [fallback UTF8StringLength];
	}

	state = 0;
	last = 0;
	for (size_t i = 0; i < UTF8StringLength; i++) {
		switch (state) {
		case 0:
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
615
616
617
618
619
620
621



622
623
624

625









626

627






628




629
630
631
632
633
634
635







-
-
-



-
+
-
-
-
-
-
-
-
-
-

-
+
-
-
-
-
-
-
+
-
-
-
-







			} else {
				[ret appendString: @"%"];
				state = 0;
			}
			break;
		case 2:
			if (UTF8String[i] == ']') {
				va_list argsCopy;
				OFConstantString *name;

				OFString *var = [OFString
				    stringWithUTF8String: UTF8String + last
						  length: i - last];

				OFString *value = [variables objectForKey: var];
				/*
				 * We loop, as most of the time, we only have
				 * one or maybe two variables, meaning looping
				 * is faster than constructing a dictionary.
				 */
				va_copy(argsCopy, arguments);
				while ((name = va_arg(argsCopy,
				    OFConstantString *)) != nil) {
					id value = va_arg(argsCopy, id);

					if (value == nil)
				if (value != nil)
						@throw
						    [OFInvalidArgumentException
						    exception];

					if ([name isEqual: var]) {
						[ret appendString:
					[ret appendString: value.description];
						    [value description]];
						break;
					}
				}

				last = i + 1;
				state = 0;
			}
			break;
		}
	}

Modified src/OFMD5Hash.h from [afbdcd7e31] to [7314ebcbd4].

30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47







-
+










@interface OFMD5Hash: OFObject <OFCryptoHash>
{
	OFSecureData *_iVarsData;
	struct of_md5_hash_ivars {
		uint32_t state[4];
		uint64_t bits;
		union of_md5_hash_buffer {
			uint8_t bytes[64];
			unsigned char bytes[64];
			uint32_t words[16];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFMapTable.h from [39846c8467] to [18718d5a71].

23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39

40

41
42
43
44
45
46
47
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48







-
+









+
-
+







/*! @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.
 */
typedef struct {
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 */
	uint32_t (*_Nullable hash)(void *_Nullable object);
	/*! The function to compare keys / objects */
	bool (*_Nullable equal)(void *_Nullable object1,
	    void *_Nullable object2);
};
} of_map_table_functions_t;
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

Modified src/OFMutableArray.h from [9d4047891f] to [5d8e847393].

200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214







-
+







 */
- (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 @ref OFComparing#compare:.
 *		   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

Modified src/OFMutableDictionary.h from [50c90d0e94] to [ceac8e2f46].

64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81

82
83
84
85
86
87
88
89







-
+










-
+







 * @return An initialized OFMutableDictionary
 */
- (instancetype)initWithCapacity: (size_t)capacity;

/*!
 * @brief Sets an object for a key.
 *
 * A key can be any object that conforms to the @ref OFCopying protocol.
 * 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 @ref OFCopying protocol.
 * 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:.
 */

Modified src/OFMutableString.h from [dd3e9e1fa2] to [7d782f5154].

11
12
13
14
15
16
17



18

19
20
21

22
23
24
25
26
27
28
11
12
13
14
15
16
17
18
19
20

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







+
+
+
-
+



+







 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either 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_OF_MUTABLE_STRING_H
#define OBJFW_OF_MUTABLE_STRING_H

#import "OFString.h"
#include "OFString.h"

OF_ASSUME_NONNULL_BEGIN

#ifdef __OBJC__
/*!
 * @class OFMutableString OFString.h ObjFW/OFString.h
 *
 * @brief A class for storing and modifying strings.
 */
@interface OFMutableString: OFString
/*!
208
209
210
211
212
213
214

215
216


212
213
214
215
216
217
218
219
220
221
222
223







+


+
+
- (void)deleteEnclosingWhitespaces;

/*!
 * @brief Converts the mutable string to an immutable string.
 */
- (void)makeImmutable;
@end
#endif

OF_ASSUME_NONNULL_END

#endif

Modified src/OFNumber.m from [5a67a624f2] to [f96cb2d4be].

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
554
555
556
557
558
559
560








561

562






563



564
565
566

567
568
569
570
571
572
573
574
575
576







-
-
-
-
-
-
-
-

-
+
-
-
-
-
-
-
+
-
-
-
+
+

-
+
+
+







			 */
			_type = OF_NUMBER_TYPE_UINTMAX;
			_value.uIntMax = element.decimalValue;
		} else if ([typeString isEqual: @"signed"]) {
			_type = OF_NUMBER_TYPE_INTMAX;
			_value.intMax = element.decimalValue;
		} else if ([typeString isEqual: @"float"]) {
			union {
				float f;
				uint32_t u;
			} f;

			f.u = OF_BSWAP32_IF_LE(
			    (uint32_t)element.hexadecimalValue);

			_type = OF_NUMBER_TYPE_FLOAT;
			_value.float_ = OF_BSWAP_FLOAT_IF_LE(f.f);
			_value.float_ = OF_BSWAP_FLOAT_IF_LE(
		} else if ([typeString isEqual: @"double"]) {
			union {
				double d;
				uint64_t u;
			} d;

			    OF_INT_TO_FLOAT_RAW(OF_BSWAP32_IF_LE(
			d.u = OF_BSWAP64_IF_LE(
			    (uint64_t)element.hexadecimalValue);

			    (uint32_t)element.hexadecimalValue)));
		} else if ([typeString isEqual: @"double"]) {
			_type = OF_NUMBER_TYPE_DOUBLE;
			_value.double_ = OF_BSWAP_DOUBLE_IF_LE(d.d);
			_value.double_ = OF_BSWAP_DOUBLE_IF_LE(
			    OF_INT_TO_DOUBLE_RAW(OF_BSWAP64_IF_LE(
			    (uint64_t)element.hexadecimalValue)));
		} else
			@throw [OFInvalidArgumentException exception];

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
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
1059
1060
1061
1062
1063
1064
1065


1066


1067
1068
1069
1070

1071
1072
1073

1074
1075
1076
1077
1078
1079
1080
1081







-
-
+
-
-




-
+


-
+







				type &= ~OF_NUMBER_TYPE_FLOAT;
		}
	}

	OF_HASH_INIT(hash);

	if (type & OF_NUMBER_TYPE_FLOAT) {
		union {
			double d;
		double d;
			uint8_t b[sizeof(double)];
		} d;

		if (isnan(self.doubleValue))
			return 0;

		d.d = OF_BSWAP_DOUBLE_IF_BE(self.doubleValue);
		d = OF_BSWAP_DOUBLE_IF_BE(self.doubleValue);

		for (uint_fast8_t i = 0; i < sizeof(double); i++)
			OF_HASH_ADD(hash, d.b[i]);
			OF_HASH_ADD(hash, ((char *)&d)[i]);
	} else if (type & OF_NUMBER_TYPE_SIGNED) {
		intmax_t v = self.intMaxValue * -1;

		while (v != 0) {
			OF_HASH_ADD(hash, v & 0xFF);
			v >>= 8;
		}
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
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







-
+



-
+
-
-
-
-
-
-
-


-
-
-
+
+
+


-
+
-
-
-
-
-
-
-


-
-
-
+
+
+
+







	case OF_NUMBER_TYPE_INT8:
	case OF_NUMBER_TYPE_INT16:
	case OF_NUMBER_TYPE_INT32:
	case OF_NUMBER_TYPE_INT64:
	case OF_NUMBER_TYPE_SSIZE:
	case OF_NUMBER_TYPE_INTMAX:
	case OF_NUMBER_TYPE_PTRDIFF:
	case OF_NUMBER_TYPE_INTPTR:;
	case OF_NUMBER_TYPE_INTPTR:
		[element addAttributeWithName: @"type"
				  stringValue: @"signed"];
		break;
	case OF_NUMBER_TYPE_FLOAT:;
	case OF_NUMBER_TYPE_FLOAT:
		union {
			float f;
			uint32_t u;
		} f;

		f.f = OF_BSWAP_FLOAT_IF_LE(_value.float_);

		[element addAttributeWithName: @"type"
				  stringValue: @"float"];
		element.stringValue =
		    [OFString stringWithFormat: @"%08" PRIx32,
						OF_BSWAP32_IF_LE(f.u)];
		element.stringValue = [OFString stringWithFormat: @"%08" PRIx32,
		    OF_BSWAP32_IF_LE(OF_FLOAT_TO_INT_RAW(OF_BSWAP_FLOAT_IF_LE(
		    _value.float_)))];

		break;
	case OF_NUMBER_TYPE_DOUBLE:;
	case OF_NUMBER_TYPE_DOUBLE:
		union {
			double d;
			uint64_t u;
		} d;

		d.d = OF_BSWAP_DOUBLE_IF_LE(_value.double_);

		[element addAttributeWithName: @"type"
				  stringValue: @"double"];
		element.stringValue =
		    [OFString stringWithFormat: @"%016" PRIx64,
						OF_BSWAP64_IF_LE(d.u)];
		element.stringValue = [OFString
		    stringWithFormat: @"%016" PRIx64,
		    OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE(
		    _value.double_)))];

		break;
	default:
		@throw [OFInvalidFormatException exception];
	}

	[element retain];

Modified src/OFObject.h from [67744f52d1] to [0775955f65].

11
12
13
14
15
16
17



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


34
35
36
37
38
39
40
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34


35
36
37
38
39
40
41
42
43







+
+
+














-
-
+
+







 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#ifndef OBJFW_OF_OBJECT_H
#define OBJFW_OF_OBJECT_H

#include "objfw-defs.h"

#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS
#endif
#ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS
#endif

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>

#import "macros.h"
#import "block.h"
#include "macros.h"
#include "block.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__
84
85
86
87
88
89
90
91

92
93
94
95

96

97
98
99
100
101
102
103
87
88
89
90
91
92
93

94
95
96
97
98
99

100
101
102
103
104
105
106
107







-
+




+
-
+







} of_byte_order_t;

/*!
 * @struct of_range_t OFObject.h ObjFW/OFObject.h
 *
 * @brief A range.
 */
typedef struct OF_BOXABLE {
struct OF_BOXABLE of_range_t {
	/*! The start of the range */
	size_t location;
	/*! The length of the range */
	size_t length;
};
} of_range_t;
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
135
136
137
138
139
140
141
142

143
144
145
146

147

148
149
150
151
152
153
154
139
140
141
142
143
144
145

146
147
148
149
150
151

152
153
154
155
156
157
158
159







-
+




+
-
+







typedef double of_time_interval_t;

/*!
 * @struct of_point_t OFObject.h ObjFW/OFObject.h
 *
 * @brief A point.
 */
typedef struct OF_BOXABLE {
struct OF_BOXABLE of_point_t {
	/*! The x coordinate of the point */
	float x;
	/*! The y coordinate of the point */
	float y;
};
} of_point_t;
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
181
182
183
184
185
186
187
188

189
190
191
192

193

194
195
196
197
198
199
200
186
187
188
189
190
191
192

193
194
195
196
197
198

199
200
201
202
203
204
205
206







-
+




+
-
+







}

/*!
 * @struct of_dimension_t OFObject.h ObjFW/OFObject.h
 *
 * @brief A dimension.
 */
typedef struct OF_BOXABLE {
struct OF_BOXABLE of_dimension_t {
	/*! The width of the dimension */
	float width;
	/*! The height of the dimension */
	float height;
};
} of_dimension_t;
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
227
228
229
230
231
232
233
234

235
236
237
238

239

240
241
242
243
244
245
246
233
234
235
236
237
238
239

240
241
242
243
244
245

246
247
248
249
250
251
252
253







-
+




+
-
+







}

/*!
 * @struct of_rectangle_t OFObject.h ObjFW/OFObject.h
 *
 * @brief A rectangle.
 */
typedef struct OF_BOXABLE {
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;
};
} of_rectangle_t;
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
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
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







+













-
+

-
+

-
+








	if (!of_dimension_equal(rectangle1.size, rectangle2.size))
		return false;

	return true;
}

#ifdef __OBJC__
@class OFMethodSignature;
@class OFString;
@class OFThread;

/*!
 * @protocol OFObject OFObject.h ObjFW/OFObject.h
 *
 * @brief The protocol which all root classes implement.
 */
@protocol OFObject
/*!
 * @brief The class of the object.
 */
#ifndef __cplusplus
# ifndef __cplusplus
@property (readonly, nonatomic) Class class;
#else
# else
@property (readonly, nonatomic, getter=class) Class class_;
#endif
# endif

/*!
 * @brief The superclass of the object.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) Class superclass;

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







+






+




-
+

-
+

-
+


-
-
+
+

-
+

-
+



-
+







/*!
 * @brief Retain a weak reference to this object.
 *
 * @return Whether a weak reference to this object has been retained
 */
- (bool)retainWeakReference;
@end
#endif

/*!
 * @class OFObject OFObject.h ObjFW/OFObject.h
 *
 * @brief The root class for all other classes inside ObjFW.
 */
#ifdef __OBJC__
OF_ROOT_CLASS
@interface OFObject <OFObject>
{
@private
#ifndef __clang_analyzer__
# ifndef __clang_analyzer__
	Class _isa;
#else
# else
	Class _isa __attribute__((__unused__));
#endif
# endif
}

#ifdef OF_HAVE_CLASS_PROPERTIES
# ifndef __cplusplus
# ifdef OF_HAVE_CLASS_PROPERTIES
#  ifndef __cplusplus
@property (class, readonly, nonatomic) Class class;
# else
#  else
@property (class, readonly, nonatomic, getter=class) Class class_;
# endif
#  endif
@property (class, readonly, nonatomic) OFString *className;
@property (class, readonly, nullable, nonatomic) Class superclass;
@property (class, readonly, nonatomic) OFString *description;
#endif
# endif

/*!
 * @brief The name of the object's class.
 */
@property (readonly, nonatomic) OFString *className;

/*!
733
734
735
736
737
738
739
740

741
742










743





744
745
746







747
748
749
750
751
752
753
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







-
+

-
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
+
+
+
+
+
+
+







 * @return The class of the object
 */
+ (id)copy;

/*!
 * @brief Initializes an already allocated object.
 *
 * Derived classes may override this, but need to do
 * Derived classes may override this, but need to use the following pattern:
 * @code
 *   self = [super init]
 * self = [super init];
 *
 * @try {
 *         // Custom initialization code goes here.
 * } @catch (id e) {
 *         [self release];
 *         @throw e;
 * }
 *
 * return self;
 * @endcode
 *
 * With ARC enabled, the following pattern needs to be used instead:
 * @code
 * self = [super init];
 *
 * before they do any initialization themselves. @ref init may never return
 * `nil`, instead an exception (for example @ref
 * OFInitializationFailedException) should be thrown.
 * // Custom initialization code goes here.
 *
 * return self;
 * @endcode
 *
 * @ref init may never return `nil`, instead an exception (for example
 * @ref OFInitializationFailedException) should be thrown.
 *
 * @return An initialized object
 */
- (instancetype)init;

/*!
 * @brief Returns the method signature for the specified selector.
933
934
935
936
937
938
939
940

941
942
943
944
945
946
947
961
962
963
964
965
966
967

968
969
970
971
972
973
974
975







-
+







- (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
# 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
 * @param waitUntilDone Whether to wait until the perform finished
 */
1191
1192
1193
1194
1195
1196
1197
1198

1199
1200
1201
1202
1203
1204
1205
1219
1220
1221
1222
1223
1224
1225

1226
1227
1228
1229
1230
1231
1232
1233







-
+







- (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
# 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.
 *
 * @note When the message should not be forwarded, you should not return `nil`,
1216
1217
1218
1219
1220
1221
1222



1223

1224
1225
1226
1227
1228
1229
1230
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262







+
+
+

+







 * @warning If you override this method, you must make sure that it never
 *	    returns!
 *
 * @param selector The selector not understood by the receiver
 */
- (void)doesNotRecognizeSelector: (SEL)selector OF_NO_RETURN;
@end
#else
typedef void OFObject;
#endif

#ifdef __OBJC__
/*!
 * @protocol OFCopying OFObject.h ObjFW/OFObject.h
 *
 * @brief A protocol for the creation of copies.
 */
@protocol OFCopying
/*!
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





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







+


















+
-
-
+
+
+
+
+
 * @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
#ifdef OF_APPLE_RUNTIME
extern void *_Null_unspecified objc_autoreleasePoolPush(void);
extern void objc_autoreleasePoolPop(void *_Null_unspecified pool);
#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;
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#ifdef __OBJC__
#import "OFObject+KeyValueCoding.h"
#import "OFObject+Serialization.h"
# import "OFObject+KeyValueCoding.h"
# import "OFObject+Serialization.h"
#endif

#endif

Modified src/OFOptionsParser.h from [0f3268b0df] to [d206f16719].

23
24
25
26
27
28
29
30

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

30
31
32
33
34
35
36
37







-
+







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.
 */
typedef struct {
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;
51
52
53
54
55
56
57
58

59
60
61

62

63
64
65
66
67
68
69
51
52
53
54
55
56
57

58
59
60
61
62

63
64
65
66
67
68
69
70







-
+



+
-
+







	/*!
	 * An optional pointer to a bool that is set to whether the option has
	 * been specified.
	 */
	bool *_Nullable isSpecifiedPtr;

	/*!
	 * An optional pointer to an @ref OFString * that is set to the
	 * 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;
};
} of_options_parser_option_t;
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

Modified src/OFPlugin.m from [3e825cedc9] to [539230b0ba].

21
22
23
24
25
26
27

28
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45




46
47
48
49
50
51
52
21
22
23
24
25
26
27
28
29

30
31
32
33
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 <string.h>

#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#endif

#import "OFPlugin.h"
#import "OFLocale.h"
#import "OFString.h"
#import "OFLocale.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);
		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

Modified src/OFProcess.h from [b0e3da0617] to [376740648e].

193
194
195
196
197
198
199







200
201
202
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209







+
+
+
+
+
+
+



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

Modified src/OFRIPEMD160Hash.h from [b0c573b36f] to [bd428762b3].

30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47







-
+










@interface OFRIPEMD160Hash: OFObject <OFCryptoHash>
{
	OFSecureData *_iVarsData;
	struct of_ripemd160_hash_ivars {
		uint32_t state[5];
		uint64_t bits;
		union of_ripemd160_hash_buffer {
			uint8_t bytes[64];
			unsigned char bytes[64];
			uint32_t words[16];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFRunLoop+Private.h from [f938cc8f46] to [f46c29fa91].

14
15
16
17
18
19
20
21
22



23
24
25
26
27
28
29


30

31
32
33
34
35
36
37
14
15
16
17
18
19
20


21
22
23
24
25
26
27
28


29
30
31
32
33
34
35
36
37
38
39







-
-
+
+
+





-
-
+
+

+







 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#import "OFRunLoop.h"
#import "OFStream.h"
#ifdef OF_HAVE_SOCKETS
# import "OFTCPSocket.h"
# import "OFUDPSocket.h"
# import "OFDatagramSocket.h"
# import "OFSequencedPacketSocket.h"
# import "OFStreamSocket.h"
#endif

OF_ASSUME_NONNULL_BEGIN

#ifdef OF_HAVE_SOCKETS
@protocol OFTCPSocketDelegate_Private <OFObject>
- (void)of_socketDidConnect: (OFTCPSocket *)socket
@protocol OFRunLoopConnectDelegate <OFObject>
- (void)of_socketDidConnect: (id)socket
		  exception: (nullable id)exception;
- (id)of_connectionFailedExceptionForErrNo: (int)errNo;
@end
#endif

@interface OFRunLoop ()
+ (void)of_setMainRunLoop: (OFRunLoop *)runLoop;
#ifdef OF_HAVE_SOCKETS
+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
79
80
81
82
83
84
85
86
87


88
89

90
91
92


93
94

95
96
97
98

99
100
101
102
103












104
105
106










107
108
109

110
111
112



113
114

115
116

117
118
119
120

121
122
123
124
125
126
127
128
129
81
82
83
84
85
86
87


88
89


90
91


92
93


94




95





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


109
110
111
112
113
114
115
116
117
118

119

120



121
122
123


124
125

126


127

128

129
130
131
132
133
134
135
136







-
-
+
+
-
-
+

-
-
+
+
-
-
+
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
+
+
+
+
-

-
+
-
-
-
+
+
+
-
-
+

-
+
-
-

-
+
-








# 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_addAsyncConnectForTCPSocket: (OFTCPSocket *)socket
				  mode: (of_run_loop_mode_t)mode
+ (void)of_addAsyncConnectForSocket: (id)socket
			       mode: (of_run_loop_mode_t)mode
			      delegate: (id <OFTCPSocketDelegate_Private>)
					    delegate;
			   delegate: (id <OFRunLoopConnectDelegate>)delegate;
# endif
+ (void)of_addAsyncAcceptForTCPSocket: (OFTCPSocket *)socket
				 mode: (of_run_loop_mode_t)mode
+ (void)of_addAsyncAcceptForSocket: (id)socket
			      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
				block: (nullable
			     block: (nullable id)block
					   of_tcp_socket_async_accept_block_t)
					   block
# endif
			     delegate: (nullable id <OFTCPSocketDelegate>)
			  delegate: (nullable id)delegate;
					   delegate;
+ (void)of_addAsyncReceiveForUDPSocket: (OFUDPSocket *)socket
				buffer: (void *)buffer
				length: (size_t)length
				  mode: (of_run_loop_mode_t)mode
+ (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_udp_socket_async_receive_block_t)
     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
					    block
# endif
			      delegate: (nullable id <OFUDPSocketDelegate>)
  delegate: (nullable id <OFSequencedPacketSocketDelegate>) delegate;
					    delegate;
+ (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)socket
			       data: (OFData *)data
+ (void)of_addAsyncSendForSequencedPacketSocket:
					       (OFSequencedPacketSocket *)socket
      data: (OFData *)data
			   receiver: (const of_socket_address_t *)receiver
			       mode: (of_run_loop_mode_t)mode
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			      block: (nullable
     block: (nullable of_sequenced_packet_socket_async_send_data_block_t)block
					 of_udp_socket_async_send_data_block_t)
					 block
# endif
			   delegate: (nullable id <OFUDPSocketDelegate>)
  delegate: (nullable id <OFSequencedPacketSocketDelegate>)delegate;
					 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

Modified src/OFRunLoop.m from [ac74ecd3ae] to [8701d2b9f0].

23
24
25
26
27
28
29



30
31


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
23
24
25
26
27
28
29
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 "OFRunLoop.h"
#import "OFRunLoop+Private.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_SOCKETS
# import "OFKernelEventObserver.h"
# import "OFDatagramSocket.h"
# import "OFSequencedPacketSocket.h"
# import "OFSequencedPacketSocket+Private.h"
# import "OFTCPSocket.h"
# import "OFTCPSocket+Private.h"
# import "OFStreamSocket.h"
# import "OFStreamSocket+Private.h"
#endif
#import "OFThread.h"
#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
# import "OFCondition.h"
#endif
#import "OFSortedList.h"
#import "OFTimer.h"
#import "OFTimer+Private.h"
#import "OFDate.h"

#import "OFObserveFailedException.h"
#ifdef OF_HAVE_SOCKETS
# import "OFConnectionFailedException.h"
#endif

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







+
+
+
+
-
+
+
+
+
+
+

+
+



+
+
+
+
+
+
+
+
+
+
+
-
+



-
+






-
+



-
+


-







@end
# endif

@interface OFRunLoopAcceptQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	id _block;
# endif
}
@end
	of_tcp_socket_async_accept_block_t _block;

@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 OFRunLoopUDPReceiveQueueItem: OFRunLoopQueueItem
@interface OFRunLoopPacketReceiveQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_udp_socket_async_receive_block_t _block;
	of_sequenced_packet_socket_async_receive_block_t _block;
# endif
	void *_buffer;
	size_t _length;
}
@end

@interface OFRunLoopUDPSendQueueItem: OFRunLoopQueueItem
@interface OFRunLoopPacketSendQueueItem: OFRunLoopQueueItem
{
@public
# ifdef OF_HAVE_BLOCKS
	of_udp_socket_async_send_data_block_t _block;
	of_sequenced_packet_socket_async_send_data_block_t _block;
# endif
	OFData *_data;
	of_socket_address_t _receiver;
}
@end
#endif

@implementation OFRunLoopState
- (instancetype)init
{
414
415
416
417
418
419
420
421

422
423
424
425
426
427
428
435
436
437
438
439
440
441

442
443
444
445
446
447
448
449







-
+







	} @catch (id e) {
		length = 0;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return _block(object, _buffer, length, exception);
		return _block(length, exception);
	else {
# endif
		if (![_delegate respondsToSelector:
		    @selector(stream:didReadIntoBuffer:length:exception:)])
			return false;

		return [_delegate stream: object
462
463
464
465
466
467
468
469

470
471
472
473
474
475
476
483
484
485
486
487
488
489

490
491
492
493
494
495
496
497







-
+








	if (_readLength != _exactLength && ![object isAtEndOfStream] &&
	    exception == nil)
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		if (!_block(object, _buffer, _readLength, exception))
		if (!_block(_readLength, exception))
			return false;

		_readLength = 0;
		return true;
	} else {
# endif
		if (![_delegate respondsToSelector:
514
515
516
517
518
519
520
521

522
523
524
525
526
527
528
535
536
537
538
539
540
541

542
543
544
545
546
547
548
549







-
+







	}

	if (line == nil && ![object isAtEndOfStream] && exception == nil)
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return _block(object, line, exception);
		return _block(line, exception);
	else {
# endif
		if (![_delegate respondsToSelector:
		    @selector(stream:didReadLine:exception:)])
			return false;

		return [_delegate stream: object
564
565
566
567
568
569
570
571

572
573
574
575
576
577
578
585
586
587
588
589
590
591

592
593
594
595
596
597
598
599







-
+







	_writtenLength += length;

	if (_writtenLength != dataLength && exception == nil)
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newData = _block(object, _data, _writtenLength, exception);
		newData = _block(_data, _writtenLength, exception);

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];
636
637
638
639
640
641
642
643

644
645
646
647
648
649
650
651
657
658
659
660
661
662
663

664

665
666
667
668
669
670
671







-
+
-







	_writtenLength += length;

	if (_writtenLength != cStringLength && exception == nil)
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newString = _block(object, _string, _encoding, _writtenLength,
		newString = _block(_string, _writtenLength, exception);
		    exception);

		if (newString == nil)
			return false;

		oldString = _string;
		_string = [newString copy];
		[oldString release];
693
694
695
696
697
698
699
700
701

702
703
704

705
706
707
708
709
710
711
713
714
715
716
717
718
719


720



721
722
723
724
725
726
727
728







-
-
+
-
-
-
+







@implementation OFRunLoopConnectQueueItem
- (bool)handleObject: (id)object
{
	id exception = nil;
	int errNo;

	if ((errNo = [object of_socketError]) != 0)
		exception = [OFConnectionFailedException
		    exceptionWithHost: nil
		exception =
				 port: 0
			       socket: object
				errNo: errNo];
		    [_delegate of_connectionFailedExceptionForErrNo: errNo];

	if ([_delegate respondsToSelector:
	    @selector(of_socketDidConnect:exception:)]) {
		/*
		 * Make sure we only call the delegate once we removed the
		 * socket from the kernel event observer. This is necessary as
		 * otherwise we could try to connect to the next address and it
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
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







-
-
+









-
-
-
+
+
+
+
+
+
+
+
+
+
+
+







}
@end
# endif

@implementation OFRunLoopAcceptQueueItem
- (bool)handleObject: (id)object
{
	OFTCPSocket *acceptedSocket;
	id exception = nil;
	id acceptedSocket, exception = nil;

	@try {
		acceptedSocket = [object accept];
	} @catch (id e) {
		acceptedSocket = nil;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return _block(object, acceptedSocket, exception);
	else {
	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
			 didAcceptSocket: acceptedSocket
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
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







-
+

















-
+







	[_block release];

	[super dealloc];
}
# endif
@end

@implementation OFRunLoopUDPReceiveQueueItem
@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) {
		length = 0;
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return _block(object, _buffer, length, &address, exception);
		return _block(length, &address, exception);
	else {
# endif
		if (![_delegate respondsToSelector: @selector(
		    socket:didReceiveIntoBuffer:length:sender:exception:)])
			return false;

		return [_delegate socket: object
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
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







-
+















-
+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	[_block release];

	[super dealloc];
}
# endif
@end

@implementation OFRunLoopUDPSendQueueItem
@implementation OFRunLoopDatagramSendQueueItem
- (bool)handleObject: (id)object
{
	id exception = nil;
	OFData *newData, *oldData;

	@try {
		[object sendBuffer: _data.items
			    length: _data.count * _data.itemSize
			  receiver: &_receiver];
	} @catch (id e) {
		exception = e;
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL) {
		newData = _block(object, _data, &_receiver, exception);
		newData = _block(_data, &_receiver, exception);

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];

		return true;
	} else {
# endif
		if (![_delegate respondsToSelector:
		    @selector(socket:didSendData:receiver:exception:)])
			return false;

		newData = [_delegate socket: object
				didSendData: _data
				   receiver: &_receiver
				  exception: exception];

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];

		return true;
# ifdef OF_HAVE_BLOCKS
	}
# endif
}

- (void)dealloc
{
	[_data release];
# ifdef OF_HAVE_BLOCKS
	[_block release];
# endif

	[super dealloc];
}
@end

@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)
		return _block(length, exception);
	else {
# endif
		if (![_delegate respondsToSelector: @selector(
		    socket:didReceiveIntoBuffer:length:exception:)])
			return false;

		return [_delegate socket: object
		    didReceiveIntoBuffer: _buffer
				  length: length
			       exception: exception];
# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc
{
	[_block release];

	[super dealloc];
}
# endif
@end

@implementation OFRunLoopPacketSendQueueItem
- (bool)handleObject: (id)object
{
	id exception = nil;
	OFData *newData, *oldData;

	@try {
		[object sendBuffer: _data.items
			    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];

		return true;
	} else {
# endif
		if (![_delegate respondsToSelector:
		    @selector(socket:didSendData:exception:)])
			return false;

		newData = [_delegate socket: object
				didSendData: _data
				  exception: exception];

		if (newData == nil)
			return false;

		oldData = _data;
		_data = [newData copy];
		[oldData release];

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







-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+



-

-
-
+
+
+
+

-
+

-
+

-
+





+
+




-
-
-
-
+
+
+
+
+

-
+
-

-
+

-
+











-
-
+
+
-
-
+

-
+
-

-
+

-
+






-







	queueItem->_string = [string copy];
	queueItem->_encoding = encoding;

	QUEUE_ITEM
}

# if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
+ (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)stream
				  mode: (of_run_loop_mode_t)mode
			      delegate: (id <OFTCPSocketDelegate_Private>)
					    delegate
+ (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)
	NEW_WRITE(OFRunLoopConnectQueueItem, stream, 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
}
# endif

+ (void)of_addAsyncAcceptForTCPSocket: (OFTCPSocket *)stream
				 mode: (of_run_loop_mode_t)mode
+ (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_tcp_socket_async_accept_block_t)block
     block: (of_datagram_socket_async_send_data_block_t)block
# endif
			     delegate: (id <OFTCPSocketDelegate>)delegate
  delegate: (id <OFDatagramSocketDelegate>)delegate
{
	NEW_READ(OFRunLoopAcceptQueueItem, stream, mode)
	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_addAsyncReceiveForUDPSocket: (OFUDPSocket *)sock
				buffer: (void *)buffer
				length: (size_t)length
				  mode: (of_run_loop_mode_t)mode
+ (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_udp_socket_async_receive_block_t)
     block: (of_sequenced_packet_socket_async_receive_block_t)block
					    block
# endif
			      delegate: (id <OFUDPSocketDelegate>)delegate
  delegate: (id <OFSequencedPacketSocketDelegate>)delegate
{
	NEW_READ(OFRunLoopUDPReceiveQueueItem, sock, mode)
	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_addAsyncSendForUDPSocket: (OFUDPSocket *)sock
			       data: (OFData *)data
+ (void)of_addAsyncSendForSequencedPacketSocket: (OFSequencedPacketSocket *)sock
      data: (OFData *)data
			   receiver: (const of_socket_address_t *)receiver
			       mode: (of_run_loop_mode_t)mode
      mode: (of_run_loop_mode_t)mode
# ifdef OF_HAVE_BLOCKS
			      block: (of_udp_socket_async_send_data_block_t)
     block: (of_sequenced_packet_socket_async_send_data_block_t)block
					 block
# endif
			   delegate: (id <OFUDPSocketDelegate>)delegate
  delegate: (id <OFSequencedPacketSocketDelegate>)delegate
{
	NEW_WRITE(OFRunLoopUDPSendQueueItem, sock, mode)
	NEW_WRITE(OFRunLoopPacketSendQueueItem, sock, mode)

	queueItem->_delegate = [delegate retain];
# ifdef OF_HAVE_BLOCKS
	queueItem->_block = [block copy];
# endif
	queueItem->_data = [data copy];
	queueItem->_receiver = *receiver;

	QUEUE_ITEM
}
# undef NEW_READ
# undef NEW_WRITE
# undef QUEUE_ITEM

Added src/OFSCTPSocket.h version [946c9ca590].
































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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.
 */

#import "OFSequencedPacketSocket.h"
#import "OFRunLoop.h"

#import "socket.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(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

Added 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 [812276bc67] to [bd4ba48e95].

30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47







-
+










@interface OFSHA1Hash: OFObject <OFCryptoHash>
{
	OFSecureData *_iVarsData;
	struct of_sha1_hash_ivars {
		uint32_t state[5];
		uint64_t bits;
		union of_sha1_hash_buffer {
			uint8_t bytes[64];
			unsigned char bytes[64];
			uint32_t words[80];
		} buffer;
		size_t bufferLength;
	} *_iVars;
	bool _allowsSwappableMemory;
	bool _calculated;
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFSHA224Or256Hash.h from [9b410449c3] to [deb793f9c1].

31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50







-
+












@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 {
			uint8_t bytes[64];
			unsigned char bytes[64];
			uint32_t words[64];
		} buffer;
		size_t bufferLength;
	} *_iVars;
@private
	bool _allowsSwappableMemory;
	bool _calculated;
	OF_RESERVE_IVARS(4)
}
@end

OF_ASSUME_NONNULL_END

Modified src/OFSHA384Or512Hash.h from [b0f0d3732f] to [e5381155b9].

31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50







-
+












@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 {
			uint8_t bytes[128];
			unsigned char bytes[128];
			uint64_t words[80];
		} buffer;
		size_t bufferLength;
	} *_iVars;
@private
	bool _allowsSwappableMemory;
	bool _calculated;
	OF_RESERVE_IVARS(4)
}
@end

OF_ASSUME_NONNULL_END

Added src/OFSPXSocket.h version [b8282e6d38].










































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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 "OFSequencedPacketSocket.h"
#import "OFRunLoop.h"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/*! @file */

@class OFSPXSocket;
@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_spx_socket_async_connect_block_t)(id _Nullable exception);
#endif

/*!
 * @protocol OFSPXSocketDelegate OFSPXSocket.h ObjFW/OFSPXSocket.h
 *
 * A delegate for OFSPXSocket.
 */
@protocol OFSPXSocketDelegate <OFSequencedPacketSocketDelegate>
@optional
/*!
 * @brief A method which is called when a socket connected.
 *
 * @param socket The socket which connected
 * @param node The node the socket connected to
 * @param network The network of the node the socket connected to
 * @param port The port of the node to which the socket connected
 * @param exception An exception that occurred while connecting, or nil on
 *		    success
 */
-     (void)socket: (OFSPXSocket *)socket
  didConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
	   network: (uint32_t)network
	      port: (uint16_t)port
	 exception: (nullable id)exception;
@end

/*!
 * @class OFSPXSocket OFSPXSocket.h ObjFW/OFSPXSocket.h
 *
 * @brief A class which provides methods to create and use SPX sockets.
 *
 * @note If you want to use SPX in streaming mode instead of in message mode,
 *	 use @ref OFSPXStreamSocket instead.
 *
 * 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 OFSPXSocket: OFSequencedPacketSocket
{
	OF_RESERVE_IVARS(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 <OFSPXSocketDelegate> delegate;

/*!
 * @brief 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
 */
- (void)connectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
	      network: (uint32_t)network
		 port: (uint16_t)port;

/*!
 * @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
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port;

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

Added src/OFSPXSocket.m version [9940cead8b].





































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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

@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);
		_network = network;
		_port = port;
#ifdef OF_HAVE_BLOCKS
		_block = [block copy];
#endif
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_socket release];
#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
	if (_block != NULL)
		_block(exception);
	else {
#endif
		if ([delegate respondsToSelector:
		    @selector(socket:didConnectToNode:network:port:exception:)])
			[delegate     socket: _socket
			    didConnectToNode: _node
				     network: _network
					port: _port
				   exception: exception];
#ifdef OF_HAVE_BLOCKS
	}
#endif
}

- (id)of_connectionFailedExceptionForErrNo: (int)errNo
{
	return [OFConnectionFailedException exceptionWithNode: _node
						      network: _network
							 port: _port
						       socket: _socket
							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

Added src/OFSPXStreamSocket.h version [722b6414cf].
















































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 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"
#import "OFRunLoop.h"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/*! @file */

@class OFSPXStreamSocket;
@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_spx_stream_socket_async_connect_block_t)(
    id _Nullable exception);
#endif

/*!
 * @protocol OFSPXStreamSocketDelegate OFSPXStreamSocket.h \
 *	     ObjFW/OFSPXStreamSocket.h
 *
 * A delegate for OFSPXStreamSocket.
 */
@protocol OFSPXStreamSocketDelegate <OFStreamSocketDelegate>
@optional
/*!
 * @brief A method which is called when a socket connected.
 *
 * @param socket The socket which connected
 * @param node The node the socket connected to
 * @param network The network of the node the socket connected to
 * @param port The port of the node to which the socket connected
 * @param exception An exception that occurred while connecting, or nil on
 *		    success
 */
-     (void)socket: (OFSPXStreamSocket *)socket
  didConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
	   network: (uint32_t)network
	      port: (uint16_t)port
	 exception: (nullable id)exception;
@end

/*!
 * @class OFSPXStreamSocket OFSPXStreamSocket.h ObjFW/OFSPXStreamSocket.h
 *
 * @brief A class which provides methods to create and use SPX stream sockets.
 *
 * @note If you want to use SPX in message mode instead of in streaming mode,
 *	 use @ref OFSPXSocket instead.
 *
 * 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 OFSPXStreamSocket: OFStreamSocket
{
	OF_RESERVE_IVARS(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 <OFSPXStreamSocketDelegate> delegate;

/*!
 * @brief 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
 */
- (void)connectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
	      network: (uint32_t)network
		 port: (uint16_t)port;

/*!
 * @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
 */
- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
		   network: (uint32_t)network
		      port: (uint16_t)port;

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

Added src/OFSPXStreamSocket.m version [cb1a9316c7].









































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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

@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);
		_network = network;
		_port = port;
#ifdef OF_HAVE_BLOCKS
		_block = [block copy];
#endif
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_socket release];
#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;

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(exception);
	else {
#endif
		if ([delegate respondsToSelector:
		    @selector(socket:didConnectToNode:network:port:exception:)])
			[delegate     socket: _socket
			    didConnectToNode: _node
				     network: _network
					port: _port
				   exception: exception];
#ifdef OF_HAVE_BLOCKS
	}
#endif
}

- (id)of_connectionFailedExceptionForErrNo: (int)errNo
{
	return [OFConnectionFailedException exceptionWithNode: _node
						      network: _network
							 port: _port
						       socket: _socket
							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

Modified src/OFSecureData.h from [994e2ce1cc] to [1f5cbd3690].

23
24
25
26
27
28
29
30
31



32
33
34
35
36
37
38
23
24
25
26
27
28
29


30
31
32
33
34
35
36
37
38
39







-
-
+
+
+







 * @class OFSecureData OFSecureData.h ObjFW/OFSecureData.h
 *
 * @brief A class for storing arbitrary data in secure (non-swappable) memory,
 *	  securely wiping it when it gets deallocated.
 *
 * @warning Non-swappable memory might be unavailable, in which case this falls
 *	    back to swappable memory, but still wipes the data when it gets
 *	    deallocated. Check the @ref swappable property to see whether a
 *	    particular OFSecureData was allocated in swappable memory.
 *	    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;
}

Added src/OFSequencedPacketSocket+Private.h version [a194b9d55d].





























1
2
3
4
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 "OFSequencedPacketSocket.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFSequencedPacketSocket ()
#ifndef OF_WII
@property (readonly, nonatomic) int of_socketError;
#endif
@end

OF_ASSUME_NONNULL_END

Added src/OFSequencedPacketSocket.h version [92ebbc1deb].
























































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 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
 *
 * @brief A delegate for OFSequencedPacketSocket.
 */
@protocol OFSequencedPacketSocketDelegate <OFObject>
@optional
/*!
 * @brief This method is called when a packet has been received.
 *
 * @param socket The sequenced packet socket which received a packet
 * @param buffer The buffer the packet has been written to
 * @param length The length of the packet
 * @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: (OFSequencedPacketSocket *)socket
  didReceiveIntoBuffer: (void *)buffer
		length: (size_t)length
	     exception: (nullable id)exception;

/*!
 * @brief This method is called when a packet has been sent.
 *
 * @param socket The sequenced packet socket which sent a packet
 * @param data The data which was sent
 * @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: (OFSequencedPacketSocket *)socket
		didSendData: (OFData *)data
		  exception: (nullable id)exception;

/*!
 * @brief A method which is called when a socket accepted a connection.
 *
 * @param socket The socket which accepted the connection
 * @param acceptedSocket The socket which has been accepted
 * @param exception An exception that occurred while accepting, or nil on
 *		    success
 * @return A bool whether to accept the next incoming connection
 */
-    (bool)socket: (OFSequencedPacketSocket *)socket
  didAcceptSocket: (OFSequencedPacketSocket *)acceptedSocket
	exception: (nullable id)exception;
@end

/*!
 * @class OFSequencedPacketSocket OFSequencedPacketSocket.h \
 *	  ObjFW/OFSequencedPacketSocket.h
 *
 * @brief A base class for sequenced packet 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 OFSequencedPacketSocket: OFObject <OFCopying,
    OFReadyForReadingObserving, OFReadyForWritingObserving>
{
	of_socket_t _socket;
	bool _canBlock, _listening;
	of_socket_address_t _remoteAddress;
	id _Nullable _delegate;
	OF_RESERVE_IVARS(4)
}

/*!
 * @brief Whether the socket can block.
 *
 * By default, a socket can block.
 */
@property (nonatomic) bool canBlock;

/*!
 * @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.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFSequencedPacketSocketDelegate> delegate;

/*!
 * @brief Returns a new, autoreleased OFSequencedPacketSocket.
 *
 * @return A new, autoreleased OFSequencedPacketSocket
 */
+ (instancetype)socket;

/*!
 * @brief 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
 * @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.
 */
- (void)listenWithBacklog: (int)backlog;

/*!
 * @brief Listen on the socket.
 */
- (void)listen;

/*!
 * @brief Accept an incoming connection.
 *
 * @return An autoreleased sequenced packet socket for the accepted connection.
 */
- (instancetype)accept;

/*!
 * @brief Asynchronously accept an incoming connection.
 */
- (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;

/*!
 * @brief Closes the socket so that it can neither receive nor send any more
 *	  datagrams.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END

Added src/OFSequencedPacketSocket.m version [395d4dcdd4].






































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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
{
	return [self retain];
}

- (bool)canBlock
{
	return _canBlock;
}

- (void)setCanBlock: (bool)canBlock
{
#if defined(HAVE_FCNTL)
	int flags = fcntl(_socket, F_GETFL, 0);

	if (flags == -1)
		@throw [OFSetOptionFailedException exceptionWithObject: self
								 errNo: errno];

	if (canBlock)
		flags &= ~O_NONBLOCK;
	else
		flags |= O_NONBLOCK;

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

Modified src/OFStdIOStream.h from [3916cd29d8] to [4b738f8353].

20
21
22
23
24
25
26




27
28
29
30
31
32
33
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37







+
+
+
+








#ifdef OF_AMIGAOS
# include <dos/dos.h>
#endif

OF_ASSUME_NONNULL_BEGIN

/*! @file */

@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.
45
46
47
48
49
50
51





52
53
54
55
56
57
58
59
60
61
62
63
64


























































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







+
+
+
+
+













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







#else
	BPTR _handle;
	bool _closable;
#endif
	bool _atEndOfStream;
}

/*!
 * @brief Whether there is an underlying terminal.
 */
@property (readonly, nonatomic) bool hasTerminal;

/*!
 * @brief The number of columns, or -1 if there is no underlying terminal or
 *	  the number of columns could not be queried.
 */
@property (readonly, nonatomic) int columns;

/*!
 * @brief The number of rows, or -1 if there is no underlying terminal or the
 *	  number of rows could not be queried.
 */
@property (readonly, nonatomic) int rows;

- (instancetype)init OF_UNAVAILABLE;

/*!
 * @brief Sets the foreground color on the underlying terminal. Does nothing if
 *	  there is no underlying terminal or colors are unsupported.
 *
 * @param color The foreground color to set
 */
- (void)setForegroundColor: (OFColor *)color;

/*!
 * @brief Sets the background color on the underlying terminal. Does nothing if
 *	  there is no underlying terminal or colors are unsupported.
 *
 * @param color The background color to set
 */
- (void)setBackgroundColor: (OFColor *)color;

/*!
 * @brief Resets all attributes (color, bold, etc.). Does nothing if there is
 *	  no underlying terminal.
 */
- (void)reset;

/*!
 * @brief Clears the entire underlying terminal. Does nothing if there is no
 *	  underlying terminal.
 */
- (void)clear;

/*!
 * @brief Erases the entire current line on the underlying terminal. Does
 *	  nothing if there is no underlying terminal.
 */
- (void)eraseLine;

/*!
 * @brief Moves the cursor to the specified column in the current row. Does
 *	  nothing if there is no underlying terminal.
 *
 * @param column The column in the current row to move the cursor to
 */
- (void)setCursorColumn: (unsigned int)column;

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

80
81
82
83
84
85
86






87

88
89
90
91
92
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165







+
+
+
+
+
+
-
+





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 *, ...);
extern void of_log(OFConstantString *format, ...);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFStdIOStream.m from [730550a384] to [26a72da19c].

26
27
28
29
30
31
32

33
34
35
36
37
38
39

40
41
42
43
44
45
46
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48







+







+







#endif
#ifdef HAVE_SYS_TTYCOM_H
# include <sys/ttycom.h>
#endif

#import "OFStdIOStream.h"
#import "OFStdIOStream+Private.h"
#import "OFColor.h"
#import "OFDate.h"
#import "OFApplication.h"
#ifdef OF_WINDOWS
# include "OFWin32ConsoleStdIOStream.h"
#endif

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#ifdef OF_AMIGAOS
# include <proto/exec.h>
91
92
93
94
95
96
97









































98
99
100
101
102
103
104
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	va_end(arguments);

	[of_stderr writeFormat: @"[%@.%03d %@(%d)] %@\n", dateString,
				date.microsecond / 1000, me, getpid(), msg];

	objc_autoreleasePoolPop(pool);
}

#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
static int
colorToANSI(OFColor *color)
{
	if ([color isEqual: [OFColor black]])
		return 30;
	if ([color isEqual: [OFColor maroon]])
		return 31;
	if ([color isEqual: [OFColor green]])
		return 32;
	if ([color isEqual: [OFColor olive]])
		return 33;
	if ([color isEqual: [OFColor navy]])
		return 34;
	if ([color isEqual: [OFColor purple]])
		return 35;
	if ([color isEqual: [OFColor teal]])
		return 36;
	if ([color isEqual: [OFColor silver]])
		return 37;
	if ([color isEqual: [OFColor grey]])
		return 90;
	if ([color isEqual: [OFColor red]])
		return 91;
	if ([color isEqual: [OFColor lime]])
		return 92;
	if ([color isEqual: [OFColor yellow]])
		return 93;
	if ([color isEqual: [OFColor blue]])
		return 94;
	if ([color isEqual: [OFColor fuchsia]])
		return 95;
	if ([color isEqual: [OFColor aqua]])
		return 96;
	if ([color isEqual: [OFColor white]])
		return 97;

	return -1;
}
#endif

@implementation OFStdIOStream
#ifndef OF_WINDOWS
+ (void)load
{
	if (self != [OFStdIOStream class])
		return;
338
339
340
341
342
343
344









345
346
347
348
349
350
351
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403







+
+
+
+
+
+
+
+
+







{
}

- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}

- (bool)hasTerminal
{
#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	return isatty(_fd);
#else
	return false;
#endif
}

- (int)columns
{
#if defined(HAVE_SYS_IOCTL_H) && defined(TIOCGWINSZ) && !defined(OF_AMIGAOS)
	struct winsize ws;

	if (ioctl(_fd, TIOCGWINSZ, &ws) != 0)
366
367
368
369
370
371
372






































































































373
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

		return -1;

	return ws.ws_row;
#else
	return -1;
#endif
}

- (void)setForegroundColor: (OFColor *)color
{
#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	int code;

	if (!isatty(_fd))
		return;

	if ((code = colorToANSI(color)) == -1)
		return;

	[self writeFormat: @"\033[%um", code];
#endif
}

- (void)setBackgroundColor: (OFColor *)color
{
#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	int code;

	if (!isatty(_fd))
		return;

	if ((code = colorToANSI(color)) == -1)
		return;

	[self writeFormat: @"\033[%um", code + 10];
#endif
}

- (void)reset
{
#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	if (!isatty(_fd))
		return;

	[self writeString: @"\033[0m"];
#endif
}

- (void)clear
{
#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	if (!isatty(_fd))
		return;

	[self writeString: @"\033[2J"];
#endif
}

- (void)eraseLine
{
#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	if (!isatty(_fd))
		return;

	[self writeString: @"\033[2K"];
#endif
}

- (void)setCursorColumn: (unsigned int)column
{
#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	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];

#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	if (!isatty(_fd))
		return;

	[self writeFormat: @"\033[%u;%uH",
			   (unsigned)position.y + 1, (unsigned)position.x + 1];
#endif
}

- (void)setRelativeCursorPosition: (of_point_t)position
{
#if defined(HAVE_ISATTY) && !defined(OF_AMIGAOS)
	if (!isatty(_fd))
		return;

	if (position.x > 0)
		[self writeFormat: @"\033[%uC", (unsigned)position.x];
	else if (position.x < 0)
		[self writeFormat: @"\033[%uD", (unsigned)-position.x];

	if (position.y > 0)
		[self writeFormat: @"\033[%uB", (unsigned)position.y];
	else if (position.y < 0)
		[self writeFormat: @"\033[%uA", (unsigned)-position.y];
#endif
}
@end

Modified src/OFStream.h from [7421ae1300] to [cac7e1c78f].

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54


55
56
57
58
59
60
61
62
63
64
65
66
67
68


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

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

102
103
104
105
106
107
108
39
40
41
42
43
44
45


46
47
48
49
50


51
52
53
54
55
56
57

58
59
60
61
62
63


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


80
81
82
83
84
85
86
87
88
89

90
91
92
93
94


95
96
97
98
99
100
101
102







-
-





-
-
+
+





-






-
-
+
+














-
-
+









-





-
-
+







@class OFData;

#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_BLOCKS)
/*!
 * @brief A block which is called when data was read asynchronously from a
 *	  stream.
 *
 * @param stream The stream on which data was read
 * @param buffer A buffer with the data that has been read
 * @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)(OFStream *_Nonnull stream,
    void *_Nonnull buffer, size_t length, id _Nullable exception);
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 stream The stream on which a line was read
 * @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)(OFStream *_Nonnull stream,
    OFString *_Nullable line, id _Nullable exception);
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)(
    OFStream *_Nonnull stream, OFData *_Nonnull data,
    size_t bytesWritten, id _Nullable exception);
    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 encoding The encoding in which the string was written
 * @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)(
    OFStream *_Nonnull stream, OFString *_Nonnull string,
    of_string_encoding_t encoding, size_t bytesWritten, id _Nullable exception);
    OFString *_Nonnull string, size_t bytesWritten, id _Nullable exception);
#endif

/*!
 * @protocol OFStreamDelegate OFStream.h ObjFW/OFStream.h
 *
 * A delegate for OFStream.
 */
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
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







-
+

-
+





-
+











-
+







-
+

-
+


-
+





-
+











-
+
+
+







 *	 the methods that do the actual work. OFStream uses those for all other
 *	 methods and does all the caching and other stuff for you. If you
 *	 override these methods without the `lowlevel` prefix, you *will* break
 *	 caching and get broken results!
 */
@interface OFStream: OFObject <OFCopying>
{
	bool _blocking;
	bool _canBlock;
	id _Nullable _delegate;
#if !defined(OF_SEEKABLE_STREAM_M) && !defined(OF_TCP_SOCKET_M)
#ifndef OF_SEEKABLE_STREAM_M
@private
#endif
	char *_Nullable _readBuffer, *_Nullable _readBufferMemory;
	char *_Nullable _writeBuffer;
	size_t _readBufferLength, _writeBufferLength;
	bool _writeBuffered, _waitingForDelimiter;
	bool _buffersWrites, _waitingForDelimiter;
	OF_RESERVE_IVARS(4)
}

/*!
 * @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, getter=isWriteBuffered) bool writeBuffered;
@property (nonatomic, nonatomic) bool buffersWrites;

/*!
 * @brief Whether data is present in the internal read buffer.
 */
@property (readonly, nonatomic) bool hasDataInReadBuffer;

/*!
 * @brief Whether the stream is in blocking mode.
 * @brief Whether the stream can block.
 *
 * By default, a stream is in blocking mode.
 * By default, a stream can block.
 * On Win32, setting this currently only works for sockets!
 */
@property (nonatomic, getter=isBlocking) bool blocking;
@property (nonatomic) bool canBlock;

/*!
 * @brief The delegate for asynchronous operations on the stream.
 *
 * @note The delegate is retained for as long as asynchronous operations are
 *	 still outstanding.
 *	 still ongoing.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFStreamDelegate> delegate;

/*!
 * @brief 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
 * @ref readIntoBuffer:exactLength:. Note that a read can even return 0 bytes -
 * this does not necessarily mean that the stream ended, so you still need to
 * check @ref atEndOfStream.
 * check @ref atEndOfStream. Do not assume that the stream ended just because a
 * read returned 0 bytes - some streams do internal processing that has a
 * 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
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
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







-
+
+
+




















-
+
+
+







 * @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
 * @ref asyncReadIntoBuffer:exactLength:. Note that a read can even return 0
 * bytes - this does not necessarily mean that the stream ended, so you still
 * need to check @ref atEndOfStream.
 * need to check @ref atEndOfStream. Do not assume that the stream ended just
 * because a read returned 0 bytes - some streams do internal processing that
 * has a result of 0 bytes.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 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
 * @ref asyncReadIntoBuffer:exactLength:. Note that a read can even return 0
 * bytes - this does not necessarily mean that the stream ended, so you still
 * need to check @ref atEndOfStream.
 * need to check @ref atEndOfStream. Do not assume that the stream ended just
 * because a read returned 0 bytes - some streams do internal processing that
 * has a result of 0 bytes.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 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.
365
366
367
368
369
370
371
372



373
374
375
376
377
378
379
365
366
367
368
369
370
371

372
373
374
375
376
377
378
379
380
381







-
+
+
+







 * @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
 * @ref asyncReadIntoBuffer:exactLength:block:. Note that a read can even
 * return 0 bytes - this does not necessarily mean that the stream ended, so
 * you still need to check @ref atEndOfStream.
 * you still need to check @ref atEndOfStream. Do not assume that the stream
 * ended just because a read returned 0 bytes - some streams do internal
 * processing that has a result of 0 bytes.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 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.
392
393
394
395
396
397
398
399



400
401
402
403
404
405
406
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408
409
410







-
+
+
+







 * @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
 * @ref asyncReadIntoBuffer:exactLength:block:. Note that a read can even
 * return 0 bytes - this does not necessarily mean that the stream ended, so
 * you still need to check @ref atEndOfStream.
 * you still need to check @ref atEndOfStream. Do not assume that the stream
 * ended just because a read returned 0 bytes - some streams do internal
 * processing that has a result of 0 bytes.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 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.

Modified src/OFStream.m from [749b5fd798] to [dd10e2b21b].

59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77

78
79



80
81



82
83
84
85




86
87
88
89
90
91
92
93
94
95
96
97
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79


80
81
82
83
84
85
86
87




88
89
90
91





92
93
94
95
96
97
98







+












+
-
-
+
+
+


+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-







#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
{
	if (self == [OFStream class])
		signal(SIGPIPE, SIG_IGN);
}
#endif

- (instancetype)init
{
	self = [super init];
	if ([self isMemberOfClass: [OFStream class]]) {
		@try {

	@try {
		if (self.class == [OFStream class]) {
			[self doesNotRecognizeSelector: _cmd];
			abort();
		}

		_canBlock = true;
		} @catch (id e) {
			[self release];
			@throw e;
		}
	} @catch (id e) {
		[self release];
		@throw e;
	}
	}

	self = [super init];

	_blocking = true;

	return self;
}

- (bool)lowlevelIsAtEndOfStream
{
	OF_UNRECOGNIZED_SELECTOR
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
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







-
-
-
-
-
-
-
-
-
-
















-
+



-
+








- (OFString *)tryReadTillDelimiter: (OFString *)delimiter
{
	return [self tryReadTillDelimiter: delimiter
				 encoding: OF_STRING_ENCODING_UTF_8];
}

- (bool)isWriteBuffered
{
	return _writeBuffered;
}

- (void)setWriteBuffered: (bool)enable
{
	_writeBuffered = enable;
}

- (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 (!_writeBuffered) {
	if (!_buffersWrites) {
		size_t bytesWritten = [self lowlevelWriteBuffer: buffer
							 length: length];

		if (_blocking && bytesWritten < length)
		if (_canBlock && bytesWritten < length)
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: length
				   bytesWritten: bytesWritten
					  errNo: 0];

		return bytesWritten;
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
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







-
+

-
+


-
+

















-
+







}

- (bool)hasDataInReadBuffer
{
	return (_readBufferLength > 0);
}

- (bool)isBlocking
- (bool)canBlock
{
	return _blocking;
	return _canBlock;
}

- (void)setBlocking: (bool)enable
- (void)setCanBlock: (bool)canBlock
{
#if defined(HAVE_FCNTL) && !defined(OF_AMIGAOS)
	bool readImplemented = false, writeImplemented = false;

	@try {
		int readFlags;

		readFlags = fcntl(((id <OFReadyForReadingObserving>)self)
		    .fileDescriptorForReading, F_GETFL, 0);

		readImplemented = true;

		if (readFlags == -1)
			@throw [OFSetOptionFailedException
			    exceptionWithObject: self
					  errNo: errno];

		if (enable)
		if (canBlock)
			readFlags &= ~O_NONBLOCK;
		else
			readFlags |= O_NONBLOCK;

		if (fcntl(((id <OFReadyForReadingObserving>)self)
		    .fileDescriptorForReading, F_SETFL, readFlags) == -1)
			@throw [OFSetOptionFailedException
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
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







-
+
















-
+







		writeImplemented = true;

		if (writeFlags == -1)
			@throw [OFSetOptionFailedException
			    exceptionWithObject: self
					  errNo: errno];

		if (enable)
		if (canBlock)
			writeFlags &= ~O_NONBLOCK;
		else
			writeFlags |= O_NONBLOCK;

		if (fcntl(((id <OFReadyForWritingObserving>)self)
		    .fileDescriptorForWriting, F_SETFL, writeFlags) == -1)
			@throw [OFSetOptionFailedException
			    exceptionWithObject: self
					  errNo: errno];
	} @catch (OFNotImplementedException *e) {
	}

	if (!readImplemented && !writeImplemented)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	_blocking = enable;
	_canBlock = canBlock;
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (int)fileDescriptorForReading
{
1912
1913
1914
1915
1916
1917
1918
1919

1920
1921
1922
1923
1903
1904
1905
1906
1907
1908
1909

1910
1911
1912
1913
1914







-
+




	[self freeMemory: _readBufferMemory];
	_readBuffer = _readBufferMemory = NULL;
	_readBufferLength = 0;

	[self freeMemory: _writeBuffer];
	_writeBuffer = NULL;
	_writeBufferLength = 0;
	_writeBuffered = false;
	_buffersWrites = false;

	_waitingForDelimiter = false;
}
@end

Added src/OFStreamSocket+Private.h version [c1cf7136f2].





























1
2
3
4
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 "OFStreamSocket.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFStreamSocket ()
#ifndef OF_WII
@property (readonly, nonatomic) int of_socketError;
#endif
@end

OF_ASSUME_NONNULL_END

Modified src/OFStreamSocket.h from [2284f227d5] to [ded40b41a4].

17
18
19
20
21
22
23







































24
25
26
27
28
29
30
31
32
33


34
35
36
37





















38

39
40

41
42






















































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

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




#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.
 */
@protocol OFStreamSocketDelegate <OFStreamDelegate>
@optional
/*!
 * @brief A method which is called when a socket accepted a connection.
 *
 * @param socket The socket which accepted the connection
 * @param acceptedSocket The socket which has been accepted
 * @param exception An exception that occurred while accepting, or nil on
 *		    success
 * @return A bool whether to accept the next incoming connection
 */
-    (bool)socket: (OFStreamSocket *)socket
  didAcceptSocket: (OFStreamSocket *)acceptedSocket
	exception: (nullable id)exception;
@end

/*!
 * @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;
	bool _atEndOfStream, _listening;
	of_socket_address_t _remoteAddress;
	OF_RESERVE_IVARS(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.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFStreamSocketDelegate> delegate;

/*!
 * @brief Returns a new, autoreleased OFTCPSocket.
 * @brief Returns a new, autoreleased OFStreamSocket.
 *
 * @return A new, autoreleased OFTCPSocket
 * @return A new, autoreleased OFStreamSocket
 */
+ (instancetype)socket;

/*!
 * @brief Listen on the socket.
 *
 * @param backlog Maximum length for the queue of pending connections.
 */
- (void)listenWithBacklog: (int)backlog;

/*!
 * @brief Listen on the socket.
 */
- (void)listen;

/*!
 * @brief Accept an incoming connection.
 *
 * @return An autoreleased OFStreamSocket for the accepted connection.
 */
- (instancetype)accept;

/*!
 * @brief Asynchronously accept an incoming connection.
 */
- (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

Modified src/OFStreamSocket.m from [bc21bce60d] to [2df2daa037].

15
16
17
18
19
20
21

22
23
24
25



26

27


28
29
30
31
32
33
34
35
36
37



38
39
40
41
42
43
44
45
46
47
48
49
50
51



























52
53
54
55
56
57
58
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95







+




+
+
+

+

+
+










+
+
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 * 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;
69
70
71
72
73
74
75
76

77
78
79

80
81
82
83
84
85
86
106
107
108
109
110
111
112

113
114
115

116
117
118
119
120
121
122
123







-
+


-
+







#ifndef OF_WINDOWS
	if ((ret = recv(_socket, buffer, length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
#else
	if (length > UINT_MAX)
	if (length > INT_MAX)
		@throw [OFOutOfRangeException exception];

	if ((ret = recv(_socket, buffer, (unsigned int)length, 0)) < 0)
	if ((ret = recv(_socket, buffer, (int)length, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
#endif

	if (ret == 0)
121
122
123
124
125
126
127
128

129
130
131

132
133

134
135
136
137
138
139
140
141

142
143
144
145
146
147
148
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







-
+


-
+

-
+







-
+







				  errNo: of_socket_errno()];
#endif

	return (size_t)bytesWritten;
}

#if defined(OF_WINDOWS) || defined(OF_AMIGAOS)
- (void)setBlocking: (bool)enable
- (void)setCanBlock: (bool)canBlock
{
# ifdef OF_WINDOWS
	u_long v = enable;
	u_long v = canBlock;
# else
	char v = enable;
	char v = canBlock;
# endif

	if (ioctlsocket(_socket, FIONBIO, &v) == SOCKET_ERROR)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	_blocking = enable;
	_canBlock = canBlock;
}
#endif

- (int)fileDescriptorForReading
{
#ifndef OF_WINDOWS
	return _socket;
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
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








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
+
+







-
-
-
-
-
-
-
-


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

- (void)dealloc
{
	if (_socket != INVALID_SOCKET)
		[self close];

	[super dealloc];
}
@end

Modified src/OFString.h from [dae29d5404] to [18fc9bbd71].

10
11
12
13
14
15
16



17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40






41
42
43
44
45

46




47
48
49
50
51
52
53
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39




40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63







+
+
+








-
+











-
-
-
-
+
+
+
+
+
+





+

+
+
+
+







 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either 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_OF_STRING_H
#define OBJFW_OF_STRING_H

#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS
#endif
#ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS
#endif

#import "objfw-defs.h"
#include "objfw-defs.h"

#ifdef OF_HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#include <stdarg.h>
#include <stdint.h>
#ifdef OF_HAVE_INTTYPES_H
# include <inttypes.h>
#endif

#import "OFObject.h"
#import "OFSerialization.h"
#import "OFJSONRepresentation.h"
#import "OFMessagePackRepresentation.h"
#include "OFObject.h"
#ifdef __OBJC__
# import "OFSerialization.h"
# import "OFJSONRepresentation.h"
# import "OFMessagePackRepresentation.h"
#endif

OF_ASSUME_NONNULL_BEGIN

/*! @file */

#ifdef __OBJC__
@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;
106
107
108
109
110
111
112

113
114
115
116
117
118
119
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130







+







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

/*!
 * @class OFString OFString.h ObjFW/OFString.h
 *
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
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 decimal value of the string as an `intmax_t`.
 *
 * Leading and trailing whitespaces are ignored.
 *
 * If the string contains any non-number characters, an
 * @ref OFInvalidEncodingException is thrown.
 * @ref OFInvalidFormatException is thrown.
 *
 * If the number is too big to fit into an `intmax_t`, an
 * @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) intmax_t decimalValue;

/*!
 * @brief The hexadecimal value of the string as an `uintmax_t`.
 *
 * Leading and trailing whitespaces are ignored.
 *
 * If the string contains any non-number characters, an
 * @ref OFInvalidEncodingException is thrown.
 * @ref OFInvalidFormatException is thrown.
 *
 * If the number is too big to fit into an `uintmax_t`, an
 * @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) uintmax_t hexadecimalValue;

/*!
 * @brief The octal value of the string as an `uintmax_t`.
 *
 * Leading and trailing whitespaces are ignored.
 *
 * If the string contains any non-number characters, an
 * @ref OFInvalidEncodingException is thrown.
 * @ref OFInvalidFormatException is thrown.
 *
 * If the number is too big to fit into an `uintmax_t`, an
 * @ref OFOutOfRangeException is thrown.
 */
@property (readonly, nonatomic) uintmax_t octalValue;

/*!
 * @brief The float value of the string as a float.
 *
 * If the string contains any non-number characters, an
 * @ref OFInvalidEncodingException is thrown.
 * @ref OFInvalidFormatException is thrown.
 */
@property (readonly, nonatomic) float floatValue;

/*!
 * @brief The double value of the string as a double.
 *
 * If the string contains any non-number characters, an
 * OFInvalidEncodingException is thrown.
 * @ref OFInvalidFormatException is thrown.
 */
@property (readonly, nonatomic) double doubleValue;

/*!
 * @brief The string as an array of Unicode characters.
 *
 * The result is valid until the autorelease pool is released. If you want to
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
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







-
+










-
+
+
+
+
+
+
+
+
+







@property (readonly, nonatomic) OFString *stringByDeletingTrailingWhitespaces;

/*!
 * @brief The string with leading and trailing whitespaces deleted.
 */
@property (readonly, nonatomic) OFString *stringByDeletingEnclosingWhitespaces;

#ifdef OF_HAVE_UNICODE_TABLES
# ifdef OF_HAVE_UNICODE_TABLES
/*!
 * @brief The string in Unicode Normalization Form D (NFD).
 */
@property (readonly, nonatomic) OFString *decomposedStringWithCanonicalMapping;

/*!
 * @brief The string in Unicode Normalization Form KD (NFKD).
 */
@property (readonly, nonatomic)
    OFString *decomposedStringWithCompatibilityMapping;
#endif
# endif

# ifdef OF_WINDOWS
/*!
 * @brief The string with the Windows Environment Strings expanded.
 */
@property (readonly, nonatomic)
    OFString *stringByExpandingWindowsEnvironmentStrings;
# endif

/*!
 * @brief Creates a new OFString.
 *
 * @return A new, autoreleased OFString
 */
+ (instancetype)string;
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
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







-
+



















-
+

-
+







 * `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
# ifdef OF_HAVE_FILES
/*!
 * @brief Creates a new OFString with the contents of the specified UTF-8
 *	  encoded file.
 *
 * @param path The path to the file
 * @return A new autoreleased OFString
 */
+ (instancetype)stringWithContentsOfFile: (OFString *)path;

/*!
 * @brief Creates a new OFString with the contents of the specified file in the
 *	  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
# endif

#if defined(OF_HAVE_FILES) || defined(OF_HAVE_SOCKETS)
# 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
534
535
536
537
538
539
540
541

542
543
544
545
546
547
548
553
554
555
556
557
558
559

560
561
562
563
564
565
566
567







-
+







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







-
+



















-
+







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

#ifdef OF_HAVE_FILES
# ifdef OF_HAVE_FILES
/*!
 * @brief Initializes an already allocated OFString with the contents of the
 *	  specified file in the specified encoding.
 *
 * @param path The path to the file
 * @return An initialized OFString
 */
- (instancetype)initWithContentsOfFile: (OFString *)path;

/*!
 * @brief Initializes an already allocated OFString with the contents of the
 *	  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
# 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.
 *
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


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







-
+
















-
+

















-
+






-
+

+
















-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+















+
+
 * @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
# 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
# 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
# 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
# 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

#import "OFConstantString.h"
#import "OFMutableString.h"
#import "OFString+CryptoHashing.h"
#import "OFString+JSONValue.h"
#ifdef OF_HAVE_FILES
# import "OFString+PathAdditions.h"
#endif
#import "OFString+PropertyListValue.h"
#import "OFString+Serialization.h"
#import "OFString+URLEncoding.h"
#import "OFString+XMLEscaping.h"
#import "OFString+XMLUnescaping.h"
#include "OFConstantString.h"
#include "OFMutableString.h"
#ifdef __OBJC__
# import "OFString+CryptoHashing.h"
# import "OFString+JSONValue.h"
# ifdef OF_HAVE_FILES
#  import "OFString+PathAdditions.h"
# endif
# import "OFString+PropertyListValue.h"
# import "OFString+Serialization.h"
# import "OFString+URLEncoding.h"
# import "OFString+XMLEscaping.h"
# import "OFString+XMLUnescaping.h"
#endif

#if !defined(NSINTEGER_DEFINED) && !__has_feature(modules)
#if defined(__OBJC__) && !defined(NSINTEGER_DEFINED) && !__has_feature(modules)
/*
 * Very *ugly* hack required for string boxing literals to work.
 *
 * This hack is needed in order to work with `@class NSString` from Apple's
 * objc/NSString.h - which is included when using modules - as
 * @compatibility_alias does not work if @class has been used before.
 * For some reason, this makes Clang refer to OFString for string box literals
 * and not to NSString (which would result in a linker error, but would be the
 * correct behavior).
 *
 * TODO: Submit a patch for Clang that makes the boxing classes configurable!
 */
@interface NSString: OFString
@end
#endif

#endif

Modified src/OFString.m from [57dadde5e8] to [9bf92ef7e1].

37
38
39
40
41
42
43

44
45
46
47
48
49
50
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51







+







#import "OFDictionary.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
# import "OFFileManager.h"
#endif
#import "OFLocale.h"
#import "OFStream.h"
#import "OFSystemInfo.h"
#import "OFURL.h"
#import "OFURLHandler.h"
#import "OFUTF8String.h"
#import "OFUTF8String+Private.h"
#import "OFXMLElement.h"

#import "OFInitializationFailedException.h"
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201







-
+







	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 [OFInvalidEncodingException exception];
		@throw [OFInvalidArgumentException exception];

	objc_autoreleasePoolPop(pool);

	return encoding;
}

OFString *
2701
2702
2703
2704
2705
2706
2707






























2708
2709
2710
2711
2712
2713
2714
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	    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

Modified src/OFSystemInfo.h from [3dbc07c33c] to [0a359fe12b].

54
55
56
57
58
59
60



61
62
63
64
65
66
67
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70







+
+
+







@property (class, readonly, nonatomic) bool supportsAVX2;
@property (class, readonly, nonatomic) bool supportsAESNI;
@property (class, readonly, nonatomic) bool supportsSHAExtensions;
# endif
# if defined(OF_POWERPC) || defined(OF_POWERPC64) || defined(DOXYGEN)
@property (class, readonly, nonatomic) bool supportsAltiVec;
# endif
# ifdef OF_WINDOWS
@property (class, readonly, nonatomic, getter=isWindowsNT) bool windowsNT;
# endif
#endif

/*!
 * @brief Returns the size of a page.
 *
 * @return The size of a page
 */
282
283
284
285
286
287
288











289
290
291
292
293
294
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308







+
+
+
+
+
+
+
+
+
+
+






 *
 * @note This method is only available on PowerPC and PowerPC 64.
 *
 * @return Whether the CPU and OS support AltiVec
 */
+ (bool)supportsAltiVec;
#endif

#ifdef OF_WINDOWS
/*!
 * @brief Returns whether the application is running on Windows NT.
 *
 * @note This method is only available on Windows.
 *
 * @return Whether the application is running on Windows NT
 */
+ (bool)isWindowsNT;
#endif

+ (instancetype)alloc OF_UNAVAILABLE;
- (instancetype)init OF_UNAVAILABLE;
@end

OF_ASSUME_NONNULL_END

Modified src/OFSystemInfo.m from [7bac8e5add] to [0f92abda7c].

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







+
-
+


-
+



-
+



-
-
-
-
-
+
+
+
+
+

-
+









-
+


-
+







	}
# endif
#elif defined(OF_WINDOWS)
# ifdef OF_HAVE_FILES
	void *pool = objc_autoreleasePoolPush();

	@try {
		of_string_encoding_t encoding = [OFLocale encoding];
		wchar_t systemDir[PATH_MAX];
		char systemDir[PATH_MAX];
		UINT systemDirLen;
		OFString *systemDirString;
		const of_char16_t *path;
		const char *path;
		void *buffer;
		DWORD bufferLen;

		systemDirLen = GetSystemDirectoryW(systemDir, PATH_MAX);
		systemDirLen = GetSystemDirectoryA(systemDir, PATH_MAX);
		if (systemDirLen == 0)
			return;

		systemDirString = [OFString
		    stringWithUTF16String: systemDir
				   length: systemDirLen];
		path = [systemDirString stringByAppendingPathComponent:
		    @"kernel32.dll"].UTF16String;
		systemDirString = [OFString stringWithCString: systemDir
						     encoding: encoding
						       length: systemDirLen];
		path = [[systemDirString stringByAppendingPathComponent:
		    @"kernel32.dll"] cStringWithEncoding: encoding];

		if ((bufferLen = GetFileVersionInfoSizeW(path, NULL)) == 0)
		if ((bufferLen = GetFileVersionInfoSizeA(path, NULL)) == 0)
			return;
		if ((buffer = malloc(bufferLen)) == 0)
			return;

		@try {
			void *data;
			UINT dataLen;
			VS_FIXEDFILEINFO *info;

			if (!GetFileVersionInfoW(path, 0, bufferLen, buffer))
			if (!GetFileVersionInfoA(path, 0, bufferLen, buffer))
				return;

			if (!VerQueryValueW(buffer, L"\\", &data, &dataLen) ||
			if (!VerQueryValueA(buffer, "\\", &data, &dataLen) ||
			    dataLen < sizeof(info))
				return;

			info = (VS_FIXEDFILEINFO *)data;

			operatingSystemVersion = [[OFString alloc]
			    initWithFormat: @"%u.%u.%u",
660
661
662
663
664
665
666







667
668
669
670
671
672
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680







+
+
+
+
+
+
+






	    SYSTEMINFOTYPE_PPC_ALTIVEC, TAG_DONE) > 0)
		return supportsAltiVec;
# endif

	return false;
}
#endif

#ifdef OF_WINDOWS
+ (bool)isWindowsNT
{
	return !(GetVersion() & 0x80000000);
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}
@end

Deleted src/OFTCPSocket+Private.h version [04e97a23b2].

1
2
3
4
5
6
7
8
9
10
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 "OFTCPSocket.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFTCPSocket ()
#ifndef OF_WII
@property (readonly, nonatomic) int of_socketError;
#endif

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

OF_ASSUME_NONNULL_END

Modified src/OFTCPSocket.h from [e4fec95ad0] to [cedf99692f].

27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

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

130
131
132
133

134
135
136
137
138


139
140
141
142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
27
28
29
30
31
32
33

34
35
36

37














38
39
40
41
42
43
44

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













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


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












85
86

87
88
89
90

91
92
93
94
95

96
97
98
99
100

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

118
119
120
121
122
123
124
125







-



-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-







-
+














-
-
-
-
-
-
-
-
-
-
-
-
-












-
-













-
-
-
-
-
-
-
-
-
-
-
-


-
+



-
+




-
+
+



-
+
















-
+







@class OFTCPSocket;
@class OFString;

#ifdef OF_HAVE_BLOCKS
/*!
 * @brief A block which is called when the socket connected.
 *
 * @param socket The socket which connected
 * @param exception An exception which occurred while connecting the socket or
 *		    `nil` on success
 */
typedef void (^of_tcp_socket_async_connect_block_t)(OFTCPSocket *socket,
typedef void (^of_tcp_socket_async_connect_block_t)(id _Nullable exception);
    id _Nullable exception);

/*!
 * @brief A block which is called when the socket accepted a connection.
 *
 * @param socket The socket which accepted the 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_tcp_socket_async_accept_block_t)(OFTCPSocket *socket,
    OFTCPSocket *acceptedSocket, id _Nullable exception);
#endif

/*!
 * @protocol OFTCPSocketDelegate OFTCPSocket.h ObjFW/OFTCPSocket.h
 *
 * A delegate for OFTCPSocket.
 */
@protocol OFTCPSocketDelegate <OFStreamDelegate>
@protocol OFTCPSocketDelegate <OFStreamSocketDelegate>
@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: (OFTCPSocket *)socket
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (nullable id)exception;

/*!
 * @brief A method which is called when a socket accepted a connection.
 *
 * @param socket The socket which accepted the connection
 * @param acceptedSocket The socket which has been accepted
 * @param exception An exception that occurred while accepting, or nil on
 *		    success
 * @return A bool whether to accept the next incoming connection
 */
-    (bool)socket: (OFTCPSocket *)socket
  didAcceptSocket: (OFTCPSocket *)acceptedSocket
	exception: (nullable id)exception;
@end

/*!
 * @class OFTCPSocket OFTCPSocket.h ObjFW/OFTCPSocket.h
 *
 * @brief A class which provides methods to create and use TCP 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 OFTCPSocket: OFStreamSocket
{
	bool _listening;
	of_socket_address_t _remoteAddress;
	OFString *_Nullable _SOCKS5Host;
	uint16_t _SOCKS5Port;
#ifdef OF_WII
	uint16_t _port;
#endif
	OF_RESERVE_IVARS(4)
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, nullable, copy, nonatomic) OFString *SOCKS5Host;
@property (class, nonatomic) uint16_t SOCKS5Port;
#endif

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

#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
/*!
 * @brief Whether keep alives are enabled for the connection.
 * @brief Whether the socket sends keep alives for the connection.
 *
 * @warning This is not available on the Wii or Nintendo 3DS!
 */
@property (nonatomic, getter=isKeepAliveEnabled) bool keepAliveEnabled;
@property (nonatomic) bool sendsKeepAlives;
#endif

#ifndef OF_WII
/*!
 * @brief Whether TCP_NODELAY is enabled for the connection
 * @brief Whether sending segments can be delayed. Setting this to NO sets
 *        TCP_NODELAY on the socket.
 *
 * @warning This is not available on the Wii!
 */
@property (nonatomic, getter=isTCPNoDelayEnabled) bool TCPNoDelayEnabled;
@property (nonatomic) bool canDelaySendingSegments;
#endif

/*!
 * @brief The host to use as a SOCKS5 proxy.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *SOCKS5Host;

/*!
 * @brief The port to use on the SOCKS5 proxy.
 */
@property (nonatomic) uint16_t SOCKS5Port;

/*!
 * @brief The delegate for asynchronous operations on the socket.
 *
 * @note The delegate is retained for as long as asynchronous operations are
 *	 still outstanding.
 *	 still ongoing.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFTCPSocketDelegate> delegate;

/*!
 * @brief Sets the global SOCKS5 proxy host to use when creating a new socket
 *
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
211
212
213
214
215
216
217





















































218
219
220
221
222
223
224
225
226
227
228







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-











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

/*!
 * @brief Listen on the socket.
 *
 * @param backlog Maximum length for the queue of pending connections.
 */
- (void)listenWithBacklog: (int)backlog;

/*!
 * @brief Listen on the socket.
 */
- (void)listen;

/*!
 * @brief Accept an incoming connection.
 *
 * @return An autoreleased OFTCPSocket for the accepted connection.
 */
- (instancetype)accept;

/*!
 * @brief Asynchronously accept an incoming connection.
 */
- (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_tcp_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_tcp_socket_async_accept_block_t)block;
#endif
@end

#ifdef __cplusplus
extern "C" {
#endif
extern Class _Nullable of_tls_socket_class;
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFTCPSocket.m from [6288b956d6] to [dc32b6d4c9].

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




38
39
40
41
42


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
11
12
13
14
15
16
17

18
19
20
21

22
23
24
25
26
27
28
29
30
31




32
33
34
35
36
37
38


39
40
41

42
43

44



45
46


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

60













































61
62
63
64
65
66
67
68
































































































































































































































































































































































































































































































69
70
71
72
73
74
75







-




-










-
-
-
-
+
+
+
+



-
-
+
+

-


-

-
-
-


-
-













-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-








-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either 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_TCP_SOCKET_M
#define __NO_EXT_QNX

#include "config.h"

#include <assert.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 "OFTCPSocket+Private.h"
#import "OFDate.h"
#import "OFDNSResolver.h"
#import "OFData.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 "OFTimer.h"
#import "OFTCPSocketSOCKS5Connector.h"
#import "OFThread.h"

#import "OFAcceptFailedException.h"
#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFConnectionFailedException.h"
#import "OFGetOptionFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFListenFailedException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.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 OFTCPSocketAsyncConnectDelegate: OFObject <OFTCPSocketDelegate,
@interface OFTCPSocket () <OFIPSocketAsyncConnecting>
    OFTCPSocketDelegate_Private, OFDNSResolverHostDelegate>
{
	OFTCPSocket *_socket;
	OFString *_host;
	uint16_t _port;
	OFString *_SOCKS5Host;
	uint16_t _SOCKS5Port;
	id <OFTCPSocketDelegate> _delegate;
#ifdef OF_HAVE_BLOCKS
	of_tcp_socket_async_connect_block_t _block;
#endif
	id _exception;
	OFData *_socketAddresses;
	size_t _socketAddressesIndex;
	enum {
		SOCKS5_STATE_SEND_AUTHENTICATION = 1,
		SOCKS5_STATE_READ_VERSION,
		SOCKS5_STATE_SEND_REQUEST,
		SOCKS5_STATE_READ_RESPONSE,
		SOCKS5_STATE_READ_ADDRESS,
		SOCKS5_STATE_READ_ADDRESS_LENGTH,
	} _SOCKS5State;
	/* Longest read is domain name (max 255 bytes) + port */
	unsigned char _buffer[257];
	OFMutableData *_request;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		    SOCKS5Host: (OFString *)SOCKS5Host
		    SOCKS5Port: (uint16_t)SOCKS5Port
		      delegate: (id <OFTCPSocketDelegate>)delegate;
#ifdef OF_HAVE_BLOCKS
- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		    SOCKS5Host: (OFString *)SOCKS5Host
		    SOCKS5Port: (uint16_t)SOCKS5Port
			 block: (of_tcp_socket_async_connect_block_t)block;
#endif
- (void)didConnect;
- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
- (void)sendSOCKS5Request;
@end

@interface OFTCPSocketConnectDelegate: OFObject <OFTCPSocketDelegate>
{
@public
	bool _done;
	id _exception;
}
@end

@implementation OFTCPSocketAsyncConnectDelegate
- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		    SOCKS5Host: (OFString *)SOCKS5Host
		    SOCKS5Port: (uint16_t)SOCKS5Port
		      delegate: (id <OFTCPSocketDelegate>)delegate
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_host = [host copy];
		_port = port;
		_SOCKS5Host = [SOCKS5Host copy];
		_SOCKS5Port = SOCKS5Port;
		_delegate = [delegate retain];

		_socket.delegate = self;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

#ifdef OF_HAVE_BLOCKS
- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		    SOCKS5Host: (OFString *)SOCKS5Host
		    SOCKS5Port: (uint16_t)SOCKS5Port
			 block: (of_tcp_socket_async_connect_block_t)block
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_host = [host copy];
		_port = port;
		_SOCKS5Host = [SOCKS5Host copy];
		_SOCKS5Port = SOCKS5Port;
		_block = [block copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
#endif

- (void)dealloc
{
#ifdef OF_HAVE_BLOCKS
	if (_block == NULL)
#endif
		if (_socket.delegate == self)
			_socket.delegate = _delegate;

	[_socket release];
	[_host release];
	[_SOCKS5Host release];
	[_delegate release];
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif
	[_exception release];
	[_socketAddresses release];
	[_request release];

	[super dealloc];
}

- (void)didConnect
{
	if (_exception == nil)
		_socket.blocking = true;

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(_socket, _exception);
	else {
#endif
		_socket.delegate = _delegate;

		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: (OFTCPSocket *)sock
		  exception: (id)exception
{
	if (exception != nil) {
		/*
		 * self might be retained only by the pending async requests,
		 * which we're about to cancel.
		 */
		[[self retain] autorelease];

		[sock cancelAsyncRequests];
		[sock of_closeSocket];

		if (_socketAddressesIndex >= _socketAddresses.count) {
			_exception = [exception retain];
			[self didConnect];
		} else {
			/*
			 * We must not call it before returning, as otherwise
			 * the new socket would be removed from the queue upon
			 * return.
			 */
			OFRunLoop *runLoop = [OFRunLoop currentRunLoop];
			SEL selector =
			    @selector(tryNextAddressWithRunLoopMode:);
			OFTimer *timer = [OFTimer
			    timerWithTimeInterval: 0
					   target: self
					 selector: selector
					   object: runLoop.currentMode
					  repeats: false];
			[runLoop addTimer: timer
				  forMode: runLoop.currentMode];
		}

		return;
	}

	if (_SOCKS5Host != nil)
		[self sendSOCKS5Request];
	else
		[self didConnect];
}

- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
{
	of_socket_address_t address = *(const of_socket_address_t *)
	    [_socketAddresses itemAtIndex: _socketAddressesIndex++];
	int errNo;

	if (_SOCKS5Host != nil)
		of_socket_address_set_port(&address, _SOCKS5Port);
	else
		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];
			return;
		}

		[self tryNextAddressWithRunLoopMode: runLoopMode];
		return;
	}

#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
	/*
	 * On Wii and 3DS, connect() fails if non-blocking is enabled.
	 *
	 * Additionally, on Wii, there is no getsockopt(), so it would not be
	 * possible to get the error (or success) after connecting anyway.
	 *
	 * So for now, connecting is blocking on Wii and 3DS.
	 *
	 * FIXME: Use a different thread as a work around.
	 */
	_socket.blocking = true;
#else
	_socket.blocking = false;
#endif

	if (![_socket of_connectSocketToAddress: &address
					  errNo: &errNo]) {
#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
		if (errNo == EINPROGRESS) {
			[OFRunLoop of_addAsyncConnectForTCPSocket: _socket
							     mode: runLoopMode
							 delegate: self];
			return;
		} else {
#endif
			[_socket of_closeSocket];

			if (_socketAddressesIndex >= _socketAddresses.count) {
				_exception = [[OFConnectionFailedException
				    alloc] initWithHost: _host
						   port: _port
						 socket: _socket
						  errNo: errNo];
				[self didConnect];
				return;
			}

			[self tryNextAddressWithRunLoopMode: runLoopMode];
			return;
#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
		}
#endif
	}

#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
	_socket.blocking = false;
#endif

	[self didConnect];
}

- (void)resolver: (OFDNSResolver *)resolver
  didResolveHost: (OFString *)host
       addresses: (OFData *)addresses
       exception: (id)exception
{
	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return;
	}

	_socketAddresses = [addresses copy];

	[self tryNextAddressWithRunLoopMode:
	    [OFRunLoop currentRunLoop].currentMode];
}

- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
{
	OFString *host;
	uint16_t port;

	if (_SOCKS5Host != nil) {
		if (_host.UTF8StringLength > 255)
			@throw [OFOutOfRangeException exception];

		host = _SOCKS5Host;
		port = _SOCKS5Port;
	} else {
		host = _host;
		port = _port;
	}

	@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];
}

- (void)sendSOCKS5Request
{
	OFData *data = [OFData dataWithItems: "\x05\x01\x00"
				       count: 3];

	_SOCKS5State = 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 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 = SOCKS5_STATE_SEND_REQUEST;
		[_socket asyncWriteData: _request
			    runLoopMode: runLoopMode];
		return false;
	case SOCKS5_STATE_READ_RESPONSE:
		response = buffer;

		if (response[0] != 5 || response[2] != 0) {
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		if (response[1] != 0) {
			int errNo;

			switch (response[1]) {
			case 0x02:
				errNo = EPERM;
				break;
			case 0x03:
				errNo = ENETUNREACH;
				break;
			case 0x04:
				errNo = EHOSTUNREACH;
				break;
			case 0x05:
				errNo = ECONNREFUSED;
				break;
			case 0x06:
				errNo = ETIMEDOUT;
				break;
			case 0x07:
				errNo = EOPNOTSUPP;
				break;
			case 0x08:
				errNo = EAFNOSUPPORT;
				break;
			default:
#ifdef EPROTO
				errNo = EPROTO;
#else
				errNo = 0;
#endif
				break;
			}

			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: _socket
				   errNo: errNo];
			[self didConnect];
			return false;
		}

		/* Skip the rest of the response */
		switch (response[3]) {
		case 1: /* IPv4 */
			_SOCKS5State = SOCKS5_STATE_READ_ADDRESS;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 4 + 2
					 runLoopMode: runLoopMode];
			return false;
		case 3: /* Domain name */
			_SOCKS5State = SOCKS5_STATE_READ_ADDRESS_LENGTH;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 1
					 runLoopMode: runLoopMode];
			return false;
		case 4: /* IPv6 */
			_SOCKS5State = 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 SOCKS5_STATE_READ_ADDRESS:
		[self didConnect];
		return false;
	case SOCKS5_STATE_READ_ADDRESS_LENGTH:
		addressLength = buffer;

		_SOCKS5State = 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 SOCKS5_STATE_SEND_AUTHENTICATION:
		_SOCKS5State = SOCKS5_STATE_READ_VERSION;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 2
				 runLoopMode: runLoopMode];
		return nil;
	case SOCKS5_STATE_SEND_REQUEST:
		[_request release];
		_request = nil;

		_SOCKS5State = SOCKS5_STATE_READ_RESPONSE;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 4
				 runLoopMode: runLoopMode];
		return nil;
	default:
		assert(0);
		return nil;
	}
}
@end

@implementation OFTCPSocketConnectDelegate
- (void)dealloc
{
	[_exception release];

647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
113
114
115
116
117
118
119

120
121
122
123
124
125
126







-







}

- (instancetype)init
{
	self = [super init];

	@try {
		_socket = INVALID_SOCKET;
		_SOCKS5Host = [defaultSOCKS5Host copy];
		_SOCKS5Port = defaultSOCKS5Port;
	} @catch (id e) {
		[self release];
		@throw e;
	}

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







+















-
-
-
-
-
-
-
-
-
-
-
-
-
-




-
+








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

#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)connectToHost: (OFString *)host
		 port: (uint16_t)port
{
	void *pool = objc_autoreleasePoolPush();
	id <OFTCPSocketDelegate> delegate = [_delegate retain];
	id <OFTCPSocketDelegate> delegate = _delegate;
	OFTCPSocketConnectDelegate *connectDelegate =
	    [[[OFTCPSocketConnectDelegate alloc] init] autorelease];
	OFRunLoop *runLoop = [OFRunLoop currentRunLoop];

	self.delegate = connectDelegate;
	[self asyncConnectToHost: host
			    port: port
767
768
769
770
771
772
773

774


















775

776
777
778
779
780
781
782



783
784
785
786
787
788
789
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







+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+



-
-
-
-
+
+
+







}

- (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
			  delegate: _delegate
#ifdef OF_HAVE_BLOCKS
			     block: NULL
#endif
		    ] autorelease];
		host = _SOCKS5Host;
		port = _SOCKS5Port;
	} else
		delegate = _delegate;

	[[[[OFTCPSocketAsyncConnectDelegate alloc]
	[[[[OFIPSocketAsyncConnector alloc]
		  initWithSocket: self
			    host: host
			    port: port
		      SOCKS5Host: _SOCKS5Host
		      SOCKS5Port: _SOCKS5Port
			delegate: _delegate] autorelease]
	    startWithRunLoopMode: runLoopMode];
			delegate: delegate
			   block: NULL
	    ] autorelease] startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
797
798
799
800
801
802
803

804














805

806
807
808
809

810
811

812
813
814
815
816
817
818
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







+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+



-
+
-
-
+








- (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
			  delegate: nil
			     block: block] autorelease];
		host = _SOCKS5Host;
		port = _SOCKS5Port;
	}

	[[[[OFTCPSocketAsyncConnectDelegate alloc]
	[[[[OFIPSocketAsyncConnector alloc]
		  initWithSocket: self
			    host: host
			    port: port
		      SOCKS5Host: _SOCKS5Host
			delegate: delegate
		      SOCKS5Port: _SOCKS5Port
			   block: block] autorelease]
			   block: (delegate == nil ? block : NULL)] autorelease]
	    startWithRunLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}
#endif

- (uint16_t)bindToHost: (OFString *)host
844
845
846
847
848
849
850
851

852
853
854
855
856
857
858
328
329
330
331
332
333
334

335
336
337
338
339
340
341
342







-
+







	    SOCK_STREAM | SOCK_CLOEXEC, 0)) == INVALID_SOCKET)
		@throw [OFBindFailedException
		    exceptionWithHost: host
				 port: port
			       socket: self
				errNo: of_socket_errno()];

	_blocking = true;
	_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,
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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
+

-
+








-
+







-
+







-
+

-
+








-
+







-
+


-
+





-
-
-
-







	@throw [OFBindFailedException exceptionWithHost: host
						   port: port
						 socket: self
						  errNo: EADDRNOTAVAIL];
#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
{
	OFTCPSocket *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
	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_addAsyncAcceptForTCPSocket: self
					    mode: runLoopMode
# ifdef OF_HAVE_BLOCKS
					   block: NULL
# endif
					delegate: _delegate];
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncAcceptWithBlock: (of_tcp_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_tcp_socket_async_accept_block_t)block
{
	[OFRunLoop of_addAsyncAcceptForTCPSocket: 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;
}

- (bool)isListening
{
	return _listening;
}

#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS)
- (void)setKeepAliveEnabled: (bool)enabled
- (void)setSendsKeepAlives: (bool)sendsKeepAlives
{
	int v = enabled;
	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)isKeepAliveEnabled
- (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
		    exceptionWithStream: self
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	return v;
}
#endif

#ifndef OF_WII
- (void)setTCPNoDelayEnabled: (bool)enabled
- (void)setCanDelaySendingSegments: (bool)canDelaySendingSegments
{
	int v = enabled;
	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)isTCPNoDelayEnabled
- (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
		    exceptionWithStream: self
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	return v;
	return !v;
}
#endif

- (void)close
{
	_listening = false;

	memset(&_remoteAddress, 0, sizeof(_remoteAddress));

#ifdef OF_WII
	_port = 0;
#endif

	[super close];
}
@end

Added src/OFTCPSocketSOCKS5Connector.h version [1461d18ce8].




























































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

#import "OFTCPSocket.h"

OF_ASSUME_NONNULL_BEGIN

@class OFString;

@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

Added src/OFTCPSocketSOCKS5Connector.m version [a4bbcb1257].























































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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 "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];
		_port = port;
		_delegate = [delegate retain];
#ifdef OF_HAVE_BLOCKS
		_block = [block copy];
#endif

		_socket.delegate = self;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_socket.delegate == self)
		_socket.delegate = _delegate;

	[_socket release];
	[_host release];
	[_delegate release];
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif
	[_exception release];
	[_request release];

	[super dealloc];
}

- (void)didConnect
{
	_socket.delegate = _delegate;

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

-     (void)socket: (OFTCPSocket *)sock
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (id)exception
{
	OFData *data;

	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
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		if (response[1] != 0) {
			int errNo;

			switch (response[1]) {
			case 0x02:
				errNo = EPERM;
				break;
			case 0x03:
				errNo = ENETUNREACH;
				break;
			case 0x04:
				errNo = EHOSTUNREACH;
				break;
			case 0x05:
				errNo = ECONNREFUSED;
				break;
			case 0x06:
				errNo = ETIMEDOUT;
				break;
			case 0x07:
				errNo = EOPNOTSUPP;
				break;
			case 0x08:
				errNo = EAFNOSUPPORT;
				break;
			default:
#ifdef EPROTO
				errNo = EPROTO;
#else
				errNo = 0;
#endif
				break;
			}

			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: _socket
				   errNo: errNo];
			[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

Modified src/OFTLSSocket.h from [c867497861] to [ba822d3148].

77
78
79
80
81
82
83
84

85
86
87
88

89
90
91
92
93
94
95
96
77
78
79
80
81
82
83

84
85
86
87

88

89
90
91
92
93
94
95







-
+



-
+
-







 * @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 certificate verification is enabled.
 * @brief Whether certificates are verified.
 *
 * The default is enabled.
 */
@property (nonatomic, getter=isCertificateVerificationEnabled)
@property (nonatomic) bool verifiesCertificates;
    bool certificateVerificationEnabled;

/*!
 * @brief Initializes the TLS socket with the specified TCP socket as its
 *	  underlying socket.
 *
 * @param socket The TCP socket to use as underlying socket
 */

Modified src/OFTarArchive.m from [fe80dcdcf2] to [74012ab28d].

96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114

115
116
117
118

119
120
121
122
123
124
125
96
97
98
99
100
101
102



103

104
105
106
107
108
109
110

111
112
113
114

115
116
117
118
119
120
121
122







-
-
-
+
-







-
+



-
+







			_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) {
			union {
				char c[1024];
				uint32_t u32[1024 / sizeof(uint32_t)];
			uint32_t buffer[1024 / sizeof(uint32_t)];
			} buffer;
			bool empty = true;

			if (![_stream isKindOfClass: [OFSeekableStream class]])
				@throw [OFInvalidArgumentException exception];

			[(OFSeekableStream *)_stream seekToOffset: -1024
							   whence: SEEK_END];
			[_stream readIntoBuffer: buffer.c
			[_stream readIntoBuffer: buffer
				    exactLength: 1024];

			for (size_t i = 0; i < 1024 / sizeof(uint32_t); i++)
				if (buffer.u32[i] != 0)
				if (buffer[i] != 0)
					empty = false;

			if (!empty)
				@throw [OFInvalidFormatException exception];

			[(OFSeekableStream *)stream seekToOffset: -1024
							  whence: SEEK_END];
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
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







-
-
-
+
-

















-
+



-
+



-
+



-
+






-
+








	[super dealloc];
}

- (OFTarArchiveEntry *)nextEntry
{
	OFTarArchiveEntry *entry;
	union {
		unsigned char c[512];
		uint32_t u32[512 / sizeof(uint32_t)];
	uint32_t buffer[512 / sizeof(uint32_t)];
	} buffer;
	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.c
	[_stream readIntoBuffer: buffer
		    exactLength: 512];

	for (size_t i = 0; i < 512 / sizeof(uint32_t); i++)
		if (buffer.u32[i] != 0)
		if (buffer[i] != 0)
			empty = false;

	if (empty) {
		[_stream readIntoBuffer: buffer.c
		[_stream readIntoBuffer: buffer
			    exactLength: 512];

		for (size_t i = 0; i < 512 / sizeof(uint32_t); i++)
			if (buffer.u32[i] != 0)
			if (buffer[i] != 0)
				@throw [OFInvalidFormatException exception];

		return nil;
	}

	entry = [[[OFTarArchiveEntry alloc]
	    of_initWithHeader: buffer.c
	    of_initWithHeader: (unsigned char *)buffer
		     encoding: _encoding] autorelease];

	_lastReturnedStream = [[OFTarArchiveFileReadStream alloc]
	    of_initWithStream: _stream
			entry: entry];

	return entry;
500
501
502
503
504
505
506
507

508
509

510
511
512
513
514
515

516
517
518
519
520
521
522
523
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 (_toWrite > 0)
		@throw [OFTruncatedDataException exception];

	remainder = 512 - _entry.size % 512;

	if (remainder != 512) {
		bool wasWriteBuffered = _stream.writeBuffered;
		bool didBufferWrites = _stream.buffersWrites;

		[_stream setWriteBuffered: true];
		_stream.buffersWrites = true;

		while (remainder--)
			[_stream writeInt8: 0];

		[_stream flushWriteBuffer];
		_stream.writeBuffered = wasWriteBuffered;
		_stream.buffersWrites = didBufferWrites;
	}

	[_stream release];
	_stream = nil;

	[super close];
}
@end

Modified src/OFUDPSocket.h from [1854fff845] to [d9eb072d8d].

11
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
11
12
13
14
15
16
17

18


19


20
21



22
23

24






























25
26
27
28

29































30
31
32
33
34
35
36







-
+
-
-

-
-


-
-
-
+

-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-




-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 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 "OFDatagramSocket.h"
#import "OFKernelEventObserver.h"
#import "OFRunLoop.h"

#import "socket.h"

OF_ASSUME_NONNULL_BEGIN

/*! @file */

@class OFUDPSocket;
@class OFString;

#ifdef OF_HAVE_BLOCKS
/*!
 * @brief A block which is called when a packet has been received.
 *
 * @param socket The UDP socket which received a packet
 * @param buffer The buffer the packet has been written to
 * @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_udp_socket_async_receive_block_t)(
    OFUDPSocket *_Nonnull socket, void *_Nonnull buffer, 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 socket The UDP socket which sent a packet
 * @param data The data which was sent
 * @param receiver The receiver for the UDP 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_udp_socket_async_send_data_block_t)(
    OFUDPSocket *_Nonnull socket, OFData *_Nonnull data,
    const of_socket_address_t *_Nonnull receiver, id _Nullable exception);
#endif

/*!
 * @protocol OFUDPSocketDelegate OFUDPSocket.h ObjFW/OFUDPSocket.h
 *
 * @brief A delegate for OFUDPSocket.
 */
@protocol OFUDPSocketDelegate <OFObject>
@protocol OFUDPSocketDelegate <OFDatagramSocketDelegate>
@optional
/*!
 * @brief This method is called when a packet has been received.
 *
 * @param socket The UDP socket which received a packet
 * @param buffer The buffer the packet has been written to
 * @param length The length of the packet
 * @param sender The address of the sender of the packet
 * @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: (OFUDPSocket *)socket
  didReceiveIntoBuffer: (void *)buffer
		length: (size_t)length
		sender: (const of_socket_address_t *_Nonnull)sender
	     exception: (nullable id)exception;

/*!
 * @brief This which is called when a packet has been sent.
 *
 * @param socket The UDP socket which sent a packet
 * @param data The data which was sent
 * @param receiver The receiver for the UDP 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: (OFUDPSocket *)socket
		didSendData: (OFData *)data
		   receiver: (const of_socket_address_t *_Nonnull)receiver
		  exception: (nullable id)exception;
@end

/*!
 * @class OFUDPSocket OFUDPSocket.h ObjFW/OFUDPSocket.h
 *
 * @brief A class which provides methods to create and use UDP sockets.
 *
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

143
144
145
146
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
44
45
46
47
48
49
50

51

52

53
54
55


56
57
58







59
60
61
62

63
64
65
66
67







68
69
70
71
72
73
74
75
76
77
78
































































































































































79
80
81







-
+
-

-



-
-



-
-
-
-
-
-
-




-
+




-
-
-
-
-
-
-











-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



 * @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 OFUDPSocket: OFObject <OFCopying, OFReadyForReadingObserving,
@interface OFUDPSocket: OFDatagramSocket
    OFReadyForWritingObserving>
{
	of_socket_t _socket;
#ifdef OF_WII
	uint16_t _port;
#endif
	bool _blocking;
	id _Nullable _delegate;
	OF_RESERVE_IVARS(4)
}

/*!
 * @brief Whether the socket is in blocking mode.
 *
 * By default, a socket is in blocking mode.
 */
@property (nonatomic, getter=isBlocking) bool blocking;

/*!
 * @brief The delegate for asynchronous operations on the socket.
 *
 * @note The delegate is retained for as long as asynchronous operations are
 *	 still outstanding.
 *	 still ongoing.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFUDPSocketDelegate> delegate;

/*!
 * @brief Returns a new, autoreleased OFUDPSocket.
 *
 * @return A new, autoreleased OFUDPSocket
 */
+ (instancetype)socket;

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

/*!
 * @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_udp_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_udp_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_udp_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_udp_socket_async_send_data_block_t)block;
#endif

/*!
 * @brief Cancels all pending asynchronous requests on the socket.
 */
- (void)cancelAsyncRequests;

/*!
 * @brief Closes the socket so that it can neither receive nor send any more
 *	  datagrams.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END

Modified src/OFUDPSocket.m from [c8408c9960] to [851b1788ef].

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
13
14
15
16
17
18
19

20

21
22
23
24
25
26
27
28
29


30
31
32
33








34
35
36
37
38

39












































































40
41
42
43
44
45
46







-

-









-
-




-
-
-
-
-
-
-
-





-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







 * Public License, either version 2 or 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>
#include <string.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#import "OFUDPSocket.h"
#import "OFUDPSocket+Private.h"
#import "OFDNSResolver.h"
#import "OFData.h"
#import "OFRunLoop+Private.h"
#import "OFRunLoop.h"
#import "OFThread.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindFailedException.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFSetOptionFailedException.h"
#import "OFWriteFailedException.h"

#import "socket.h"
#import "socket_helpers.h"

@implementation OFUDPSocket
@synthesize delegate = _delegate;
@dynamic delegate;

+ (void)initialize
{
	if (self != [OFUDPSocket class])
		return;

	if (!of_socket_init())
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

+ (instancetype)socket
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	self = [super init];

	_socket = INVALID_SOCKET;
	_blocking = true;

	return self;
}

- (void)dealloc
{
	if (_socket != INVALID_SOCKET)
		[self close];

	[super dealloc];
}

- (id)copy
{
	return [self retain];
}

- (bool)isBlocking
{
	return _blocking;
}

- (void)setBlocking: (bool)enable
{
#if defined(HAVE_FCNTL)
	int flags = fcntl(_socket, F_GETFL, 0);

	if (flags == -1)
		@throw [OFSetOptionFailedException exceptionWithObject: self
								 errNo: errno];

	if (enable)
		flags &= ~O_NONBLOCK;
	else
		flags |= O_NONBLOCK;

	if (fcntl(_socket, F_SETFL, flags) == -1)
		@throw [OFSetOptionFailedException exceptionWithObject: self
								 errNo: errno];

	_blocking = enable;
#elif defined(OF_WINDOWS)
	u_long v = enable;

	if (ioctlsocket(_socket, FIONBIO, &v) == SOCKET_ERROR)
		@throw [OFSetOptionFailedException
		    exceptionWithObject: self
				  errNo: of_socket_errno()];

	_blocking = enable;
#else
	OF_UNRECOGNIZED_SELECTOR
#endif
}

- (uint16_t)of_bindToAddress: (of_socket_address_t *)address
		   extraType: (int)extraType
{
	void *pool = objc_autoreleasePoolPush();
	OFString *host;
	uint16_t port;
142
143
144
145
146
147
148
149

150
151

152

153

154
155
156
157
158
159
160
54
55
56
57
58
59
60

61
62
63
64

65
66
67
68
69
70
71
72
73
74







-
+


+
-
+

+







		@throw [OFBindFailedException
		    exceptionWithHost: host
				 port: port
			       socket: self
				errNo: of_socket_errno()];
	}

	_blocking = true;
	_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)
	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) {
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
189
190
191
192
193
194
195





































































































































































































































196







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

	port = [self of_bindToAddress: &address
			    extraType: 0];

	objc_autoreleasePoolPop(pool);

	return port;
}

- (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
	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_addAsyncReceiveForUDPSocket: 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_udp_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_udp_socket_async_receive_block_t)block
{
	[OFRunLoop of_addAsyncReceiveForUDPSocket: 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_addAsyncSendForUDPSocket: 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_udp_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_udp_socket_async_send_data_block_t)block
{
	[OFRunLoop of_addAsyncSendForUDPSocket: 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

Modified src/OFURL.m from [0cca3e3685] to [71131960ae].

30
31
32
33
34
35
36

















37
38
39
40
41
42
43
44


45
46

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
























61

62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59


60
61

62
63














64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95

96

97
98
99
100
101
102
103







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
+
+
-

+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







-
+
-







# 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
@end

@interface OFURLPathAllowedCharacterSet: OFURLAllowedCharacterSetBase
@end

@interface OFURLQueryOrFragmentAllowedCharacterSet: OFURLAllowedCharacterSetBase
@end

static OFCharacterSet *URLAllowedCharacterSet = nil;
static OFCharacterSet *URLSchemeAllowedCharacterSet = nil;
static OFCharacterSet *URLPathAllowedCharacterSet = nil;
static OFCharacterSet *URLQueryOrFragmentAllowedCharacterSet = nil;

@interface OFURLAllowedCharacterSetBase: OFCharacterSet
- (instancetype)of_init OF_METHOD_FAMILY(init);
static of_once_t URLAllowedCharacterSetOnce = OF_ONCE_INIT;
static of_once_t URLQueryOrFragmentAllowedCharacterSetOnce = OF_ONCE_INIT;
@end

static void
@interface OFURLAllowedCharacterSet: OFURLAllowedCharacterSetBase
+ (OFCharacterSet *)URLAllowedCharacterSet;
@end

@interface OFURLSchemeAllowedCharacterSet: OFURLAllowedCharacterSetBase
+ (OFCharacterSet *)URLSchemeAllowedCharacterSet;
@end

@interface OFURLPathAllowedCharacterSet: OFURLAllowedCharacterSetBase
+ (OFCharacterSet *)URLPathAllowedCharacterSet;
@end

@interface OFURLQueryOrFragmentAllowedCharacterSet: OFURLAllowedCharacterSetBase
+ (OFCharacterSet *)URLQueryOrFragmentAllowedCharacterSet;
initURLAllowedCharacterSet(void)
{
	URLAllowedCharacterSet = [[OFURLAllowedCharacterSet alloc] init];
}

static void
initURLSchemeAllowedCharacterSet(void)
{
	URLSchemeAllowedCharacterSet =
	    [[OFURLSchemeAllowedCharacterSet alloc] init];
}

static void
initURLPathAllowedCharacterSet(void)
{
	URLPathAllowedCharacterSet =
	    [[OFURLPathAllowedCharacterSet alloc] init];
}

static void
initURLQueryOrFragmentAllowedCharacterSet(void)
{
	URLQueryOrFragmentAllowedCharacterSet =
	    [[OFURLQueryOrFragmentAllowedCharacterSet alloc] init];
@end
}

@interface OFInvertedCharacterSetWithoutPercent: OFCharacterSet
{
	OFCharacterSet *_characterSet;
	bool (*_characterIsMember)(id, SEL, of_unichar_t);
}

- (instancetype)of_initWithCharacterSet: (OFCharacterSet *)characterSet
- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet;
    OF_METHOD_FAMILY(init);
@end

bool
of_url_is_ipv6_host(OFString *host)
{
	const char *UTF8String = host.UTF8String;
	bool hasColon = false;
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
114
115
116
117
118
119
120










121
122
123
124
125
126
127







-
-
-
-
-
-
-
-
-
-







		UTF8String++;
	}

	return hasColon;
}

@implementation OFURLAllowedCharacterSetBase
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_init
{
	return [super init];
}

- (instancetype)autorelease
{
	return self;
}

- (instancetype)retain
{
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
135
136
137
138
139
140
141













142
143
144
145
146
147
148







-
-
-
-
-
-
-
-
-
-
-
-
-







- (unsigned int)retainCount
{
	return OF_RETAIN_COUNT_MAX;
}
@end

@implementation OFURLAllowedCharacterSet
+ (void)initialize
{
	if (self != [OFURLAllowedCharacterSet class])
		return;

	URLAllowedCharacterSet = [[OFURLAllowedCharacterSet alloc] of_init];
}

+ (OFCharacterSet *)URLAllowedCharacterSet
{
	return URLAllowedCharacterSet;
}

- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '-':
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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-

















-
-
-
-
-
-
-
-
-
-
-
-
-
-







	default:
		return false;
	}
}
@end

@implementation OFURLSchemeAllowedCharacterSet
+ (void)initialize
{
	if (self != [OFURLSchemeAllowedCharacterSet class])
		return;

	URLSchemeAllowedCharacterSet =
	    [[OFURLSchemeAllowedCharacterSet alloc] of_init];
}

+ (OFCharacterSet *)URLSchemeAllowedCharacterSet
{
	return URLSchemeAllowedCharacterSet;
}

- (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
+ (void)initialize
{
	if (self != [OFURLPathAllowedCharacterSet class])
		return;

	URLPathAllowedCharacterSet =
	    [[OFURLPathAllowedCharacterSet alloc] of_init];
}

+ (OFCharacterSet *)URLPathAllowedCharacterSet
{
	return URLPathAllowedCharacterSet;
}

- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '-':
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
213
214
215
216
217
218
219














220
221
222
223
224
225
226







-
-
-
-
-
-
-
-
-
-
-
-
-
-







	default:
		return false;
	}
}
@end

@implementation OFURLQueryOrFragmentAllowedCharacterSet
+ (void)initialize
{
	if (self != [OFURLQueryOrFragmentAllowedCharacterSet class])
		return;

	URLQueryOrFragmentAllowedCharacterSet =
	    [[OFURLQueryOrFragmentAllowedCharacterSet alloc] of_init];
}

+ (OFCharacterSet *)URLQueryOrFragmentAllowedCharacterSet
{
	return URLQueryOrFragmentAllowedCharacterSet;
}

- (bool)characterIsMember: (of_unichar_t)character
{
	if (character < CHAR_MAX && of_ascii_isalnum(character))
		return true;

	switch (character) {
	case '-':
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
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







-
-
-
-
-
-
+



+
-
-
-
+
+
+
+
+
+
+
+







	default:
		return false;
	}
}
@end

@implementation OFInvertedCharacterSetWithoutPercent
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)of_initWithCharacterSet: (OFCharacterSet *)characterSet
- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet
{
	self = [super init];

	@try {
	_characterSet = [characterSet retain];
	_characterIsMember = (bool (*)(id, SEL, of_unichar_t))
	    [_characterSet methodForSelector: @selector(characterIsMember:)];
		_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];
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
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







-
+










+
+
+
-
+




+
+
-
+




+
+
-
+




+
+
-
+




+
+
+
-
+




+
-
-
+
+
+




+
-
-
+
+
+








void
of_url_verify_escaped(OFString *string, OFCharacterSet *characterSet)
{
	void *pool = objc_autoreleasePoolPush();

	characterSet = [[[OFInvertedCharacterSetWithoutPercent alloc]
	    of_initWithCharacterSet: characterSet] autorelease];
	    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 [OFURLSchemeAllowedCharacterSet URLSchemeAllowedCharacterSet];
	return URLSchemeAllowedCharacterSet;
}

+ (OFCharacterSet *)URLHostAllowedCharacterSet
{
	of_once(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return [OFURLAllowedCharacterSet URLAllowedCharacterSet];
	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLUserAllowedCharacterSet
{
	of_once(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return [OFURLAllowedCharacterSet URLAllowedCharacterSet];
	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLPasswordAllowedCharacterSet
{
	of_once(&URLAllowedCharacterSetOnce, initURLAllowedCharacterSet);

	return [OFURLAllowedCharacterSet URLAllowedCharacterSet];
	return URLAllowedCharacterSet;
}

+ (OFCharacterSet *)URLPathAllowedCharacterSet
{
	static of_once_t onceControl = OF_ONCE_INIT;
	of_once(&onceControl, initURLPathAllowedCharacterSet);

	return [OFURLPathAllowedCharacterSet URLPathAllowedCharacterSet];
	return URLPathAllowedCharacterSet;
}

+ (OFCharacterSet *)URLQueryAllowedCharacterSet
{
	of_once(&URLQueryOrFragmentAllowedCharacterSetOnce,
	return [OFURLQueryOrFragmentAllowedCharacterSet
	    URLQueryOrFragmentAllowedCharacterSet];
	    initURLQueryOrFragmentAllowedCharacterSet);

	return URLQueryOrFragmentAllowedCharacterSet;
}

+ (OFCharacterSet *)URLFragmentAllowedCharacterSet
{
	of_once(&URLQueryOrFragmentAllowedCharacterSetOnce,
	return [OFURLQueryOrFragmentAllowedCharacterSet
	    URLQueryOrFragmentAllowedCharacterSet];
	    initURLQueryOrFragmentAllowedCharacterSet);

	return URLQueryOrFragmentAllowedCharacterSet;
}
@end

@implementation OFURL
+ (instancetype)URL
{
	return [[[self alloc] init] autorelease];

Modified src/OFWin32ConsoleStdIOStream.h from [e3e1fc8637] to [77f92f2723].

20
21
22
23
24
25
26

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







+







#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

Modified src/OFWin32ConsoleStdIOStream.m from [f6d82fa9cc] to [959c780c29].

44
45
46
47
48
49
50

51
52
53

54
55
56
57
58
59
60
61



















62
63
64
65
66
67
68
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89







+



+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







#include "config.h"

#include <assert.h>
#include <errno.h>
#include <io.h>

#import "OFWin32ConsoleStdIOStream.h"
#import "OFColor.h"
#import "OFData.h"
#import "OFStdIOStream+Private.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#include <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])
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95



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

113
114
115
116
117
118
119
120
121

122
123
124
125
126
127






























128
129
130
131
132
133
134
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
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







+








+
+
+
















-
+









+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








- (instancetype)of_initWithFileDescriptor: (int)fd
{
	self = [super of_initWithFileDescriptor: fd];

	@try {
		DWORD mode;
		CONSOLE_SCREEN_BUFFER_INFO csbi;

		_handle = (HANDLE)_get_osfhandle(fd);
		if (_handle == INVALID_HANDLE_VALUE)
			@throw [OFInvalidArgumentException exception];

		/* Not a console: Treat it as a regular OFStdIOStream */
		if (!GetConsoleMode(_handle, &mode))
			object_setClass(self, [OFStdIOStream class]);

		if (GetConsoleScreenBufferInfo(_handle, &csbi))
			_attributes = csbi.wAttributes;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer_
			  length: (size_t)length
{
	void *pool = objc_autoreleasePoolPush();
	char *buffer = buffer_;
	of_char16_t *UTF16;
	size_t j = 0;

	if (length > sizeof(UINT32_MAX))
	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];
			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
					requestedLength: length
						  errNo: EIO];

			encoding = codepageToEncoding(GetConsoleCP());
			string = [OFString stringWithCString: (char *)UTF16
						    encoding: encoding
						      length: UTF16Len];
			stringLen = string.UTF16StringLength;

			if (stringLen > length)
				@throw [OFOutOfRangeException exception];

			UTF16Len = (DWORD)stringLen;
			memcpy(UTF16, string.UTF16String, stringLen);
		}

		if (UTF16Len > 0 && _incompleteUTF16Surrogate != 0) {
			of_unichar_t c =
			    (((_incompleteUTF16Surrogate & 0x3FF) << 10) |
			    (UTF16[0] & 0x3FF)) + 0x10000;
			char UTF8[4];
			size_t UTF8Len;
267
268
269
270
271
272
273

274
275
276
277
278
279
280































281
282
283
284
285
286
287
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







+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







				UTF16Len = 2;
			} else {
				UTF16[0] = c;
				UTF16Len = 1;
			}
		}

		if ([OFSystemInfo isWindowsNT]) {
		if (!WriteConsoleW(_handle, UTF16, UTF16Len, &bytesWritten,
		    NULL))
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: UTF16Len * 2
				   bytesWritten: 0
					  errNo: EIO];
			if (!WriteConsoleW(_handle, UTF16, UTF16Len,
			    &bytesWritten, NULL))
				@throw [OFWriteFailedException
				    exceptionWithObject: self
					requestedLength: UTF16Len * 2
					   bytesWritten: bytesWritten * 2
						  errNo: EIO];
		} else {
			void *pool = objc_autoreleasePoolPush();
			OFString *string = [OFString
			    stringWithUTF16String: UTF16
					   length: UTF16Len];
			of_string_encoding_t encoding =
			    codepageToEncoding(GetConsoleOutputCP());
			size_t nativeLen = [string
			    cStringLengthWithEncoding: encoding];

			if (nativeLen > UINT32_MAX)
				@throw [OFOutOfRangeException exception];

			if (!WriteConsoleA(_handle,
			    [string cStringWithEncoding: encoding],
			    (DWORD)nativeLen, &bytesWritten, NULL))
				@throw [OFWriteFailedException
				    exceptionWithObject: self
					requestedLength: nativeLen
					   bytesWritten: bytesWritten
						  errNo: EIO];

			objc_autoreleasePoolPop(pool);
		}

		if (bytesWritten != UTF16Len)
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: UTF16Len * 2
				   bytesWritten: bytesWritten * 2
					  errNo: 0];
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
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







+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


			i += UTF8Len;
		}

		if (j > UINT32_MAX)
			@throw [OFOutOfRangeException exception];

		if ([OFSystemInfo isWindowsNT]) {
		if (!WriteConsoleW(_handle, tmp, (DWORD)j, &bytesWritten, NULL))
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: j * 2
				   bytesWritten: 0
					  errNo: EIO];
			if (!WriteConsoleW(_handle, tmp, (DWORD)j,
			    &bytesWritten, NULL))
				@throw [OFWriteFailedException
				    exceptionWithObject: self
					requestedLength: j * 2
					   bytesWritten: bytesWritten * 2
						  errNo: EIO];
		} else {
			void *pool = objc_autoreleasePoolPush();
			OFString *string = [OFString stringWithUTF16String: tmp
								    length: j];
			of_string_encoding_t encoding =
			    codepageToEncoding(GetConsoleOutputCP());
			size_t nativeLen = [string
			    cStringLengthWithEncoding: encoding];

			if (nativeLen > UINT32_MAX)
				@throw [OFOutOfRangeException exception];

			if (!WriteConsoleA(_handle,
			    [string cStringWithEncoding: encoding],
			    (DWORD)nativeLen, &bytesWritten, NULL))
				@throw [OFWriteFailedException
				    exceptionWithObject: self
					requestedLength: nativeLen
					   bytesWritten: bytesWritten
						  errNo: EIO];

			objc_autoreleasePoolPop(pool);
		}

		if (bytesWritten != j)
			@throw [OFWriteFailedException
			    exceptionWithObject: self
				requestedLength: j * 2
				   bytesWritten: bytesWritten * 2
					  errNo: 0];
	} @finally {
		[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.
	 */
	return length;
}

- (bool)hasTerminal
{
	/*
	 * We can never get here if there is no terminal, as the initializer
	 * changes the class to OFStdIOStream in that case.
	 */
	return true;
}

- (int)columns
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return -1;

	return csbi.dwSize.X;
}

- (int)rows
{
	/*
	 * The buffer size returned is almost always larger than the window
	 * size, so this is useless.
	 */
	return -1;
}

- (void)setForegroundColor: (OFColor *)color
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	float red, green, blue;

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN |
	    FOREGROUND_BLUE | FOREGROUND_INTENSITY);

	[color getRed: &red
		green: &green
		 blue: &blue
		alpha: NULL];

	if (red >= 0.25)
		csbi.wAttributes |= FOREGROUND_RED;
	if (green >= 0.25)
		csbi.wAttributes |= FOREGROUND_GREEN;
	if (blue >= 0.25)
		csbi.wAttributes |= FOREGROUND_BLUE;

	if (red >= 0.75 || green >= 0.75 || blue >= 0.75)
		csbi.wAttributes |= FOREGROUND_INTENSITY;

	SetConsoleTextAttribute(_handle, csbi.wAttributes);
}

- (void)setBackgroundColor: (OFColor *)color
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	float red, green, blue;

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN |
	    BACKGROUND_BLUE | BACKGROUND_INTENSITY);

	[color getRed: &red
		green: &green
		 blue: &blue
		alpha: NULL];

	if (red >= 0.25)
		csbi.wAttributes |= BACKGROUND_RED;
	if (green >= 0.25)
		csbi.wAttributes |= BACKGROUND_GREEN;
	if (blue >= 0.25)
		csbi.wAttributes |= BACKGROUND_BLUE;

	if (red >= 0.75 || green >= 0.75 || blue >= 0.75)
		csbi.wAttributes |= BACKGROUND_INTENSITY;

	SetConsoleTextAttribute(_handle, csbi.wAttributes);
}

- (void)reset
{
	SetConsoleTextAttribute(_handle, _attributes);
}

- (void)clear
{
	static COORD zero = { 0, 0 };
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	DWORD bytesWritten;

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	if (!FillConsoleOutputCharacter(_handle, ' ',
	    csbi.dwSize.X * csbi.dwSize.Y, zero, &bytesWritten))
		return;

	if (!FillConsoleOutputAttribute(_handle, csbi.wAttributes,
	    csbi.dwSize.X * csbi.dwSize.Y, zero, &bytesWritten))
		return;

	SetConsoleCursorPosition(_handle, zero);
}

- (void)eraseLine
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	DWORD bytesWritten;

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.dwCursorPosition.X = 0;

	if (!FillConsoleOutputCharacter(_handle, ' ', csbi.dwSize.X,
		csbi.dwCursorPosition, &bytesWritten))
		return;

	FillConsoleOutputAttribute(_handle, csbi.wAttributes, csbi.dwSize.X,
	    csbi.dwCursorPosition, &bytesWritten);
}

- (void)setCursorColumn: (unsigned int)column
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;

	if (!GetConsoleScreenBufferInfo(_handle, &csbi))
		return;

	csbi.dwCursorPosition.X = column;

	SetConsoleCursorPosition(_handle, csbi.dwCursorPosition);
}

- (void)setCursorPosition: (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

Modified src/OFWindowsRegistryKey.h from [e667684d2c] to [1bed1bb2f4].

128
129
130
131
132
133
134
135
136


137
138
139
140
141
142
143
144
145
146
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
128
129
130
131
132
133
134


135
136
137
138
139
140
141


142
143
144
145



146
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







-
-
+
+





-
-




-
-
-
+
















-


-
+
-





-
-




-
-
-
+







 *		      `lpdwDisposition`.
 * @return The subkey with the specified path
 */
- (OFWindowsRegistryKey *)
	 createSubkeyAtPath: (OFString *)path
		    options: (DWORD)options
    securityAndAccessRights: (REGSAM)securityAndAccessRights
	 securityAttributes: (nullable LPSECURITY_ATTRIBUTES)securityAttributes
		disposition: (nullable LPDWORD)disposition;
	 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 subkeyPath The path of the key from which to retrieve the value
 * @param flags Extra flags for `RegGetValue()`. Usually 0.
 * @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
		       subkeyPath: (nullable OFString *)subkeyPath
			    flags: (DWORD)flags
			     type: (nullable LPDWORD)type;
			     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
 * @param subkeyPath The path of the key from which to retrieve the value
 * @return The string for the specified value
 */
- (nullable OFString *)stringForValue: (nullable OFString *)value
- (nullable OFString *)stringForValue: (nullable OFString *)value;
			   subkeyPath: (nullable OFString *)subkeyPath;

/*!
 * @brief Returns the string for the specified value at the specified path.
 *
 * @param value The name of the value to return
 * @param subkeyPath The path of the key from which to retrieve the value
 * @param flags Extra flags for `RegGetValue()`. Usually 0.
 * @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
			   subkeyPath: (nullable OFString *)subkeyPath
				flags: (DWORD)flags
				 type: (nullable LPDWORD)type;
				 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
 */

Modified src/OFWindowsRegistryKey.m from [c6d5abaa71] to [adb04f0d00].

15
16
17
18
19
20
21


22
23
24
25
26
27
28

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







+
+







+







 * file.
 */

#include "config.h"

#import "OFWindowsRegistryKey.h"
#import "OFData.h"
#import "OFLocale.h"
#import "OFSystemInfo.h"

#include <windows.h>

#import "OFCreateWindowsRegistryKeyFailedException.h"
#import "OFDeleteWindowsRegistryKeyFailedException.h"
#import "OFDeleteWindowsRegistryValueFailedException.h"
#import "OFGetWindowsRegistryValueFailedException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFOpenWindowsRegistryKeyFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFSetWindowsRegistryValueFailedException.h"

@interface OFWindowsRegistryKey ()
- (instancetype)of_initWithHKey: (HKEY)hKey
103
104
105
106
107
108
109

110
111








112
113
114
115
116
117
118
106
107
108
109
110
111
112
113


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128







+
-
-
+
+
+
+
+
+
+
+







				   options: (DWORD)options
		   securityAndAccessRights: (REGSAM)securityAndAccessRights
{
	void *pool = objc_autoreleasePoolPush();
	LSTATUS status;
	HKEY subKey;

	if ([OFSystemInfo isWindowsNT])
	if ((status = RegOpenKeyExW(_hKey, path.UTF16String, options,
	    securityAndAccessRights, &subKey)) != ERROR_SUCCESS) {
		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
140
141
142
143
144
145
146
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
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







-
+





+
-
-
-
+
+
+
+
+
+
+
+
+
+
















-
-
+
-


-
+


+



+
+
+
+
-
-
+
+
+







}

- (OFWindowsRegistryKey *)
	 createSubkeyAtPath: (OFString *)path
		    options: (DWORD)options
    securityAndAccessRights: (REGSAM)securityAndAccessRights
	 securityAttributes: (LPSECURITY_ATTRIBUTES)securityAttributes
		disposition: (LPDWORD)disposition
		disposition: (DWORD *)disposition
{
	void *pool = objc_autoreleasePoolPush();
	LSTATUS status;
	HKEY subKey;

	if ([OFSystemInfo isWindowsNT])
	if ((status = RegCreateKeyExW(_hKey, path.UTF16String, 0,
	    NULL, options, securityAndAccessRights, securityAttributes,
	    &subKey, NULL)) != ERROR_SUCCESS)
		status = RegCreateKeyExW(_hKey, path.UTF16String, 0,
		    NULL, options, securityAndAccessRights, securityAttributes,
		    &subKey, NULL);
	else
		status = RegCreateKeyExA(_hKey,
		    [path cStringWithEncoding: [OFLocale encoding]], 0, NULL,
		    options, securityAndAccessRights, securityAttributes,
		    &subKey, NULL);

	if (status != ERROR_SUCCESS)
		@throw [OFCreateWindowsRegistryKeyFailedException
		    exceptionWithRegistryKey: self
					path: path
				     options: options
		     securityAndAccessRights: securityAndAccessRights
			  securityAttributes: securityAttributes
				      status: status];

	objc_autoreleasePoolPop(pool);

	return [[[OFWindowsRegistryKey alloc] of_initWithHKey: subKey
							close: true]
	    autorelease];
}

- (OFData *)dataForValue: (OFString *)value
	      subkeyPath: (OFString *)subkeyPath
		   flags: (DWORD)flags
		    type: (DWORD *)type
		    type: (LPDWORD)type
{
	void *pool = objc_autoreleasePoolPush();
	char stackBuffer[256], *buffer = stackBuffer;
	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 = RegGetValueW(_hKey, subkeyPath.UTF16String,
		    value.UTF16String, flags, type, buffer, &length);
			status = RegQueryValueExA(_hKey,
			    [value cStringWithEncoding: [OFLocale encoding]],
			    NULL, type, buffer, &length);

		switch (status) {
		case ERROR_SUCCESS:
			if (buffer == stackBuffer) {
				objc_autoreleasePoolPop(pool);

				return [OFData dataWithItems: buffer
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
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







-
-















+
-
-
+
+
+
+
+
+
+
+









-


-
-




-
-
+
-


+

-
-
-
+
-
-





+
+
+
+
+
+
+
-
-
+
+

-
-
+
+

-
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







			buffer = ret.mutableItems;

			continue;
		default:
			@throw [OFGetWindowsRegistryValueFailedException
			    exceptionWithRegistryKey: self
					       value: value
					  subkeyPath: subkeyPath
					       flags: flags
					      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])
	if ((status = RegSetValueExW(_hKey, value.UTF16String, 0, type,
	    data.items, (DWORD)length)) != ERROR_SUCCESS)
		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
		  subkeyPath: (OFString *)subkeyPath
{
	return [self stringForValue: value
			 subkeyPath: subkeyPath
			      flags: RRF_RT_REG_SZ
			       type: NULL];
}

- (OFString *)stringForValue: (OFString *)value
		  subkeyPath: (OFString *)subkeyPath
		       flags: (DWORD)flags
			type: (DWORD *)typeOut
			type: (LPDWORD)type
{
	void *pool = objc_autoreleasePoolPush();
	DWORD type;
	OFData *data = [self dataForValue: value
			       subkeyPath: subkeyPath
				    flags: flags
				     type: type];
				     type: &type];
	const of_char16_t *UTF16String;
	size_t length;
	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]) {
	UTF16String = data.items;
	length = data.count;
		const of_char16_t *UTF16String = data.items;
		size_t length = data.count;

	if (data.itemSize != 1 || length % 2 == 1)
		@throw [OFInvalidFormatException exception];
		if (length % 2 == 1)
			@throw [OFInvalidFormatException exception];

	length /= 2;
		length /= 2;

	/*
	 * REG_SZ and REG_EXPAND_SZ contain a \0, but can contain data after it
	 * that should be ignored.
	 */
	for (size_t i = 0; i < length; i++) {
		if (UTF16String[i] == 0) {
			length = i;
			break;
		}
	}
		/*
		 * REG_SZ and REG_EXPAND_SZ contain a \0, but can contain data
		 * after it that should be ignored.
		 */
		for (size_t i = 0; i < length; i++) {
			if (UTF16String[i] == 0) {
				length = i;
				break;
			}
		}

	ret = [[OFString alloc] initWithUTF16String: UTF16String
					     length: length];
		ret = [[OFString alloc] initWithUTF16String: UTF16String
						     length: length];
	} else {
		const char *cString = data.items;
		size_t length = data.count;

		/*
		 * REG_SZ and REG_EXPAND_SZ contain a \0, but can contain data
		 * after it that should be ignored.
		 */
		for (size_t i = 0; i < length; i++) {
			if (cString[i] == 0) {
				length = i;
				break;
			}
		}

		ret = [[OFString alloc] initWithCString: cString
					       encoding: [OFLocale encoding]
						 length: length];
	}

	if (typeOut != NULL)
		*typeOut = type;

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (void)setString: (OFString *)string
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
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







+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+












+
-
-
+
+
+
+
+
+













+
-
-
+
+
+
+
+
+








- (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];
		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])
	if ((status = RegDeleteValueW(_hKey, value.UTF16String)) !=
	    ERROR_SUCCESS)
		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
{
	void *pool = objc_autoreleasePoolPush();
	LSTATUS status;

	if ([OFSystemInfo isWindowsNT])
	if ((status = RegDeleteKeyW(_hKey, subkeyPath.UTF16String)) !=
	    ERROR_SUCCESS)
		status = RegDeleteKeyW(_hKey, subkeyPath.UTF16String);
	else
		status = RegDeleteKeyA(_hKey,
		    [subkeyPath cStringWithEncoding: [OFLocale encoding]]);

	if (status != ERROR_SUCCESS)
		@throw [OFDeleteWindowsRegistryKeyFailedException
		    exceptionWithRegistryKey: self
				  subkeyPath: subkeyPath
				      status: status];

	objc_autoreleasePoolPop(pool);
}
@end

Modified src/OFXMLParser.m from [d4fd816fdb] to [7ad1f08170].

30
31
32
33
34
35
36


37
38
39
40
41
42
43
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45







+
+







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

typedef void (*state_function_t)(id, SEL);
static SEL selectors[OF_XMLPARSER_NUM_STATES];
476
477
478
479
480
481
482
483
484









485
486
487
488
489
490
491
478
479
480
481
482
483
484


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







-
-
+
+
+
+
+
+
+
+
+







			if ([attribute isEqual: @"version"]) {
				if (![value hasPrefix: @"1."])
					return false;

				hasVersion = true;
			}

			if ([attribute isEqual: @"encoding"])
				_encoding = of_string_parse_encoding(value);
			if ([attribute isEqual: @"encoding"]) {
				@try {
					_encoding =
					    of_string_parse_encoding(value);
				} @catch (OFInvalidArgumentException *e) {
					@throw [OFInvalidEncodingException
					    exception];
				}
			}

			last = i + 1;
			PIState = 0;

			break;
		}
	}

Modified src/ObjFW.h from [e5a1a42cc2] to [58db83c230].

74
75
76
77
78
79
80








81
82
83
84
85
86
87
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95







+
+
+
+
+
+
+
+







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

Modified src/block.h from [bf1e73c83e] to [6724cf2142].

11
12
13
14
15
16
17



18

19
20
21
22
23
24
25
11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28







+
+
+
-
+







 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either 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

#import "macros.h"
#include "macros.h"

OF_ASSUME_NONNULL_BEGIN

typedef struct of_block_literal_t {
#ifdef __OBJC__
	Class isa;
#else
66
67
68
69
70
71
72


69
70
71
72
73
74
75
76
77







+
+
    ((__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/exceptions/OFBindFailedException.h from [de856810d3] to [5b18a08191].

16
17
18
19
20
21
22


23
24
25
26
27
28
29
30
31
32
33
34

35
36


37
38
39
40
41
42
43
44
45
46
47
48
49





50
51
52
53
54
55
56
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 "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.
69
70
71
72
73
74
75














76
77
78
79
80
81
82
83
84
85
86
87
88
89
90















91
92
93
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131







+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



 * @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
 * @param errNo The errno of the error that occurred
 * @return An initialized bind failed exception
 */
- (instancetype)initWithHost: (OFString *)host
			port: (uint16_t)port
		      socket: (id)socket
		       errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
		       errNo: (int)errNo;

/*!
 * @brief Initializes an already allocated bind failed exception.
 *
 * @param port The IPX port to which binding failed
 * @param packetType The IPX packet type for which binding failed
 * @param socket The socket which could not be bound
 * @param errNo The errno of the error that occurred
 * @return An initialized bind failed exception
 */
- (instancetype)initWithPort: (uint16_t)port
		  packetType: (uint8_t)packetType
		      socket: (id)socket
		       errNo: (int)errNo;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFBindFailedException.m from [a4be0b4e2f] to [66d49adee0].

17
18
19
20
21
22
23
24


25
26
27
28
29
30
31
32
33

34
35
36
37
38












39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57





















58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80









81
82
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33

34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109




110
111
112
113
114
115
116
117
118
119
120







-
+
+








-
+




-
+
+
+
+
+
+
+
+
+
+
+
+










-
+







-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















+
-
-
-
-
+
+
+
+
+
+
+
+
+



#include "config.h"

#import "OFBindFailedException.h"
#import "OFString.h"

@implementation OFBindFailedException
@synthesize host = _host, port = _port, socket = _socket, errNo = _errNo;
@synthesize host = _host, port = _port, packetType = _packetType;
@synthesize socket = _socket, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithHost: (OFString *)host
			     port: (uint16_t)port
			   socket: (id)socket
			   socket: (id)sock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithHost: host
				      port: port
				    socket: socket
				    socket: sock
				     errNo: errNo] autorelease];
}

+ (instancetype)exceptionWithPort: (uint16_t)port
		       packetType: (uint8_t)packetType
			   socket: (id)sock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithPort: port
				packetType: packetType
				    socket: sock
				     errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithHost: (OFString *)host
			port: (uint16_t)port
		      socket: (id)socket
		      socket: (id)sock
		       errNo: (int)errNo
{
	self = [super init];

	@try {
		_host = [host copy];
		_port = port;
		_socket = [socket retain];
		_socket = [sock retain];
		_errNo = errNo;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithPort: (uint16_t)port
		  packetType: (uint8_t)packetType
		      socket: (id)sock
		       errNo: (int)errNo
{
	self = [super init];

	@try {
		_port = port;
		_packetType = packetType;
		_socket = [sock retain];
		_errNo = errNo;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (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)];
		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

Modified src/exceptions/OFConnectionFailedException.h from [e2e47e53e8] to [cd5a362f90].

16
17
18
19
20
21
22


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


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54










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







+
+














+
+


















+
+
+
+
+
+
+
+
+
+







 */

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

69
70
71
72
73
74
75
















76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

















91
92
93
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



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

Modified src/exceptions/OFConnectionFailedException.m from [19c84f566e] to [7c382ce8cb].

17
18
19
20
21
22
23
24


25
26
27
28
29
30
31
32
33

34
35
36
37
38














39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55

56





















57

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





74
75
76
77
78
79
80
81







82
83
84
85
86
87
88
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33

34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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 "config.h"

#import "OFConnectionFailedException.h"
#import "OFString.h"

@implementation OFConnectionFailedException
@synthesize host = _host, port = _port, socket = _socket, errNo = _errNo;
@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)socket
			   socket: (id)sock
			    errNo: (int)errNo
{
	return [[[self alloc] initWithHost: host
				      port: port
				    socket: socket
				    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)socket
		      socket: (id)sock
		       errNo: (int)errNo
{
	self = [super init];

	@try {
		_host = [host copy];
		_port = port;
		_socket = [socket retain];
		_socket = [sock retain];
		_errNo = errNo;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)initWithNode: (unsigned char [IPX_NODE_LEN])node
		     network: (uint32_t)network
			port: (uint16_t)port
		      socket: (id)sock
		       errNo: (int)errNo
{
	self = [super init];

	@try {
		memcpy(_node, node, IPX_NODE_LEN);
		_network = network;
		_port = port;
		_socket = [sock retain];
		_errNo = errNo;
	} @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

Modified src/exceptions/OFException.m from [0f0c49d47c] to [bf7ff24437].

23
24
25
26
27
28
29
30
31
32




33
34
35
36
37
38
39
23
24
25
26
27
28
29



30
31
32
33
34
35
36
37
38
39
40







-
-
-
+
+
+
+







#include <string.h>

#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#endif

#import "OFException.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFLocale.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"
86
87
88
89
90
91
92
93

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

94
95
96
97
98
99
100
101







-
+







#ifdef HAVE_STRERROR_R
	char buffer[256];
#endif

	if (errNo == 0)
		return @"Unknown error";

#ifdef OF_WINDOWS
#if defined(OF_WINDOWS) && defined(OF_HAVE_SOCKETS)
	/*
	 * These were translated from WSAE* errors to errno and thus Win32's
	 * strerror_r() does not know about them.
	 *
	 * FIXME: These could have better descriptions!
	 */
	switch (errNo) {
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182







-
+







	case EUSERS:
		return @"EUSERS";
	case EWOULDBLOCK:
		return @"EWOULDBLOCK";
	}
#endif

#if defined(HAVE_STRERROR_R) && defined(_GNU_SOURCE)
#if defined(STRERROR_R_RETURNS_CHARP)
	/* glibc uses a different strerror_r when _GNU_SOURCE is defined */
	char *string;

	if ((string = strerror_r(errNo, buffer, 256)) == NULL)
		return @"Unknown error (strerror_r failed)";

	ret = [OFString stringWithCString: string
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
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







+

-

+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	return ret;
}

#ifdef OF_WINDOWS
OFString *
of_windows_status_to_string(LSTATUS status)
{
	OFString *string = nil;
	void *buffer;
	OFString *string;

	if ([OFSystemInfo isWindowsNT]) {
	if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
	    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS |
	    FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, status, 0, (LPWSTR)&buffer, 0,
	    NULL) != 0) {
		@try {
			string = [OFString stringWithUTF16String: buffer];
		} @finally {
			LocalFree(buffer);
		}
	} else
		if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
		    FORMAT_MESSAGE_ALLOCATE_BUFFER |
		    FORMAT_MESSAGE_IGNORE_INSERTS |
		    FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, status, 0,
		    (LPWSTR)&buffer, 0, NULL) != 0) {
			@try {
				string = [OFString
				    stringWithUTF16String: buffer];
			} @finally {
				LocalFree(buffer);
			}
		}
	} else {
		if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
		    FORMAT_MESSAGE_ALLOCATE_BUFFER |
		    FORMAT_MESSAGE_IGNORE_INSERTS |
		    FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, status, 0,
		    (LPSTR)&buffer, 0, NULL) != 0) {
			@try {
				string = [OFString
				    stringWithCString: buffer
					     encoding: [OFLocale encoding]];
			} @finally {
				LocalFree(buffer);
			}
		}
	}

	if (string == nil)
		string = [OFString stringWithFormat: @"Status code %u", status];

	return string;
}
#endif

#ifdef HAVE__UNWIND_BACKTRACE

Modified src/exceptions/OFGetOptionFailedException.h from [4a4969db85] to [70541d2ad1].

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

29
30
31
32

33
34
35
36
37

38
39

40
41
42
43
44
45
46
47
48
49
50
51

52
53
54
55

56
57
58
59
60
61
62
63

64
65
66
67

68
69
70
71
15
16
17
18
19
20
21


22
23
24
25

26
27
28
29

30
31
32
33
34

35
36

37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52

53
54
55
56
57
58
59
60

61
62
63
64

65
66
67
68
69







-
-




-
+



-
+




-
+

-
+











-
+



-
+







-
+



-
+




 * file.
 */

#import "OFException.h"

OF_ASSUME_NONNULL_BEGIN

@class OFStream;

/*!
 * @class OFGetOptionFailedException \
 *	  OFGetOptionFailedException.h ObjFW/OFGetOptionFailedException.h
 *
 * @brief An exception indicating that getting an option for a stream failed.
 * @brief An exception indicating that getting an option for an object failed.
 */
@interface OFGetOptionFailedException: OFException
{
	OFStream *_stream;
	id _object;
	int _errNo;
}

/*!
 * @brief The stream for which the option could not be retrieved.
 * @brief The object for which the option could not be retrieved.
 */
@property (readonly, nonatomic) OFStream *stream;
@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 stream The stream for which the option could not be gotten
 * @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)exceptionWithStream: (OFStream *)stream
+ (instancetype)exceptionWithObject: (id)object
			      errNo: (int)errNo;

- (instancetype)init OF_UNAVAILABLE;

/*!
 * @brief Initializes an already allocated get option failed exception.
 *
 * @param stream The stream for which the option could not be gotten
 * @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)initWithStream: (OFStream *)stream
- (instancetype)initWithObject: (id)object
			 errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFGetOptionFailedException.m from [2c7cb59f5d] to [a2104983ac].

15
16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32

33
34
35

36
37
38
39
40
41
42
43
44

45
46
47
48
49

50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66


67
68
15
16
17
18
19
20
21

22
23

24
25
26
27
28
29
30

31
32
33

34
35
36
37
38
39
40
41
42

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

#include "config.h"

#import "OFGetOptionFailedException.h"
#import "OFString.h"
#import "OFStream.h"

@implementation OFGetOptionFailedException
@synthesize stream = _stream, errNo = _errNo;
@synthesize object = _object, errNo = _errNo;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithStream: (OFStream *)stream
+ (instancetype)exceptionWithObject: (id)object
			      errNo: (int)errNo
{
	return [[[self alloc] initWithStream: stream
	return [[[self alloc] initWithObject: object
				       errNo: errNo] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithStream: (OFStream *)stream
- (instancetype)initWithObject: (id)object
			 errNo: (int)errNo
{
	self = [super init];

	_stream = [stream retain];
	_object = [object retain];
	_errNo = errNo;

	return self;
}

- (void)dealloc
{
	[_stream release];
	[_object release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Getting an option in a stream of type %@ failed: %@",
	    _stream.class, of_strerror(_errNo)];
	    @"Getting an option in an object of type %@ failed: %@",
	    [_object class], of_strerror(_errNo)];
}
@end

Modified src/exceptions/OFGetWindowsRegistryValueFailedException.h from [d8a164b776] to [f3d99ecbde].

28
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49










50
51
52
53
54
55
56
57
58
59
60
61


62
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, *_Nullable _subkeyPath;
	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 subkey path at which getting the value failed.
 */
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *subkeyPath;

/*!
 * @brief The flags with which getting the value failed.
 */
@property (readonly, nonatomic) DWORD flags;

/*!
 * @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 subkeyPath The subkey path at which getting the value failed
 * @param flags The flags with which getting the value failed
 * @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
			      subkeyPath: (nullable OFString *)subkeyPath
				   flags: (DWORD)flags
				  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 subkeyPath The subkey path at which getting the value failed
 * @param flags The flags with which getting the value failed
 * @param status The status returned by RegGetValueEx()
 * @return An initialized get Windows registry value failed exception
 */
- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			      value: (nullable OFString *)value
			 subkeyPath: (nullable OFString *)subkeyPath
			      flags: (DWORD)flags
			     status: (LSTATUS)status OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFGetWindowsRegistryValueFailedException.m from [3f3297c06e] to [1c37f45def].

16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79


80
81
16
17
18
19
20
21
22

23

24
25
26


27
28
29
30


31
32
33
34
35
36
37
38
39
40


41
42
43
44
45
46
47


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

61
62
63
64
65
66
67


68
69
70
71







-
+
-



-
-




-
-










-
-







-
-













-







-
-
+
+


 */

#include "config.h"

#import "OFGetWindowsRegistryValueFailedException.h"

@implementation OFGetWindowsRegistryValueFailedException
@synthesize registryKey = _registryKey, value = _value;
@synthesize registryKey = _registryKey, value = _value, status = _status;
@synthesize subkeyPath = _subkeyPath, flags = _flags, status = _status;

+ (instancetype)exceptionWithRegistryKey: (OFWindowsRegistryKey *)registryKey
				   value: (OFString *)value
			      subkeyPath: (OFString *)subkeyPath
				   flags: (DWORD)flags
				  status: (LSTATUS)status
{
	return [[[self alloc] initWithRegistryKey: registryKey
					    value: value
				       subkeyPath: subkeyPath
					    flags: flags
					   status: status] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithRegistryKey: (OFWindowsRegistryKey *)registryKey
			      value: (OFString *)value
			 subkeyPath: (OFString *)subkeyPath
			      flags: (DWORD)flags
			     status: (LSTATUS)status
{
	self = [super init];

	@try {
		_registryKey = [registryKey retain];
		_value = [value copy];
		_subkeyPath = [subkeyPath copy];
		_flags = flags;
		_status = status;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_registryKey release];
	[_value release];
	[_subkeyPath release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to get value %@ at subkey path %@: %@",
	    _value, _subkeyPath, of_windows_status_to_string(_status)];
	    @"Failed to get value %@: %@",
	    _value, of_windows_status_to_string(_status)];
}
@end

Modified src/macros.h from [9bed6604d9] to [b900515d8a].

10
11
12
13
14
15
16



17
18
19
20
21
22
23
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26







+
+
+







 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either 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_MACROS_H
#define OBJFW_MACROS_H

#include "objfw-defs.h"

#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS
#endif
#ifndef __STDC_CONSTANT_MACROS
33
34
35
36
37
38
39
40

41
42

43
44
45
46
47
48



49
50
51
52
53
54
55
36
37
38
39
40
41
42

43
44

45
46
47
48



49
50
51
52
53
54
55
56
57
58







-
+

-
+



-
-
-
+
+
+








#include <sys/time.h>

#include "platform.h"

#ifdef OF_OBJFW_RUNTIME
# ifdef OF_COMPILING_OBJFW
#  import "ObjFWRT.h"
#  include "ObjFWRT.h"
# else
#  import <ObjFWRT/ObjFWRT.h>
#  include <ObjFWRT/ObjFWRT.h>
# endif
#endif
#ifdef OF_APPLE_RUNTIME
# import <objc/objc.h>
# import <objc/runtime.h>
# import <objc/message.h>
# include <objc/objc.h>
# include <objc/runtime.h>
# include <objc/message.h>
#endif

#if defined(__GNUC__)
# define restrict __restrict__
#elif __STDC_VERSION__ < 199901L
# define restrict
#endif
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
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








-
-
+
+

+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
-
-
+
-
-
-
-







# 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 float OF_CONST_FUNC
OF_BSWAP_FLOAT(float f)
static OF_INLINE uint32_t
OF_FLOAT_TO_INT_RAW(float f)
{
	uint32_t ret;
	memcpy(&ret, &f, 4);
	return ret;
}
	union {
		float f;
		uint32_t i;
	} u;

	u.f = f;
	u.i = OF_BSWAP32(u.i);

	return u.f;

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)
{
	union {
		double d;
		uint64_t i;
	} u;

	return OF_INT_TO_DOUBLE_RAW(OF_BSWAP64(OF_DOUBLE_TO_INT_RAW(d)));
	u.d = d;
	u.i = OF_BSWAP64(u.i);

	return u.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)
884
885
886
887
888
889
890


903
904
905
906
907
908
909
910
911







+
+
	struct timeval tv;

	gettimeofday(&tv, NULL);
	srand((unsigned)(tv.tv_sec ^ tv.tv_usec));
	return (((uint32_t)(rand()) << 16) | ((uint32_t)(rand()) & 0xFFFF));
#endif
}

#endif

Modified src/objfw-defs.h.in from [96b18781fc] to [f12fccebb9].

11
12
13
14
15
16
17

18
19
20
21



22
23
24
25
26
27
28
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32







+




+
+
+







#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

Modified src/of_asprintf.m from [04c9933717] to [cd42320784].

95
96
97
98
99
100
101
102

103
104
105
106
107
108
109









110
111
112





















113
114
115
116
117
118
119
95
96
97
98
99
100
101

102
103
104
105
106



107
108
109
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145







-
+




-
-
-
+
+
+
+
+
+
+
+
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







}
#endif

#ifndef HAVE_ASPRINTF
static int
vasprintf(char **string, const char *format, va_list arguments)
{
	int length;
	int expectedLength, length;
	va_list argumentsCopy;

	va_copy(argumentsCopy, arguments);

	if ((length = vsnprintf(NULL, 0, format, argumentsCopy)) < 0)
		return length;
	if ((*string = malloc((size_t)length + 1)) == NULL)
	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;

	return vsnprintf(*string, (size_t)length + 1, format, arguments);
	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
asprintf(char **string, const char *format, ...)
{
	int ret;
	va_list arguments;
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
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







+
-
-
+
+













+
-
-
+
+







#endif

		ctx->lengthModifier = LENGTH_MODIFIER_J;

		break;
	case 'z':
#if defined(OF_WINDOWS)
		if (sizeof(size_t) == 8)
		if (!appendSubformat(ctx, "I", 1))
			return false;
			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, "I", 1))
			return false;
			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
350
351
352
353
354
355
356



357
358
359
360
361
362
363
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394







+
+
+







}

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







+
+
+





+
+
-
+







-
-
-
-
-
+
+
+


+







				    va_arg(ctx->arguments, long double));
			break;
		default:
			return false;
		}

#ifndef HAVE_ASPRINTF_L
		if (tmpLen == -1)
			return false;

		/*
		 * If there's no asprintf_l, we have no other choice than to
		 * use this ugly hack to replace the locale's decimal point
		 * back to ".".
		 */
		point = [OFLocale decimalPoint];

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

			@try {
				OFMutableString *tmpStr = [OFMutableString
				    stringWithUTF8String: tmp
						  length: tmpLen];
				OFString *point = [OFLocale decimalPoint];
				if (point != nil)
					[tmpStr
					    replaceOccurrencesOfString: point
							    withString: @"."];
				[tmpStr replaceOccurrencesOfString: point
							withString: @"."];

				if (tmpStr.UTF8StringLength > INT_MAX)
					return false;

				tmpLen = (int)tmpStr.UTF8StringLength;
				tmp2 = malloc(tmpLen);
				memcpy(tmp2, tmpStr.UTF8String, tmpLen);
			} @finally {
				free(tmp);
				objc_autoreleasePoolPop(pool);
			}

Modified src/platform.h from [5334ff113e] to [40ddf7ef54].

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








98
99
100
101
102
103
104
86
87
88
89
90
91
92





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







-
-
-
-
-
+
+
+
+
+
+
+
+







# endif
# if defined(__mc68020__) || defined(OF_M68030)
#  define OF_M68020
# endif
# if defined(__mc68010__) || defined(OF_M68020)
#  define OF_M68010
# endif
# if defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64
#  define OF_RISC_V_64
# elif defined(__riscv)
#  define OF_RISC_V
# endif
#elif defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64
# 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

Modified src/platform/posix/OFProcess.m from [bd118b08f1] to [a1d2451059].

127
128
129
130
131
132
133



134
135
136
137
138
139
140
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143







+
+
+







	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
370
371
372
373
374
375
376













377
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393







+
+
+
+
+
+
+
+
+
+
+
+
+

	}

	_pid = -1;
	_readPipe[0] = -1;

	[super close];
}

- (int)waitForTermination
{
	if (_readPipe[0] == -1)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (_pid != -1) {
		waitpid(_pid, &_status, 0);
		_pid = -1;
	}

	return WEXITSTATUS(_status);
}
@end

Modified src/platform/posix/thread.m from [537e06aaee] to [cdd7c510a1].

56
57
58
59
60
61
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77







+







-







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

static void *
functionWrapper(void *data)
{
	struct thread_ctx *ctx = data;

Modified src/platform/windows/OFProcess.m from [0696e641e7] to [816ee6b0db].

17
18
19
20
21
22
23
24
25


26
27



28
29
30
31
32
33
34
35
36
37
38


39
40
41
42
43
44
45
17
18
19
20
21
22
23


24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48







-
-
+
+

-
+
+
+










-
+
+








#include "config.h"

#include <errno.h>
#include <string.h>

#import "OFProcess.h"
#import "OFString.h"
#import "OFArray.h"
#import "OFArray.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFData.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_environmentForDictionary: (OFDictionary *)dictionary;
- (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];
}
109
110
111
112
113
114
115
116
117
118
119
120



121
122
123
124
125
126
127
128
129
130

131
132


133
134
135
136
137
138
139

140
141


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
112
113
114
115
116
117
118

119
120


121
122
123
124
125
126
127
128
129
130
131
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 {
		SECURITY_ATTRIBUTES sa;
		PROCESS_INFORMATION pi;
		STARTUPINFOW si;
		void *pool;
		OFMutableString *argumentsString;
		of_char16_t *argumentsCopy;
		size_t length;

		_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))
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		if (!SetHandleInformation(_readPipe[0], HANDLE_FLAG_INHERIT, 0))
			if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];

		if (!CreatePipe(&_writePipe[0], &_writePipe[1], &sa, 0))
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];

		if (!SetHandleInformation(_writePipe[1],
		    HANDLE_FLAG_INHERIT, 0))
			if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
			@throw [OFInitializationFailedException
			    exceptionWithClass: self.class];
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];

		memset(&pi, 0, sizeof(pi));
		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;

		pool = objc_autoreleasePoolPush();

		argumentsString =
		    [OFMutableString stringWithString: programName];
		[argumentsString replaceOccurrencesOfString: @"\\\""
						 withString: @"\\\\\""];
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
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







+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








			[argumentsString appendString: tmp];

			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,
		    (argumentsString.UTF16StringLength + 1) * 2);
		@try {
			if (!CreateProcessW(program.UTF16String,
			    argumentsCopy, NULL, NULL, TRUE,
			    CREATE_UNICODE_ENVIRONMENT,
			    [self of_environmentForDictionary: environment],
			    NULL, &si, &pi))
				@throw [OFInitializationFailedException
				    exceptionWithClass: self.class];
		} @finally {
			[self freeMemory: argumentsCopy];
			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);

220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264







-
+







{
	if (_readPipe[0] != NULL)
		[self close];

	[super dealloc];
}

- (of_char16_t *)of_environmentForDictionary: (OFDictionary *)environment
- (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 };

248
249
250
251
252
253
254































255
256
257
258
259
260
261
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







		[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)
341
342
343
344
345
346
347






















348
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

	}

	_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

Modified src/runtime/linklib/linklib.m from [570378ad75] to [141357802d].

59
60
61
62
63
64
65
66

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

66
67
68
69
70
71
72
73







-
+







#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
static void __attribute__((__used__))
ctor(void)
{
	static bool initialized = false;
	struct objc_libc libc = {
		.malloc = malloc,
		.calloc = calloc,
		.realloc = realloc,
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126







-
+







		fputs("Failed to initialize " OBJFWRT_AMIGA_LIB "!\n", stderr);
		abort();
	}

	initialized = true;
}

static void __attribute__((__unused__))
static void __attribute__((__used__))
dtor(void)
{
	CloseLibrary(ObjFWRTBase);
}

#if defined(OF_AMIGAOS_M68K)
ADD2INIT(ctor, -2);

Modified src/socket.h from [d2c0976e15] to [b3045e055c].

30
31
32
33
34
35
36






37
38
39
40
41
42



43
44
45
46
47
48
49
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58







+
+
+
+
+
+






+
+
+







#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
85
86
87
88
89
90
91


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


















108
109
110
111
112
113

114
115
116
117
118
119
120
121
122
123
124
125

126
127

128

129
130
131
132
133
134
135
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166







+
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+












+


+
-
+







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 {
		uint8_t s6_addr[16];
	} sin6_addr;
	uint32_t sin6_scope_id;
};
#endif

#ifndef OF_HAVE_IPX
# define IPX_NODE_LEN 6
struct sockaddr_ipx {
	sa_family_t sipx_family;
	uint32_t sipx_network;
	unsigned char sipx_node[IPX_NODE_LEN];
	uint16_t sipx_port;
	uint8_t sipx_type;
};
#endif
#ifdef OF_WINDOWS
# 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.
 */
typedef struct OF_BOXABLE {
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;
};
} of_socket_address_t;
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.
 *
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163











164
165
166
167
168
169
170
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







-









-
+
+
+
+
+
+
+
+
+
+
+







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

#ifdef OF_HAVE_IPV6
/*!
 * @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);
#endif

/*!
 * @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
209
210
211
212
213
214
215






































216
217
218
219
220
221
222
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







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

Modified src/socket.m from [353f4118f9] to [fb89efec65].

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
342
343
344
345
346
347
348


349
350
351
352
353
354
355







-
-







	return ret;
}
#endif

of_socket_address_t
of_socket_address_parse_ipv4(OFString *IPv4, uint16_t port)
{
	/* TODO: Support IPs that are not in the a.b.c.d format? */

	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;
505
506
507
508
509
510
511
























512
513
514
515
516
517
518

519
520
521
522
523
524
525
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+







{
	@try {
		return of_socket_address_parse_ipv6(IP, port);
	} @catch (OFInvalidFormatException *e) {
		return of_socket_address_parse_ipv4(IP, port);
	}
}

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)
551
552
553
554
555
556
557


















558
559
560
561
562
563
564
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







		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;
}
597
598
599
600
601
602
603




















604
605
606
607
608
609
610
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







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

717
718
719
720
721
722
723



724
725
726
727
728
729
730
731
732
733
734
735
736


737
738
739
740













































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







+
+
+













+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
	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);
}

Modified src/unicode.m from [910afd7d8b] to [8d99c0a504].

639
640
641
642
643
644
645

646
647
648
649
650

651
652
653
654
655
656
657
658
639
640
641
642
643
644
645
646
647
648
649
650

651

652
653
654
655
656
657
658







+




-
+
-







	0, 42896, 0, 42898, 42948, 0, 0, 42902,
	0, 42904, 0, 42906, 0, 42908, 0, 42910,
	0, 42912, 0, 42914, 0, 42916, 0, 42918,
	0, 42920, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 42932, 0, 42934,
	0, 42936, 0, 42938, 0, 42940, 0, 42942,
	0, 0, 0, 42946, 0, 0, 0, 0,
	42951, 0, 42953, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 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,
	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,
1443
1444
1445
1446
1447
1448
1449
1450


1451
1452
1453
1454
1455

1456
1457
1458
1459
1460
1461
1462
1463
1443
1444
1445
1446
1447
1448
1449

1450
1451
1452
1453
1454
1455

1456

1457
1458
1459
1460
1461
1462
1463







-
+
+




-
+
-







	0, 0, 0, 42892, 0, 613, 0, 0,
	42897, 0, 42899, 0, 0, 0, 42903, 0,
	42905, 0, 42907, 0, 42909, 0, 42911, 0,
	42913, 0, 42915, 0, 42917, 0, 42919, 0,
	42921, 0, 614, 604, 609, 620, 618, 0,
	670, 647, 669, 43859, 42933, 0, 42935, 0,
	42937, 0, 42939, 0, 42941, 0, 42943, 0,
	0, 0, 42947, 0, 42900, 642, 7566, 0,
	0, 0, 42947, 0, 42900, 642, 7566, 42952,
	0, 42954, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 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,
	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,
5942
5943
5944
5945
5946
5947
5948



































































































































5949
5950
5951
5952
5953
5954
5955
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
};

static const char *const decompositionPage281[0x100] = {
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	"\xF0\x91\xA4\xB5\xF0\x91\xA4\xB0", NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
};

static const char *const decompositionPage465[0x100] = {
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
10319
10320
10321
10322
10323
10324
10325
10326

10327
10328
10329
10330
10331
10332
10333
10450
10451
10452
10453
10454
10455
10456

10457
10458
10459
10460
10461
10462
10463
10464







-
+







	NULL, NULL,
	"\xEA\x9C\xA7", "\xEA\xAC\xB7",
	"\xC9\xAB", "\xEA\xAD\x92",
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, "\xCA\x8D",
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
12099
12100
12101
12102
12103
12104
12105



































































































































12106
12107
12108
12109
12110
12111
12112
12230
12231
12232
12233
12234
12235
12236
12237
12238
12239
12240
12241
12242
12243
12244
12245
12246
12247
12248
12249
12250
12251
12252
12253
12254
12255
12256
12257
12258
12259
12260
12261
12262
12263
12264
12265
12266
12267
12268
12269
12270
12271
12272
12273
12274
12275
12276
12277
12278
12279
12280
12281
12282
12283
12284
12285
12286
12287
12288
12289
12290
12291
12292
12293
12294
12295
12296
12297
12298
12299
12300
12301
12302
12303
12304
12305
12306
12307
12308
12309
12310
12311
12312
12313
12314
12315
12316
12317
12318
12319
12320
12321
12322
12323
12324
12325
12326
12327
12328
12329
12330
12331
12332
12333
12334
12335
12336
12337
12338
12339
12340
12341
12342
12343
12344
12345
12346
12347
12348
12349
12350
12351
12352
12353
12354
12355
12356
12357
12358
12359
12360
12361
12362
12363
12364
12365
12366
12367
12368
12369
12370
12371
12372
12373
12374







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
};

static const char *const decompCompatPage507[0x100] = {
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	NULL, NULL,
	"\x30", "\x31",
	"\x32", "\x33",
	"\x34", "\x35",
	"\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,
12739
12740
12741
12742
12743
12744
12745
12746

12747
12748
12749
12750
12751
12752
12753
13001
13002
13003
13004
13005
13006
13007

13008
13009
13010
13011
13012
13013
13014
13015







-
+







	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, decompositionPage272,
	decompositionPage273, emptyDecompositionPage, decompositionPage275,
	decompositionPage276, decompositionPage277, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, decompositionPage281,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
12997
12998
12999
13000
13001
13002
13003
13004

13005
13006
13007
13008
13009
13010
13011
13259
13260
13261
13262
13263
13264
13265

13266
13267
13268
13269
13270
13271
13272
13273







-
+







	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, decompositionPage272,
	decompositionPage273, emptyDecompositionPage, decompositionPage275,
	decompositionPage276, decompositionPage277, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, decompositionPage281,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
13073
13074
13075
13076
13077
13078
13079
13080

13081
13082
13083
13084
13085
13086
13087
13335
13336
13337
13338
13339
13340
13341

13342
13343
13344
13345
13346
13347
13348
13349







-
+







	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, decompCompatPage494,
	emptyDecompositionPage, emptyDecompositionPage, decompCompatPage497,
	decompCompatPage498, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	decompCompatPage507, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,
	emptyDecompositionPage, emptyDecompositionPage, emptyDecompositionPage,

Modified tests/Makefile from [c5b865857f] to [34b62fa1a0].

1
2
3
4
5
6
7
8
9
10
11
12
13
14

15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21













-
+







include ../extra.mk

SUBDIRS = ${TESTPLUGIN}

CLEAN = EBOOT.PBP		\
	boot.dol		\
	${PROG_NOINST}.arm9	\
	${PROG_NOINST}.nds

PROG_NOINST = tests${PROG_SUFFIX}
STATIC_LIB_NOINST = ${TESTS_STATIC_LIB}
SRCS = ForwardingTests.m		\
       OFArrayTests.m			\
       ${OFBLOCKTESTS_M}		\
       ${OF_BLOCK_TESTS_M}		\
       OFCharacterSetTests.m		\
       OFDataTests.m			\
       OFDateTests.m			\
       OFDictionaryTests.m		\
       OFInvocationTests.m		\
       OFJSONTests.m			\
       OFListTests.m			\
48
49
50
51
52
53
54



55

56
57

58
59
60
61
62
63



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

79
80
81

82
83
84
85
86
87
88
89
90
91
92


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


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

125
126
127

128
129
130
131
132
133
134
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

84
85
86

87
88
89
90
91
92
93
94
95
96


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


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132

133
134
135
136
137
138
139
140







+
+
+

+

-
+





-
+
+
+














-
+


-
+









-
-
+
+













-
-
+
+
















-
+


-
+







	     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		\
	       ${OFHTTPCLIENTTESTS_M}		\
	       ${OF_HTTP_CLIENT_TESTS_M}	\
	       OFHTTPCookieTests.m		\
	       OFHTTPCookieManagerTests.m	\
	       OFKernelEventObserverTests.m	\
	       OFTCPSocketTests.m		\
	       OFUDPSocketTests.m		\
	       SocketTests.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 libobjfw.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	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 libobjfwrt.dll libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	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/libobjfw.dll; then \
		${LN_S} ../src/libobjfw.dll libobjfw.dll; \
	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/libobjfwrt.dll; then \
		${LN_S} ../src/runtime/libobjfwrt.dll libobjfwrt.dll; \
	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} libobjfw.dll; \
	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 libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.dll; \
	rm -f objfwrt.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; \

Modified tests/OFDNSResolverTests.m from [d86ca4b2a2] to [8f7194537b].

23
24
25
26
27
28
29


30
31
32
33
34
35
36
37
38
39
40
41

42

43
44
45


46

47

48
49
50


51

52

53

54

55

56
57


58

59

60

61
62


63
64
65
66
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46


47
48
49
50

51
52


53
54
55
56

57
58
59

60
61
62


63
64
65
66

67
68
69


70
71
72
73
74
75







+
+












+
-
+

-
-
+
+

+
-
+

-
-
+
+

+
-
+

+
-
+

+
-
-
+
+

+
-
+

+
-
-
+
+





@implementation TestsAppDelegate (OFDNSResolverTests)
- (void)DNSResolverTests
{
	void *pool = objc_autoreleasePoolPush();
	OFDNSResolver *resolver = [OFDNSResolver resolver];
	OFMutableString *staticHosts = [OFMutableString string];

	of_stdout.foregroundColor = [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",
	PRINT(GREEN, @"Static hosts: %@", staticHosts);
	    staticHosts];

	PRINT(GREEN, @"Name servers: %@",
	    [resolver.nameServers componentsJoinedByString: @", "]);
	[of_stdout writeFormat: @"[OFDNSResolver] Name servers: %@\n",
	    [resolver.nameServers componentsJoinedByString: @", "]];

	[of_stdout writeFormat: @"[OFDNSResolver] Local domain: %@\n",
	PRINT(GREEN, @"Local domain: %@", resolver.localDomain);
	    resolver.localDomain];

	PRINT(GREEN, @"Search domains: %@",
	    [resolver.searchDomains componentsJoinedByString: @", "]);
	[of_stdout writeFormat: @"[OFDNSResolver] Search domains: %@\n",
	    [resolver.searchDomains componentsJoinedByString: @", "]];

	[of_stdout writeFormat: @"[OFDNSResolver] Timeout: %lf\n",
	PRINT(GREEN, @"Timeout: %lf", resolver.timeout);
	    resolver.timeout];

	[of_stdout writeFormat: @"[OFDNSResolver] Max attempts: %u\n",
	PRINT(GREEN, @"Max attempts: %u", resolver.maxAttempts);
	    resolver.maxAttempts];

	[of_stdout writeFormat:
	PRINT(GREEN, @"Min number of dots in absolute name: %u",
	    resolver.minNumberOfDotsInAbsoluteName);
	    @"[OFDNSResolver] Min number of dots in absolute name: %u\n",
	    resolver.minNumberOfDotsInAbsoluteName];

	[of_stdout writeFormat: @"[OFDNSResolver] Uses TCP: %u\n",
	PRINT(GREEN, @"Uses TCP: %u", resolver.usesTCP);
	    module, resolver.usesTCP];

	[of_stdout writeFormat:
	PRINT(GREEN, @"Config reload interval: %lf",
	    resolver.configReloadInterval);
	    @"[OFDNSResolver] Config reload interval: %lf\n",
	    resolver.configReloadInterval];

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFDateTests.m from [e04db68127] to [3f9f2f2e15].

53
54
55
56
57
58
59
60
61
62
63






64
65
66
67
68
69
70






71
72
73
74
75
76
77
53
54
55
56
57
58
59




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

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







-
-
-
-
+
+
+
+
+
+






-
+
+
+
+
+
+







	    isEqual: @"2000-06-20T10:34:56Z"]);

	EXPECT_EXCEPTION(@"Detection of unparsed in "
	    @"+[dateWithDateString:format:]", OFInvalidFormatException,
	    [OFDate dateWithDateString: @"2000-06-20T12:34:56+0200x"
				format: @"%Y-%m-%dT%H:%M:%S%z"])

	/*
	 * We can only test local dates that specify a time zone, as the local
	 * time zone differs between systems.
	 */
	TEST(@"+[dateWithLocalDateString:format:]",
	    [[[OFDate dateWithLocalDateString: @"2000-06-20T12:34:56"
				       format: @"%Y-%m-%dT%H:%M:%S"]
	    localDateStringWithFormat: @"%Y-%m-%dT%H:%M:%S"]
	    isEqual: @"2000-06-20T12:34:56"]);

	TEST(@"+[dateWithLocalDateString:format:]",
	    [[[OFDate dateWithLocalDateString: @"2000-06-20T12:34:56-0200"
				       format: @"%Y-%m-%dT%H:%M:%S%z"]
	    description] isEqual: @"2000-06-20T14:34:56Z"]);

	EXPECT_EXCEPTION(@"Detection of unparsed in "
	    @"+[dateWithLocalDateString:format:]", OFInvalidFormatException,
	    @"+[dateWithLocalDateString:format:] #1", OFInvalidFormatException,
	    [OFDate dateWithLocalDateString: @"2000-06-20T12:34:56x"
				     format: @"%Y-%m-%dT%H:%M:%S"])

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

Added tests/OFIPXSocketTests.m version [7578ebb381].















































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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.foregroundColor = [OFColor lime];
			[of_stdout writeLine:
			    @"[OFIPXSocket] -[bindToPort:packetType:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			of_stdout.foregroundColor = [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

Modified tests/OFLocaleTests.m from [bc413d1703] to [bba7d2e3cd].

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



29

30

31

32
33
34


35

36

37
38
39
40
15
16
17
18
19
20
21


22
23
24
25
26
27
28
29

30
31
32

33
34


35
36
37
38

39
40
41
42
43







-
-





+
+
+
-
+

+
-
+

-
-
+
+

+
-
+




 * file.
 */

#include "config.h"

#import "TestsAppDelegate.h"

static OFString *module = @"OFLocale";

@implementation TestsAppDelegate (OFLocaleTests)
- (void)localeTests
{
	void *pool = objc_autoreleasePoolPush();

	of_stdout.foregroundColor = [OFColor lime];

	[of_stdout writeFormat: @"[OFLocale]: Language: %@\n",
	PRINT(GREEN, @"Language: %@", [OFLocale language]);
	    [OFLocale language]];

	[of_stdout writeFormat: @"[OFLocale]: Territory: %@\n",
	PRINT(GREEN, @"Territory: %@", [OFLocale territory]);
	    [OFLocale territory]];

	PRINT(GREEN, @"Encoding: %@",
	    of_string_name_of_encoding([OFLocale encoding]));
	[of_stdout writeFormat: @"[OFLocale]: Encoding: %@\n",
	    of_string_name_of_encoding([OFLocale encoding])];

	[of_stdout writeFormat: @"[OFLocale]: Decimal point: %@\n",
	PRINT(GREEN, @"Decimal point: %@", [OFLocale decimalPoint]);
	    [OFLocale decimalPoint]];

	objc_autoreleasePoolPop(pool);
}
@end

Added tests/OFSCTPSocketTests.m version [42f1cba28c].
















































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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.foregroundColor = [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

Added tests/OFSPXSocketTests.m version [6d42db054f].





















































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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.foregroundColor = [OFColor lime];
			[of_stdout writeLine:
			    @"[OFSPXSocket] -[bindToPort:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case ESOCKTNOSUPPORT:
			of_stdout.foregroundColor = [OFColor lime];
			[of_stdout writeLine:
			    @"[OFSPXSocket] -[bindToPort:]: "
			    @"SPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			of_stdout.foregroundColor = [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);

	[sockClient asyncConnectToNode: node
			       network: network
				  port: port];

	[[OFRunLoop mainRunLoop] runUntilDate:
	    [OFDate dateWithTimeIntervalSinceNow: 2]];

	TEST(@"-[asyncAccept] & -[asyncConnectToNode:network:port:]",
	    delegate->_accepted && delegate->_connected)

	objc_autoreleasePoolPop(pool);
}
@end

Added tests/OFSPXStreamSocketTests.m version [c4c06acd4d].

























































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 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.foregroundColor = [OFColor lime];
			[of_stdout writeLine:
			    @"[OFSPXStreamSocket] -[bindToPort:]: "
			    @"IPX unsupported, skipping tests"];
			break;
		case ESOCKTNOSUPPORT:
			of_stdout.foregroundColor = [OFColor lime];
			[of_stdout writeLine:
			    @"[OFSPXStreamSocket] -[bindToPort:]: "
			    @"SPX unsupported, skipping tests"];
			break;
		case EADDRNOTAVAIL:
			of_stdout.foregroundColor = [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);

	[sockClient asyncConnectToNode: node
			       network: network
				  port: port];

	[[OFRunLoop mainRunLoop] runUntilDate:
	    [OFDate dateWithTimeIntervalSinceNow: 2]];

	TEST(@"-[asyncAccept] & -[asyncConnectToNode:network:port:]",
	    delegate->_accepted && delegate->_connected)

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFSystemInfoTests.m from [fbcca99557] to [fe712afca4].

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



32

33

34

35

36

37
38
39


40
41
42


43
44
45


46

47
48


49
50
51
52
53
54
55

56

57
58
59
60
61
62

63

64
65

66

67

68

69
70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90
91
92


93
94
95

96

97
98
99
100
101
15
16
17
18
19
20
21


22
23
24
25
26
27
28
29
30
31
32

33
34
35

36
37
38

39
40


41
42
43


44
45
46


47
48
49
50


51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68

69
70
71
72

73
74
75

76
77
78
79

80
81
82

83
84
85

86
87
88

89
90
91

92
93
94

95
96
97

98
99
100

101
102
103

104
105
106

107
108


109
110
111
112
113
114

115
116
117
118
119
120







-
-








+
+
+
-
+

+
-
+

+
-
+

-
-
+
+

-
-
+
+

-
-
+
+

+
-
-
+
+







+
-
+






+
-
+


+
-
+

+
-
+


+
-
+

+
-
+

+
-
+

+
-
+

+
-
+

+
-
+

+
-
+

+
-
+

+
-
+

+
-
+

-
-
+
+



+
-
+





 * file.
 */

#include "config.h"

#import "TestsAppDelegate.h"

static OFString *module = @"OFSystemInfo";

@implementation TestsAppDelegate (OFSystemInfoTests)
- (void)systemInfoTests
{
	void *pool = objc_autoreleasePoolPush();
#ifdef OF_HAVE_FILES
	OFString *userConfigPath, *userDataPath;
#endif

	of_stdout.foregroundColor = [OFColor lime];

	[of_stdout writeFormat: @"[OFSystemInfo] Page size: %zd\n",
	PRINT(GREEN, @"Page size: %zd", [OFSystemInfo pageSize]);
	    [OFSystemInfo pageSize]];

	[of_stdout writeFormat: @"[OFSystemInfo] Number of CPUs: %zd\n",
	PRINT(GREEN, @"Number of CPUs: %zd", [OFSystemInfo numberOfCPUs]);
	    [OFSystemInfo numberOfCPUs]];

	[of_stdout writeFormat: @"[OFSystemInfo] ObjFW version: %@\n",
	PRINT(GREEN, @"ObjFW version: %@", [OFSystemInfo ObjFWVersion]);
	    [OFSystemInfo ObjFWVersion]];

	PRINT(GREEN, @"ObjFW version major: %u",
	    [OFSystemInfo ObjFWVersionMajor]);
	[of_stdout writeFormat: @"[OFSystemInfo] ObjFW version major: %u\n",
	    [OFSystemInfo ObjFWVersionMajor]];

	PRINT(GREEN, @"ObjFW version minor: %u",
	    [OFSystemInfo ObjFWVersionMinor]);
	[of_stdout writeFormat: @"[OFSystemInfo] ObjFW version minor: %u\n",
	    [OFSystemInfo ObjFWVersionMinor]];

	PRINT(GREEN, @"Operating system name: %@",
	    [OFSystemInfo operatingSystemName]);
	[of_stdout writeFormat: @"[OFSystemInfo] Operating system name: %@\n",
	    [OFSystemInfo operatingSystemName]];

	[of_stdout writeFormat:
	PRINT(GREEN, @"Operating system version: %@",
	    [OFSystemInfo operatingSystemVersion]);
	    @"[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",
	PRINT(GREEN, @"User config path: %@", userConfigPath);
	    userConfigPath];

	@try {
		userDataPath = [OFSystemInfo userDataPath];
	} @catch (OFNotImplementedException *e) {
		userDataPath = @"Not implemented";
	}
	[of_stdout writeFormat: @"[OFSystemInfo] User data path: %@\n",
	PRINT(GREEN, @"User data path: %@", userDataPath);
	    userDataPath];
#endif

	[of_stdout writeFormat: @"[OFSystemInfo] CPU vendor: %@\n",
	PRINT(GREEN, @"CPU vendor: %@", [OFSystemInfo CPUVendor]);
	    [OFSystemInfo CPUVendor]];

	[of_stdout writeFormat: @"[OFSystemInfo] CPU model: %@\n",
	PRINT(GREEN, @"CPU model: %@", [OFSystemInfo CPUModel]);
	    [OFSystemInfo CPUModel]];

#if defined(OF_X86_64) || defined(OF_X86)
	[of_stdout writeFormat: @"[OFSystemInfo] Supports MMX: %d\n",
	PRINT(GREEN, @"Supports MMX: %d", [OFSystemInfo supportsMMX]);
	    [OFSystemInfo supportsMMX]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE: %d\n",
	PRINT(GREEN, @"Supports SSE: %d", [OFSystemInfo supportsSSE]);
	    [OFSystemInfo supportsSSE]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE2: %d\n",
	PRINT(GREEN, @"Supports SSE2: %d", [OFSystemInfo supportsSSE2]);
	    [OFSystemInfo supportsSSE2]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE3: %d\n",
	PRINT(GREEN, @"Supports SSE3: %d", [OFSystemInfo supportsSSE3]);
	    [OFSystemInfo supportsSSE3]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSSE3: %d\n",
	PRINT(GREEN, @"Supports SSSE3: %d", [OFSystemInfo supportsSSSE3]);
	    [OFSystemInfo supportsSSSE3]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE4.1: %d\n",
	PRINT(GREEN, @"Supports SSE4.1: %d", [OFSystemInfo supportsSSE41]);
	    [OFSystemInfo supportsSSE41]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports SSE4.2: %d\n",
	PRINT(GREEN, @"Supports SSE4.2: %d", [OFSystemInfo supportsSSE42]);
	    [OFSystemInfo supportsSSE42]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports AVX: %d\n",
	PRINT(GREEN, @"Supports AVX: %d", [OFSystemInfo supportsAVX]);
	    [OFSystemInfo supportsAVX]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports AVX2: %d\n",
	PRINT(GREEN, @"Supports AVX2: %d", [OFSystemInfo supportsAVX2]);
	    [OFSystemInfo supportsAVX2]];

	[of_stdout writeFormat: @"[OFSystemInfo] Supports AES-NI: %d\n",
	PRINT(GREEN, @"Supports AES-NI: %d", [OFSystemInfo supportsAESNI]);
	    [OFSystemInfo supportsAESNI]];

	PRINT(GREEN, @"Supports SHA extensions: %d",
	    [OFSystemInfo supportsSHAExtensions]);
	[of_stdout writeFormat: @"[OFSystemInfo] Supports SHA extensions: %d\n",
	    [OFSystemInfo supportsSHAExtensions]];
#endif

#ifdef OF_POWERPC
	[of_stdout writeFormat: @"[OFSystemInfo] Supports AltiVec: %d\n",
	PRINT(GREEN, @"Supports AltiVec: %d", [OFSystemInfo supportsAltiVec]);
	    [OFSystemInfo supportsAltiVec]];
#endif

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/OFWindowsRegistryKeyTests.m from [d53759aab6] to [814a5cca52].

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

32
33
34
35
36
37
38







-







- (void)windowsRegistryKeyTests
{
	void *pool = objc_autoreleasePoolPush();
	OFData *data = [OFData dataWithItems: "abcdef"
				       count: 6];
	OFWindowsRegistryKey *softwareKey, *ObjFWKey;
	DWORD type;
	OFString *string;

	TEST(@"+[OFWindowsRegistryKey classesRootKey]",
	    [OFWindowsRegistryKey classesRootKey])

	TEST(@"+[OFWindowsRegistryKey currentConfigKey]",
	    [OFWindowsRegistryKey currentConfigKey])

60
61
62
63
64
65
66
67

68
69
70

71
72
73
74
75
76
77
78
79
80
81

82
83

84
85
86
87


88
89
90
91
92
93
94
95
96
97
98
99
100
59
60
61
62
63
64
65

66



67
68
69
70
71
72
73
74
75
76
77

78


79




80
81




82
83
84
85
86
87
88
89
90







-
+
-
-
-
+










-
+
-
-
+
-
-
-
-
+
+
-
-
-
-










	TEST(@"-[setData:forValue:type:]",
	    R([ObjFWKey setData: data
		       forValue: @"data"
			   type: REG_BINARY]))

	TEST(@"-[dataForValue:subkeyPath:flags:type:]",
	    [[softwareKey dataForValue: @"data"
	    [[ObjFWKey dataForValue: @"data"
			    subkeyPath: @"ObjFW"
				 flags: RRF_RT_REG_BINARY
				  type: &type] isEqual: 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:]",
	    [[softwareKey stringForValue: @"string"
	    [[ObjFWKey stringForValue: @"string"] isEqual: @"foobar"] &&
			      subkeyPath: @"ObjFW"] isEqual: @"foobar"] &&
	    [[softwareKey stringForValue: @"expand"
	    [[ObjFWKey stringForValue: @"expand"
			      subkeyPath: @"ObjFW"
				   flags: RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND
				    type: &type] isEqual: @"%PATH%;foo"] &&
	    type == REG_EXPAND_SZ &&
				 type: &type] isEqual: @"%PATH%;foo"] &&
	    type == REG_EXPAND_SZ)
	    (string = [ObjFWKey stringForValue: @"expand"
				    subkeyPath: nil]) &&
	    ![string isEqual: @"%PATH%;foo"] &&
	    [string hasSuffix: @";foo"])

	TEST(@"-[deleteValue:]", R([ObjFWKey deleteValue: @"data"]))

	TEST(@"-[deleteSubkeyAtPath:]",
	    R([softwareKey deleteSubkeyAtPath: @"ObjFW"]))

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/TestsAppDelegate.h from [d4258998b2] to [95a43bbf18].

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
50
51
52
53
54
55
56







57
58
59







60
61
62
63
64


65
66
67
68
69
70
71







-
-
-
-
-
-
-



-
-
-
-
-
-
-





-
-







		else {					\
			[self outputFailure: test	\
				   inModule: module];	\
			_fails++;			\
		}					\
	}
#define R(...) (__VA_ARGS__, 1)
#define PRINT(color, fmt, ...)					\
	{							\
		OFString *msg = [OFString stringWithFormat:	\
		    @"[%@] " fmt @"\n", module, __VA_ARGS__];	\
		[self outputString: msg				\
			   inColor: color];			\
	}

@class OFString;

enum {
	NO_COLOR,
	RED,
	GREEN,
	YELLOW
};

@interface TestsAppDelegate: OFObject <OFApplicationDelegate>
{
	int _fails;
}

- (void)outputString: (OFString *)str
	     inColor: (int)color;
- (void)outputTesting: (OFString *)test
	     inModule: (OFString *)module;
- (void)outputSuccess: (OFString *)test
	     inModule: (OFString *)module;
- (void)outputFailure: (OFString *)test
	     inModule: (OFString *)module;
@end
129
130
131
132
133
134
135




136
137
138
139
140
141
142
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130







+
+
+
+







@interface TestsAppDelegate (OFHTTPCookieManagerTests)
- (void)HTTPCookieManagerTests;
@end

@interface TestsAppDelegate (OFINIFileTests)
- (void)INIFileTests;
@end

@interface TestsAppDelegate (OFIPXSocketTests)
- (void)IPXSocketTests;
@end

@interface TestsAppDelegate (OFInvocationTests)
- (void)invocationTests;
@end

@interface TestsAppDelegate (OFJSONTests)
- (void)JSONTests;
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
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







-
-
-
-
-
-
-
-



















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







- (void)RIPEMD160HashTests;
@end

@interface TestsAppDelegate (ScryptTests)
- (void)scryptTests;
@end

@interface TestsAppDelegate (OFSerializationTests)
- (void)serializationTests;
@end

@interface TestsAppDelegate (OFSetTests)
- (void)setTests;
@end

@interface TestsAppDelegate (OFSHA1HashTests)
- (void)SHA1HashTests;
@end

@interface TestsAppDelegate (OFSHA224HashTests)
- (void)SHA224HashTests;
@end

@interface TestsAppDelegate (OFSHA256HashTests)
- (void)SHA256HashTests;
@end

@interface TestsAppDelegate (OFSHA384HashTests)
- (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

@interface TestsAppDelegate (OFSerializationTests)
- (void)serializationTests;
@end

@interface TestsAppDelegate (OFSetTests)
- (void)setTests;
@end

@interface TestsAppDelegate (OFSystemInfoTests)
- (void)systemInfoTests;
@end

@interface TestsAppDelegate (OFHMACTests)
- (void)HMACTests;

Modified tests/TestsAppDelegate.m from [8cfd87b118] to [ab72b7da5d].

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






24
25
26
27
28
29
30







-
-
-
-
-
-








#include "config.h"

#include <stdlib.h>

#import "TestsAppDelegate.h"

#if defined(STDOUT) && (defined(OF_WINDOWS) || defined(OF_MSDOS) || \
    defined(OF_IOS))
# undef STDOUT
# define STDOUT_SIMPLE
#endif

#ifdef OF_IOS
# include <CoreFoundation/CoreFoundation.h>
#endif

#ifdef OF_PSP
# include <pspmoduleinfo.h>
# include <pspkernel.h>
143
144
145
146
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
137
138
139
140
141
142
143


144
145
146
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







-
-






+
-
+
-
-
-
+
+

+
-
-
+
+











+
-
-
+
+







+
-
-
+
+



















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



-
-
-
+
+
+
-
-
-
+
-
-
+
-
-
-





-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
-
-





-
-
-
+
+
+
+
+
-
-
-

-
-
-
-
-
+
+
+
+
+
+

-
-
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-








#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) {
		TestsAppDelegate *delegate =
		    [OFApplication sharedApplication].delegate;
		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.foregroundColor = [OFColor red];
		[delegate outputString: string
		[of_stdout writeString: string];
			       inColor: RED];
		[delegate outputString: backtrace
			       inColor: RED];
		[of_stdout writeString: backtrace];

# if defined(OF_WII)
		[of_stdout reset];
		[delegate outputString: @"Press home button to exit!\n"
			       inColor: NO_COLOR];
		[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];
		[delegate outputString: @"Press start button to exit!"
			       inColor: NO_COLOR];
		[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];
		[delegate outputString: @"Press start button to exit!"
			       inColor: NO_COLOR];
		[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)outputString: (OFString *)str
	     inColor: (int)color
{
#if defined(OF_PSP)
	char space = ' ';
	int y = pspDebugScreenGetY();

	pspDebugScreenSetXY(0, y);
	for (uint8_t i = 0; i < 68; i++)
		pspDebugScreenPrintData(&space, 1);

	switch (color) {
	case NO_COLOR:
		pspDebugScreenSetTextColor(0xFFFFFF);
		break;
	case RED:
		pspDebugScreenSetTextColor(0x0000FF);
		break;
	case GREEN:
		pspDebugScreenSetTextColor(0x00FF00);
		break;
	case YELLOW:
		pspDebugScreenSetTextColor(0x00FFFF);
		break;
	}

	pspDebugScreenSetXY(0, y);
	pspDebugScreenPrintData(str.UTF8String, str.UTF8StringLength);
#elif defined(STDOUT)
	switch (color) {
	case NO_COLOR:
		[of_stdout writeString: @"\r\033[K"];
# if defined(OF_WII) || defined(OF_NINTENDO_DS)
		[of_stdout writeString: @"\033[37m"];
# endif
		break;
	case RED:
		[of_stdout writeString: @"\r\033[K\033[31;1m"];
		break;
	case GREEN:
		[of_stdout writeString: @"\r\033[K\033[32;1m"];
		break;
	case YELLOW:
		[of_stdout writeString: @"\r\033[K\033[33;1m"];
		break;
	}

	[of_stdout writeString: str];
	[of_stdout writeString: @"\033[m"];
#elif defined(STDOUT_SIMPLE)
	[of_stdout writeString: str];
#else
# error No output method!
#endif
}

- (void)outputTesting: (OFString *)test
	     inModule: (OFString *)module
{
	void *pool = objc_autoreleasePoolPush();
#ifndef STDOUT_SIMPLE
	[self outputString: [OFString stringWithFormat: @"[%@] %@: testing...",
	if (of_stdout.hasTerminal) {
		of_stdout.foregroundColor = [OFColor yellow];
		[of_stdout writeFormat: @"[%@] %@: testing...", module, test];
							module, test]
		   inColor: YELLOW];
#else
	} else
	[self outputString: [OFString stringWithFormat: @"[%@] %@: ",
							module, test]
		[of_stdout writeFormat: @"[%@] %@: ", module, test];
		   inColor: YELLOW];
#endif
	objc_autoreleasePoolPop(pool);
}

- (void)outputSuccess: (OFString *)test
	     inModule: (OFString *)module
{
#ifndef STDOUT_SIMPLE
	void *pool = objc_autoreleasePoolPush();
	[self outputString: [OFString stringWithFormat: @"[%@] %@: ok\n",
							module, test]
	if (of_stdout.hasTerminal) {
		of_stdout.cursorColumn = 0;
		of_stdout.foregroundColor = [OFColor lime];
		[of_stdout eraseLine];
		[of_stdout writeFormat: @"[%@] %@: ok\n", module, test];
		   inColor: GREEN];
	objc_autoreleasePoolPop(pool);
#else
	[self outputString: @"ok\n"
	} else
		[of_stdout writeLine: @"ok"];
		   inColor: GREEN];
#endif
}

- (void)outputFailure: (OFString *)test
	     inModule: (OFString *)module
{
#ifndef STDOUT_SIMPLE
	void *pool = objc_autoreleasePoolPush();
	[self outputString: [OFString stringWithFormat: @"[%@] %@: failed\n",
	if (of_stdout.hasTerminal) {
		of_stdout.cursorColumn = 0;
		of_stdout.foregroundColor = [OFColor red];
		[of_stdout eraseLine];
		[of_stdout writeFormat: @"[%@] %@: failed\n", module, test];
							module, test]
		   inColor: RED];
	objc_autoreleasePoolPop(pool);

# ifdef OF_WII
	[self outputString: @"Press A to continue!\n"
		   inColor: NO_COLOR];
	for (;;) {
		WPAD_ScanPads();
#ifdef OF_WII
		[of_stdout reset];
		[of_stdout writeLine: @"Press A to continue!"];

		for (;;) {
			WPAD_ScanPads();

		if (WPAD_ButtonsDown(0) & WPAD_BUTTON_A)
			return;
			if (WPAD_ButtonsDown(0) & WPAD_BUTTON_A)
				return;

		VIDEO_WaitVSync();
	}
# endif
# ifdef OF_PSP
	[self outputString: @"Press X to continue!\n"
		   inColor: NO_COLOR];
	for (;;) {
		SceCtrlData pad;
			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
	[self outputString: @"Press A to continue!"
		   inColor: NO_COLOR];
	for (;;) {
		swiWaitForVBlank();
		scanKeys();
		if (keysDown() & KEY_A)
			break;
	}
# endif
# ifdef OF_NINTENDO_3DS
	[self outputString: @"Press A to continue!"
		   inColor: NO_COLOR];
	for (;;) {
		hidScanInput();
			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;
			if (hidKeysDown() & KEY_A)
				break;

		gspWaitForVBlank();
	}
# endif
#else
	[self outputString: @"failed\n"
			gspWaitForVBlank();
		}
#endif

		of_stdout.cursorColumn = 0;
		[of_stdout reset];
		[of_stdout eraseLine];
	} else
		[of_stdout writeLine: @"failed"];
		   inColor: RED];
#endif
}

- (void)applicationDidFinishLaunching
{
#if defined(OF_IOS) && defined(OF_HAVE_FILES)
	CFBundleRef mainBundle = CFBundleGetMainBundle();
	CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
411
412
413
414
415
416
417








418
419
420
421
422
423
424
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367







+
+
+
+
+
+
+
+







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







+
+

-
+
-
-


-
-
+
+









-
-
+
+
-


-
-
+
+







-
-
+
+














#ifdef OF_HAVE_SOCKETS
	[self DNSResolverTests];
#endif
	[self systemInfoTests];
	[self localeTests];

	[of_stdout reset];

#if defined(OF_IOS)
	[self outputString: [OFString stringWithFormat: @"%d tests failed!",
	[of_stdout writeFormat: @"%d tests failed!", _fails];
							_fails]
		   inColor: NO_COLOR];
	[OFApplication terminateWithStatus: _fails];
#elif defined(OF_WII)
	[self outputString: @"Press home button to exit!\n"
		   inColor: NO_COLOR];
	[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)
	[self outputString: [OFString stringWithFormat: @"%d tests failed!",
							_fails]
	[of_stdout writeFormat: @"%d tests failed!", _fails];

		   inColor: NO_COLOR];
	sceKernelSleepThreadCB();
#elif defined(OF_NINTENDO_DS)
	[self outputString: @"Press start button to exit!"
		   inColor: NO_COLOR];
	[of_stdout writeString: @"Press start button to exit!"];

	for (;;) {
		swiWaitForVBlank();
		scanKeys();
		if (keysDown() & KEY_START)
			[OFApplication terminateWithStatus: _fails];
	}
#elif defined(OF_NINTENDO_3DS)
	[self outputString: @"Press start button to exit!"
		   inColor: NO_COLOR];
	[of_stdout writeString: @"Press start button to exit!"];

	for (;;) {
		hidScanInput();

		if (hidKeysDown() & KEY_START)
			[OFApplication terminateWithStatus: _fails];

		gspWaitForVBlank();
	}
#else
	[OFApplication terminateWithStatus: _fails];
#endif
}
@end

Modified tests/objc_sync/Makefile from [b99d2a03c8] to [050b215f54].

1
2
3
4
5
6
7
8
9
10
11
12
13
14

15
16
17


18
19
20
21
22
23
24
25
26
27


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42


43
44
45
46




47
48
49
50
51
52
53

54
55
56

57
58
59
60
61


62
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16

17
18
19
20
21
22
23
24
25
26


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41


42
43
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 libobjfw.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib
	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 libobjfwrt.dll libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib
	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/libobjfw.dll; then \
		${LN_S} ../../src/libobjfw.dll libobjfw.dll; \
	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/libobjfwrt.dll; then \
		${LN_S} ../../src/runtime/libobjfwrt.dll libobjfwrt.dll; \
	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 libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} libobjfw.dll; \
	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 libobjfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} libobjfwrt.dll; \
	rm -f objfwrt.so.${OBJFWRT_LIB_MAJOR_MINOR} objfwrt.dll; \
	rm -f libobjfwrt.${OBJFWRT_LIB_MAJOR}.dylib; \
	exit $$EXIT

CPPFLAGS += -I../../src/runtime -I../../src -I../..
LIBS := -L../../src -lobjfw ${LIBS}
CPPFLAGS += -I../../src -I../../src/runtime -I../..
LIBS := -L../../src -lobjfw -L../../src/runtime ${RUNTIME_LIBS} ${LIBS}
LD = ${OBJC}

Modified tests/plugin/TestPlugin.m from [fd38c4331d] to [78de8ec89d].

20
21
22
23
24
25
26











27

28
29
30
31
32
33
34
20
21
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 "TestPlugin.h"

#ifdef OF_OBJFW_RUNTIME
# import "runtime/private.h"

OF_DESTRUCTOR()
{
	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(objc_getClass("TestPlugin"));
	objc_unregister_class(class);
}
#endif

@implementation TestPlugin
- (int)test: (int)num
{
	return num * 2;

Added tests/terminal/Makefile version [58c564f0ae].




































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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}

Added tests/terminal/TerminalTests.m version [3e135bdea5].


























































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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 "OFApplication.h"
#import "OFArray.h"
#import "OFColor.h"
#import "OFStdIOStream.h"
#import "OFThread.h"

@interface TerminalTests: OFObject <OFApplicationDelegate>
@end

OF_APPLICATION_DELEGATE(TerminalTests)

@implementation TerminalTests
- (void)applicationDidFinishLaunching
{
	OFArray *colors = [OFArray arrayWithObjects:
	    [OFColor black], [OFColor silver], [OFColor grey], [OFColor white],
	    [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.foregroundColor = color;
		[of_stdout writeFormat: @"%zx", i++];
	}
	[of_stdout reset];
	[of_stdout writeLine: @"R"];

	i = 0;
	for (OFColor *color in colors) {
		of_stdout.backgroundColor = 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.foregroundColor = color;
		of_stdout.backgroundColor = [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.backgroundColor = [colors objectAtIndex:
			    ((i / 2) + 2) % colors.count];
		else
			of_stdout.foregroundColor =
			    [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.backgroundColor = [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.cursorPosition = 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.cursorColumn = 2;
	[OFThread sleepForTimeInterval: 2];

	[of_stdout reset];

	[OFApplication terminate];
}
@end

Modified utils/Makefile from [521b7f89ce] to [82be3d0820].

1
2
3
4
5
6

7
8
9
10
11
12
13
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

Modified utils/completions/fish/Makefile from [00dfed2b56] to [6569e87046].

1
2
3

4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
10



+






DATA = objfw-compile.fish	\
       objfw-config.fish	\
       ofarc.fish		\
       ofdns.fish		\
       ofhash.fish		\
       ofhttp.fish

include ../../../buildsys.mk

PACKAGE_NAME = fish/vendor_completions.d

Added utils/completions/fish/ofdns.fish version [5080d1ffbb].






1
2
3
4
5
+
+
+
+
+
complete -c ofdns -s c -l class -x -d 'The DNS class to query (defaults to IN)'
complete -c ofdns -s h -l help -d 'Show help'
complete -c ofdns -s s -l server -x -d 'The server to query'
complete -c ofdns -s t -l type -x \
    -d 'The record type to query (defaults to ALL, can be repeated)'

Modified utils/completions/fish/ofhttp.fish from [da383b0d98] to [99fd9b7109].


1
2
3
4
5
6
7
1
2
3
4
5
6
7
8
+







complete -c ofhttp -x
complete -c ofhttp -s b -l body -r -d 'Specify the file to send as body'
complete -c ofhttp -s c -l continue -d 'Continue download of existing file'
complete -c ofhttp -s f -l force -d 'Force / overwrite existing file'
complete -c ofhttp -s h -l help -d 'Show help'
complete -c ofhttp -s H -l header -x -d 'Add a header (e.g. X-Foo:Bar)'
complete -c ofhttp -s m -l method -x -d 'Set the method of the HTTP request'
complete -c ofhttp -s o -l output -r -d 'Specify output file name'

Modified utils/ofarc/LHAArchive.m from [ac9c3a4448] to [8c23d6ffc7].

117
118
119
120
121
122
123

124






125
126
127
128

129






130
131
132
133
134
135
136
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
144
145
146
147
148







+
-
+
+
+
+
+
+




+
-
+
+
+
+
+
+







			    @"%" 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] bytes",
			     @"    'Compressed: ',"
			     @"    ["
			     @"        {'size == 1': '1 byte'},"
			     @"        {'': '%[size] bytes'}"
			     @"    ]"
			     @"]" JSONValue],
			    @"size", compressedSize)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_uncompressed_size",
			    [@"["
			    @"Uncompressed: %[size] bytes",
			     @"    'Uncompressed: ',"
			     @"    ["
			     @"        {'size == 1': '1 byte'},"
			     @"        {'': '%[size] bytes'}"
			     @"    ]"
			     @"]" JSONValue],
			    @"size", uncompressedSize)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_compression_method",
			    @"Compression method: %[method]",
			    @"method", entry.compressionMethod)];
			[of_stdout writeString: @"\t"];

Modified utils/ofarc/OFArc.m from [656314f25a] to [f2fa0ce091].

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
33
34
35
36
37
38
39

40
41
42
43
44
45
46







-







#import "GZIPArchive.h"
#import "LHAArchive.h"
#import "TarArchive.h"
#import "ZIPArchive.h"

#import "OFCreateDirectoryFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFOpenItemFailedException.h"
#import "OFReadFailedException.h"
#import "OFSeekFailedException.h"
#import "OFWriteFailedException.h"

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







-
+
















-


+







	    @"Writing archives of type %[type] is not (yet) supported!",
	    @"type", type)];
}

@implementation OFArc
- (void)applicationDidFinishLaunching
{
	OFString *outputDir = nil, *encodingString = nil, *type = nil;
	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 }
	};
	OFOptionsParser *optionsParser;
	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;
289
290
291
292
293
294
295

296
297
298
299
300
301
302

303
304
305
306
307
308
309
288
289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308
309







+






-
+







				    @"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 (OFInvalidEncodingException *e) {
	} @catch (OFInvalidArgumentException *e) {
		[of_stderr writeLine: OF_LOCALIZED(
		    @"invalid_encoding",
		    @"%[prog]: Invalid encoding: %[encoding]",
		    @"prog", [OFApplication programName],
		    @"encoding", encodingString)];

		[OFApplication terminateWithStatus: 1];
669
670
671
672
673
674
675

676

677
678
679
680
681
682
683
669
670
671
672
673
674
675
676

677
678
679
680
681
682
683
684







+
-
+







		_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;
		return false;
	}

	if (_outputLevel >= 0)
		[of_stdout writeString: OF_LOCALIZED(@"extracting_file",
		    @"Extracting %[file]...",
		    @"file", fileName)];

Modified utils/ofarc/TarArchive.m from [5bec331bce] to [ce61d629aa].

106
107
108
109
110
111
112




113



114
115
116
117
118
119
120
106
107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124
125
126







+
+
+
+
-
+
+
+







			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: %[size] bytes",
			     @"        {'': '%[size] bytes'}"
			     @"    ]"
			     @"]" JSONValue],
			    @"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",

Modified utils/ofarc/ZIPArchive.m from [2678f9aad0] to [f7e021bab7].

118
119
120
121
122
123
124

125






126
127
128
129

130






131
132
133
134
135
136
137
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
147
148
149







+
-
+
+
+
+
+
+




+
-
+
+
+
+
+
+







			    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] bytes",
			     @"    'Compressed: ',"
			     @"    ["
			     @"        {'size == 1': '1 byte'},"
			     @"        {'': '%[size] bytes'}"
			     @"    ]"
			     @"]" JSONValue],
			    @"size", compressedSize)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_uncompressed_size",
			    [@"["
			    @"Uncompressed: %[size] bytes",
			     @"    'Uncompressed: ',"
			     @"    ["
			     @"        {'size == 1': '1 byte'},"
			     @"        {'': '%[size] bytes'}"
			     @"    ]"
			     @"]" JSONValue],
			    @"size", uncompressedSize)];
			[of_stdout writeString: @"\t"];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"list_compression_method",
			    @"Compression method: %[method]",
			    @"method", compressionMethod)];
			[of_stdout writeString: @"\t"];

Modified utils/ofarc/lang/de.json from [277b513dff] to [d777ea4bdf].

64
65
66
67
68
69
70
71







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90














91
92
93
94
95
96
97
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115







-
+
+
+
+
+
+
+

















-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    "cannot_list_gz": "Kann Dateien eines .gz-Archivs nicht auflisten!",
    "cannot_extract_specific_file_from_gz": [
        "Kann keine spezifische Datei aus einem .gz-Archiv entpacken!"
    ],
    "cannot_print_specific_file_from_gz": [
        "Kann keine spezifische Datei aus einem .gz-Archiv ausgeben!"
    ],
    "list_size": "Größe: %[size] Bytes",
    "list_size": [
        "Größe: ",
        [
            {"size == 1": "1 Byte"},
            {"": "%[size] Bytes"}
        ]
    ],
    "list_mode": "Modus: %[mode]",
    "list_owner": "Besitzer: %[owner]",
    "list_group": "Gruppe: %[group]",
    "list_header_level": "Header-Level: %[level]",
    "list_modification_date": "Änderungsdatum: %[date]",
    "list_type_normal": "Typ: Normale Datei",
    "list_type_hardlink": "Typ: Harter Link",
    "list_type_symlink": "Typ: Symbolischer Link",
    "list_link_target": "Zieldateiname: %[target]",
    "list_type_character_device": "Typ: Zeichenorientiertes Gerät",
    "list_type_block_device": "Typ: Blockorientiertes Gerät",
    "list_device_major": "Major-Nummer des Geräts: %[major]",
    "list_device_minor": "Minor-Nummer des Geräts: %[minor]",
    "list_type_directory": "Typ: Verzeichnis",
    "list_type_fifo": "Typ: FIFO",
    "list_type_contiguous_file": "Typ: Zusammenhängende Datei",
    "list_type_unknown": "Typ: Unbekannt",
    "list_compressed_size": "Komprimierte Größe: %[size] Bytes",
    "list_uncompressed_size": "Unkomprimierte Größe: %[size] Bytes",
    "list_compressed_size": [
        "Komprimierte Größe: ",
        [
            {"size == 1": "1 Byte"},
            {"": "%[size] Bytes"}
        ]
    ],
    "list_uncompressed_size": [
        "Unkomprimierte Größe: ",
        [
            {"size == 1": "1 Byte"},
            {"": "%[size] Bytes"}
        ]
    ],
    "list_compression_method": "Kompressionsmethode: %[method]",
    "list_date": "Datum: %[date]",
    "list_osid": "Betriebssystem-Identifikator: %[osid]",
    "list_extensions": "Erweiterungen: %[extensions]",
    "list_version_made_by": "Erstellt mit Version: %[version]",
    "list_min_version_needed": "Mindestens benötigte Version: %[version]",
    "list_general_purpose_bit_flag": "General Purpose Bit Flag: %[gpbf]",

Modified utils/ofdns/Makefile from [3c6086809b] to [c6c2ac1b61].

1
2
3
4


5
6
7
8
9
10
11
12
13
14
15


16
17
18
19
20
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

PROG = ofdns${PROG_SUFFIX}
SRCS = OFDNS.m
DATA = lang/de.json		\
       lang/languages.json

include ../../buildsys.mk

PACKAGE_NAME = ofdns

${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2}

CPPFLAGS += -I../../src					\
	    -I../../src/runtime				\
	    -I../../src/exceptions			\
	    -I../..
	    -I../..					\
	    -DLANGUAGE_DIR=\"${datadir}/ofdns/lang\"
LIBS := -L../../src -lobjfw						\
	-L../../src/runtime -L../../src/runtime/linklib ${RUNTIME_LIBS}	\
	${LIBS}
LD = ${OBJC}
LDFLAGS += ${LDFLAGS_RPATH}

Modified utils/ofdns/OFDNS.m from [e5a880b500] to [7cbfa15818].

16
17
18
19
20
21
22


23
24
25
26




27
28
29


























30
31
32
33
34
35
36


37
38
39








40
41
42
43
44


45
46
47
48











49
50
51
52
53











54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72



























































73
74
75

76
77

78

79
80
81


82
83

84
85

86
87
88





89
90
91
92
93
94










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



71
72
73
74
75
76
77
78
79
80



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97





98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122






123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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







+
+




+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
-
-
-
+
+
+
+
+
+
+
+


-
-
-
+
+




+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+













+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+
-
-
+
-
+

-
-
+
+

-
+

-
+
-


+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

 */

#include "config.h"

#import "OFApplication.h"
#import "OFArray.h"
#import "OFDNSResolver.h"
#import "OFLocale.h"
#import "OFOptionsParser.h"
#import "OFSandbox.h"
#import "OFStdIOStream.h"

@interface OFDNS: OFObject <OFApplicationDelegate, OFDNSResolverQueryDelegate>
{
	size_t _inFlight;
	int _errors;
}
@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",
		    @"Options:\n    "
		    @"-c  --class "
		    @"  The DNS class to query (defaults to IN)\n    "
		    @"-h  --help  "
		    @"  Show this help\n    "
		    @"-s  --server"
		    @"  The server to query\n    "
		    @"-t  --type  "
		    @"  The record type to query (defaults to ALL, can be "
		    @"repeated)")];
	}

	[OFApplication terminateWithStatus: status];
}

@implementation OFDNS
-  (void)resolver: (OFDNSResolver *)resolver
  didPerformQuery: (OFDNSQuery *)query
	 response: (OFDNSResponse *)response
	exception: (id)exception
{
	_inFlight--;

	if (exception != nil) {
		[of_stderr writeFormat: @"Failed to resolve: %@\n", exception];
		[OFApplication terminateWithStatus: 1];
	if (exception == nil)
		[of_stdout writeFormat: @"%@\n", response];
	else {
		[of_stderr writeLine: OF_LOCALIZED(
		    @"failed_to_resolve",
		    @"Failed to resolve: %[exception]",
		    @"exception", exception)];
		_errors++;
	}

	[of_stdout writeFormat: @"%@\n", response];

	[OFApplication terminate];
	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 *) *arguments = [OFApplication arguments];
	of_dns_class_t DNSClass = OF_DNS_CLASS_ANY;
	of_dns_record_type_t recordType = OF_DNS_RECORD_TYPE_ALL;
	OFDNSQuery *query;
	OFDNSResolver *resolver;
	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];
	if (arguments.count < 1 || arguments.count > 4) {
		[of_stderr writeFormat:
		    @"Usage: %@ host [type [class [server]]]\n",
		    [OFApplication programName]];
		[OFApplication terminateWithStatus: 1];
	}

	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
	if (arguments.count >= 2)
		recordType = of_dns_record_type_parse(
	    ? of_dns_class_parse(DNSClassString)
		    [arguments objectAtIndex: 1]);
	    : OF_DNS_CLASS_IN);

	if (arguments.count >= 3)
		DNSClass = of_dns_class_parse([arguments objectAtIndex: 2]);
	if (recordTypes.count == 0)
		[recordTypes addObject: @"ALL"];

	if (arguments.count >= 4) {
	if (server != nil) {
		resolver.configReloadInterval = 0;
		resolver.nameServers =
		resolver.nameServers = [OFArray arrayWithObject: server];
		    [arguments objectsInRange: of_range(3, 1)];
	}

	for (OFString *domainName in remainingArguments) {
		for (OFString *recordTypeString in recordTypes) {
			of_dns_record_type_t recordType =
			    of_dns_record_type_parse(recordTypeString);
			OFDNSQuery *query =
	query = [OFDNSQuery queryWithDomainName: [arguments objectAtIndex: 0]
				       DNSClass: DNSClass
				     recordType: recordType];
	[resolver asyncPerformQuery: query
			   delegate: self];
}
			    [OFDNSQuery queryWithDomainName: domainName
						   DNSClass: DNSClass
						 recordType: recordType];

			_inFlight++;
			[resolver asyncPerformQuery: query
					   delegate: self];
		}
	}
}
@end

Added utils/ofdns/lang/de.json version [a2dd6e542b].



















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
    "usage": "Benutzung: %[prog] -[chst] domain1 [domain2 ...]",
    "full_usage": [
        "Optionen:\n",
        "    -c  --class   Die anzufragende DNS-Klasse (standardmäßig IN)\n",
        "    -h  --help    Diese Hilfe anzeigen\n",
        "    -s  --server  Der abzufragende Server\n",
        "    -t  --type    Der anzufragende Record-Typ (standardmäßig ALL,\n",
        "                  kann wiederholt werden)"
    ],
    "long_option_requires_argument": [
        "%[prog]: Option --%[opt] benötigt ein Argument"
    ],
    "option_requires_argument": "%[prog]: Option -%[opt] benötigt ein Argument",
    "unknown_long_option": "%[prog]: Unbekannte Option: --%[opt]",
    "unknown_option": "%[prog]: Unbekannte Option: -%[opt]",
    "failed_to_resolve": "Auflösen fehlgeschlagen: %[exception]"
}

Added utils/ofdns/lang/languages.json version [1722bb6119].












1
2
3
4
5
6
7
8
9
10
11
+
+
+
+
+
+
+
+
+
+
+
{
    "de": {
        "": "de"
    },
    "deutsch": {
        "": "de"
    },
    "german": {
        "": "de"
    }
}

Modified utils/ofhttp/OFHTTP.m from [fb786804a8] to [da5c2f66e5].

33
34
35
36
37
38
39

40
41
42
43
44
45
46
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47







+







#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"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFResolveHostFailedException.h"
#import "OFRetrieveItemAttributesFailedException.h"
87
88
89
90
91
92
93


94
95
96
97
98
99
100
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103







+
+








	if (full) {
		[stream writeString: @"\n"];
		[stream writeLine: OF_LOCALIZED(@"full_usage",
		    @"Options:\n    "
		    @"-b  --body           "
		    @"  Specify the file to send as body\n    "
		    @"                     "
		    @"  (- for standard input)\n    "
		    @"-c  --continue       "
		    @"  Continue download of existing file\n    "
		    @"-f  --force          "
		    @"  Force / overwrite existing file\n    "
		    @"-h  --help           "
		    @"  Show this help\n    "
		    @"-H  --header         "
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
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







-
+


+
+
+
+
+
-
-
+
+

+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-








	[_clientHeaders setObject: value
			   forKey: name];
}

- (void)setBody: (OFString *)path
{
	uintmax_t bodySize;
	OFString *contentLength = nil;

	[_body release];
	_body = nil;

	if ([path isEqual: @"-"])
		_body = [of_stdin copy];
	else {
	_body = [[OFFile alloc] initWithPath: path
					mode: @"r"];
		_body = [[OFFile alloc] initWithPath: path
						mode: @"r"];

		@try {
	bodySize = [[OFFileManager defaultManager] attributesOfItemAtPath: path]
	    .fileSize;
	[_clientHeaders setObject: [OFString stringWithFormat: @"%ju", bodySize]
			   forKey: @"Content-Length"];
			uintmax_t fileSize = [[OFFileManager defaultManager]
			    attributesOfItemAtPath: path].fileSize;

			contentLength =
			    [OFString stringWithFormat: @"%ju", fileSize];
			[_clientHeaders setObject: contentLength
					   forKey: @"Content-Length"];
		} @catch (OFRetrieveItemAttributesFailedException *e) {
		}
	}

	if (contentLength == nil)
		[_clientHeaders setObject: @"chunked"
				   forKey: @"Transfer-Encoding"];
}

- (void)setMethod: (OFString *)method
{
	void *pool = objc_autoreleasePoolPush();

	method = method.uppercaseString;

	if ([method isEqual: @"GET"])
		_method = OF_HTTP_REQUEST_METHOD_GET;
	else if ([method isEqual: @"HEAD"])
	@try {
		_method = of_http_request_method_from_string(method);
	} @catch (OFInvalidArgumentException *e) {
		_method = OF_HTTP_REQUEST_METHOD_HEAD;
	else if ([method isEqual: @"POST"])
		_method = OF_HTTP_REQUEST_METHOD_POST;
	else if ([method isEqual: @"PUT"])
		_method = OF_HTTP_REQUEST_METHOD_PUT;
	else if ([method isEqual: @"DELETE"])
		_method = OF_HTTP_REQUEST_METHOD_DELETE;
	else if ([method isEqual: @"TRACE"])
		_method = OF_HTTP_REQUEST_METHOD_TRACE;
	else {
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_input_method",
		    @"%[prog]: Invalid request method %[method]!",
		    @"prog", [OFApplication programName],
		    @"method", method)];
		[OFApplication terminateWithStatus: 1];
	}

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







-
+










-
-
+
+







		    @"%[prog]: Cannot use -o / --output when more than one URL "
		    @"has been specified!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_insecure)
		_HTTPClient.insecureRedirectsAllowed = true;
		_HTTPClient.allowsInsecureRedirects = true;

	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}

-    (void)client: (OFHTTPClient *)client
  didCreateSocket: (OFTCPSocket *)sock
	  request: (OFHTTPRequest *)request
{
	if (_insecure && [sock respondsToSelector:
	    @selector(setCertificateVerificationEnabled:)])
		((id <OFTLSSocket>)sock).certificateVerificationEnabled = false;
	    @selector(setVerifiesCertificates:)])
		((id <OFTLSSocket>)sock).verifiesCertificates = false;
}

-     (void)client: (OFHTTPClient *)client
  wantsRequestBody: (OFStream *)body
	   request: (OFHTTPRequest *)request
{
	/* TODO: Do asynchronously and print status */
678
679
680
681
682
683
684



685
686


687
688


689
690
691
692
693
694
695
687
688
689
690
691
692
693
694
695
696
697

698
699
700

701
702
703
704
705
706
707
708
709







+
+
+

-
+
+

-
+
+







		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  %[error]: %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", request.URL.string,
		    @"error", error,
		    @"exception", e)];
	} else if ([e isKindOfClass: [OFHTTPRequestFailedException class]]) {
		short statusCode = [[e response] statusCode];
		OFString *codeString = [OFString stringWithFormat: @"%d %@",
		    statusCode, of_http_status_code_to_string(statusCode)];
		[of_stderr writeLine: OF_LOCALIZED(@"download_failed",
		    @"%[prog]: Failed to download <%[url]>!",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  HTTP status code: %[code]",
		    @"prog", [OFApplication programName],
		    @"url", request.URL.string)];
		    @"url", request.URL.string,
		    @"code", codeString)];
	} else
		@throw e;

	_errorCode = 1;
	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}
709
710
711
712
713
714
715
716


717
718
719
720
721
722
723
723
724
725
726
727
728
729

730
731
732
733
734
735
736
737
738







-
+
+








		if (!_quiet)
			[of_stdout writeString: @"\n  Error!\n"];

		URL = [_URLs objectAtIndex: _URLIndex - 1];
		[of_stderr writeLine: OF_LOCALIZED(
		    @"download_failed_exception",
		    @"%[prog]: Failed to download <%[url]>: %[exception]",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", URL,
		    @"exception", exception)];

		_errorCode = 1;
		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
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







+
+
+
-
+
+
+







				lengthString = OF_LOCALIZED(@"size_kib",
				    @"%[num] KiB",
				    @"num", lengthString);
			} else {
				lengthString = [OFString stringWithFormat:
				    @"%jd", _resumedFrom + _length];
				lengthString = OF_LOCALIZED(@"size_bytes",
				    [@"["
				     @"    ["
				     @"        {'num == 1': '1 byte'},"
				    @"%[num] bytes",
				     @"        {'': '%[num] bytes'}"
				     @"    ]"
				     @"]" JSONValue],
				    @"num", lengthString);
			}
		} else
			lengthString =
			    OF_LOCALIZED(@"size_unknown", @"unknown");

		if (_verbose) {
843
844
845
846
847
848
849



850
851
852
853
854
855
856
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879







+
+
+







	}
}

-      (void)client: (OFHTTPClient *)client
  didPerformRequest: (OFHTTPRequest *)request
	   response: (OFHTTPResponse *)response
{
	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 */

Modified utils/ofhttp/ProgressBar.m from [cdabadff8c] to [813833df71].

200
201
202
203
204
205
206



207



208
209
210
211
212
213
214
200
201
202
203
204
205
206
207
208
209

210
211
212
213
214
215
216
217
218
219







+
+
+
-
+
+
+







		[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",
		     @"        {'': '%[num] bytes'}"
		     @"    ]"
		     @"]" JSONValue],
		    @"num", num)];
	}

	[of_stdout writeString: @" "];

	if (_stopped)
		_BPS = (float)_received /

Modified utils/ofhttp/lang/de.json from [9e4f95f1d1] to [ef4298a3ca].

1
2
3
4
5

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





+







{
    "usage": "Benutzung: %[prog] -[cehHmoOPqv] url1 [url2 ...]",
    "full_usage": [
        "Optionen:\n",
        "    -b  --body             Angegebene Datei als Body übergeben\n",
        "                           (- für Standard-Eingabe)\n",
        "    -c  --continue         Download von existierender Datei ",
        "fortsetzen\n",
        "    -f  --force            Existierende Datei überschreiben\n",
        "    -h  --help             Diese Hilfe anzeigen\n",
        "    -H  --header           Einen Header (z.B. X-Foo:Bar) hinzufügen\n",
        "    -m  --method           HTTP Request-Methode setzen\n",
        "    -o  --output           Ausgabe-Dateiname angeben\n",
59
60
61
62
63
64
65

66



67
68


69
70
71
72
73
74
75
76
77






78
79
80
81
82
83
84
85
86
87
88






89
60
61
62
63
64
65
66
67

68
69
70
71

72
73
74
75
76
77
78
79
80
81

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104







+
-
+
+
+

-
+
+








-
+
+
+
+
+
+










-
+
+
+
+
+
+

    "download_failed_read_or_write_failed_any": "Lesen oder Schreiben",
    "download_failed_read_or_write_failed_read": "Lesen",
    "download_failed_read_or_write_failed_write": "Schreiben",
    "download_failed_read_or_write_failed": [
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  %[error]: %[exception]"
    ],
    "download_failed": [
    "download_failed": "%[prog]: Fehler beim Download von <%[url]>!",
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  HTTP Status-Code: %[code]"
    ],
    "download_failed_exception": [
        "%[prog]: Fehler beim Download von <%[url]>: %[exception]"
        "%[prog]: Fehler beim Download von <%[url]>!\n",
        "  %[exception]"
    ],
    "download_done": "Fertig!",
    "invalid_url": "%[prog]: Ungültige URL: <%[url]>!",
    "invalid_scheme": "%[prog]: Ungültiges Schema: <%[url]>!",
    "type_unknown": "unbekannt",
    "size_gib": "%[num] GiB",
    "size_mib": "%[num] MiB",
    "size_kib": "%[num] KiB",
    "size_bytes": "%[num] Bytes",
    "size_bytes": [
        [
            {"num == 1": "1 Byte"},
            {"": "%[num] Bytes"}
        ]
    ],
    "size_unknown": "unbekannt",
    "info_name_unaligned": "Name: %[name]",
    "info_name": "Name:  %[name]",
    "info_type": "Typ:   %[type]",
    "info_size": "Größe: %[size]",
    "output_already_exists": "%[prog]: Datei %[filename] existiert bereits!",
    "failed_to_open_output": [
        "%[prog]: Kann Datei %[filename] nicht öffnen: %[exception]"
    ],
    "eta_days": "%[num] t ",
    "progress_bytes": "%[num] Bytes"
    "progress_bytes": [
        [
            {"num == 1": "1 Byte "},
            {"": "%[num] Bytes"}
        ]
    ]
}

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

Added utils/ofsock/OFSock.m version [0c75e9be8d].

























































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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.uInt16Value];

		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