PyPtt 2.2.6__tar.gz → 2.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyptt-2.2.6 → pyptt-2.2.7}/PKG-INFO +1 -1
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/__init__.py +1 -1
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_uao/__init__.py +19 -7
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/connect_core.py +13 -9
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/screens.py +9 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt.egg-info/PKG-INFO +1 -1
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt.egg-info/SOURCES.txt +1 -0
- pyptt-2.2.7/tests/test_uao_codec.py +93 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/LICENSE +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/PTT.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_bucket.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_call_status.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_change_pw.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_comment.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_del_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_board_info.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_board_list.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_bottom_post_list.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_favourite_board.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_newest_index.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_post_index.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_post_list.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_time.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_user.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_get_waterball.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_give_money.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_has_new_mail.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_loginout.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_lottery.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_mail.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_mark_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_reply_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_search_user.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_set_board_title.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_set_signature_file.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_api_util.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_uao/LICENSE +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_uao/b2u.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/_uao/u2b.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/api_server.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/check_value.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/command.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/config.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/data_type.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/exceptions.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/i18n.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/lang_en_US.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/lang_zh_TW.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/lib_util.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/log.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/service.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt/ssl_config.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt.egg-info/dependency_links.txt +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt.egg-info/requires.txt +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/PyPtt.egg-info/top_level.txt +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/README.md +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/pyproject.toml +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/setup.cfg +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/setup.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_bucket.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_change_pw.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_comment.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_del_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_del_post_unit.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_exceptions.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_fast_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_board_info.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_board_list.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_bottom_post_list.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_content_formula.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_favourite_boards.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_mail.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_newest_index.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_post_list.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_post_list_parser.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_post_parser.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_time.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_user.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_get_waterball.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_give_money.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_i18n.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_incremental_parity.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_init.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_logger.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_login_logout.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_lottery.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_mail_send_and_del.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_mark_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_parse_query_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_post_anonymous.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_pyte_parity.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_reply_post.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_search_user.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_service.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_set_board_title.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_set_signature_file.py +0 -0
- {pyptt-2.2.6 → pyptt-2.2.7}/tests/test_vt100_parser.py +0 -0
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
"""Vendored Big5-UAO codec.
|
|
2
2
|
|
|
3
|
-
Vendored
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Vendored from PttCodingMan/pyUAO, a fork of eight04/pyUAO, to drop the
|
|
4
|
+
external `uao` PyPI dependency (unmaintained upstream) and pick up this
|
|
5
|
+
fork's correctness fixes and incremental/stream codec support.
|
|
6
|
+
|
|
7
|
+
Locally modified: `_decode` batches ASCII runs with a single C-level decode
|
|
8
|
+
instead of one chr()/append per byte. PTT screens are ~70% ASCII bytes even
|
|
9
|
+
in Big5, so this speeds up the receive path measurably while producing
|
|
10
|
+
byte-identical output (the byte→unicode table is unchanged). Python's stdlib
|
|
11
|
+
`big5` codec is *not* a usable fast path — it disagrees with the UAO table on
|
|
12
|
+
~260 sequences (e.g. 0xa1 0x45 → '•' vs '‧') and can't decode UAO's
|
|
13
|
+
extensions at all.
|
|
8
14
|
|
|
9
15
|
Source: https://github.com/PttCodingMan/pyUAO
|
|
10
16
|
Commit: 9d9a8bf59dbe60800d214b66ce972bc2ddf8da89 (v0.3.0)
|
|
@@ -89,8 +95,14 @@ def _decode(input, errors, final):
|
|
|
89
95
|
while i < n:
|
|
90
96
|
c = input[i]
|
|
91
97
|
if c < 0x80:
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
# At a character boundary every byte < 0x80 is a standalone ASCII
|
|
99
|
+
# char, so decode the whole run at once. latin-1 maps 0x00-0x7f
|
|
100
|
+
# identically to ASCII and never raises.
|
|
101
|
+
j = i + 1
|
|
102
|
+
while j < n and input[j] < 0x80:
|
|
103
|
+
j += 1
|
|
104
|
+
append(input[i:j].decode('latin-1'))
|
|
105
|
+
i = j
|
|
94
106
|
continue
|
|
95
107
|
if i + 1 < n:
|
|
96
108
|
u = _b2u_get(c * 0x100 + input[i + 1])
|
|
@@ -350,11 +350,11 @@ class API(object):
|
|
|
350
350
|
return -1
|
|
351
351
|
|
|
352
352
|
msg = ''
|
|
353
|
-
receive_data_buffer = bytes()
|
|
354
353
|
# Fresh incremental parsers for this screen sequence. Each arriving
|
|
355
354
|
# chunk is fed once (per encoding) instead of re-parsing the whole
|
|
356
355
|
# accumulated buffer every time — turning the receive loop from
|
|
357
|
-
# O(N²) into O(N).
|
|
356
|
+
# O(N²) into O(N). The parsers also hold the rendered screen, so no
|
|
357
|
+
# separate raw byte buffer is accumulated.
|
|
358
358
|
self._stream_parsers = {}
|
|
359
359
|
start_time = time.time()
|
|
360
360
|
find_target = False
|
|
@@ -372,7 +372,6 @@ class API(object):
|
|
|
372
372
|
|
|
373
373
|
if isinstance(data_chunk, str):
|
|
374
374
|
data_chunk = data_chunk.encode('utf-8')
|
|
375
|
-
receive_data_buffer += data_chunk
|
|
376
375
|
|
|
377
376
|
screen = self._stream_screen(self.current_encoding, data_chunk)
|
|
378
377
|
screen, find_target, is_secret, break_detect_after_send, use_too_many_res, msg, target_index = \
|
|
@@ -396,10 +395,17 @@ class API(object):
|
|
|
396
395
|
if find_target:
|
|
397
396
|
break
|
|
398
397
|
except TimeoutError:
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
398
|
+
# The incremental parser for the current encoding already holds
|
|
399
|
+
# the fully-rendered screen for every byte received this round
|
|
400
|
+
# (byte-identical to a fresh VT100Parser over the whole buffer —
|
|
401
|
+
# see tests/test_incremental_parity.py), so reuse it instead of
|
|
402
|
+
# re-decoding and re-parsing the entire buffer from scratch.
|
|
403
|
+
parser = self._stream_parsers.get(self.current_encoding)
|
|
404
|
+
if parser is not None:
|
|
405
|
+
screen = parser.screen
|
|
406
|
+
if len(screen) > 0:
|
|
407
|
+
screens.show(self.config, screen)
|
|
408
|
+
self._RDQ.add(screen)
|
|
403
409
|
if use_too_many_res:
|
|
404
410
|
raise exceptions.UseTooManyResources()
|
|
405
411
|
return -1
|
|
@@ -450,7 +456,6 @@ class API(object):
|
|
|
450
456
|
if break_detect_after_send:
|
|
451
457
|
return -1
|
|
452
458
|
msg = ''
|
|
453
|
-
receive_data_buffer = bytes()
|
|
454
459
|
self._stream_parsers = {}
|
|
455
460
|
start_time = time.time()
|
|
456
461
|
mid_time = time.time()
|
|
@@ -459,7 +464,6 @@ class API(object):
|
|
|
459
464
|
data = self._core.read_very_eager()
|
|
460
465
|
except EOFError:
|
|
461
466
|
return -1
|
|
462
|
-
receive_data_buffer += data
|
|
463
467
|
screen = self._stream_screen(self.current_encoding, data)
|
|
464
468
|
screen, find_target, is_secret, break_detect_after_send, use_too_many_res, msg, target_index = \
|
|
465
469
|
self._decode_screen(screen, start_time, target_list, is_secret, refresh, msg)
|
|
@@ -26,6 +26,8 @@ def _cell_width(ch: str) -> int:
|
|
|
26
26
|
Ambiguous includes glyphs like ※, ←, →, ◎ that PTT's protocol treats
|
|
27
27
|
as full-width when positioning the cursor.
|
|
28
28
|
"""
|
|
29
|
+
if ch.isascii(): # every ASCII char is a single narrow cell
|
|
30
|
+
return 1
|
|
29
31
|
w = _cell_width_cache.get(ch)
|
|
30
32
|
if w is None:
|
|
31
33
|
w = 2 if unicodedata.east_asian_width(ch) in ('W', 'F', 'A') else 1
|
|
@@ -34,6 +36,11 @@ def _cell_width(ch: str) -> int:
|
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
def _cell_len(text: str) -> int:
|
|
39
|
+
# PTT screens are mostly ASCII, and an all-ASCII string is exactly one cell
|
|
40
|
+
# per character — str.isascii() (a fast C scan) lets whole segments skip the
|
|
41
|
+
# per-character loop.
|
|
42
|
+
if text.isascii():
|
|
43
|
+
return len(text)
|
|
37
44
|
return sum(_cell_width(c) for c in text)
|
|
38
45
|
|
|
39
46
|
|
|
@@ -45,6 +52,8 @@ def _str_pos_at_cells(line: str, cells: int) -> int:
|
|
|
45
52
|
"""
|
|
46
53
|
if cells <= 0:
|
|
47
54
|
return 0
|
|
55
|
+
if line.isascii(): # 1 cell per char → cell offset == string index
|
|
56
|
+
return min(cells, len(line))
|
|
48
57
|
consumed = 0
|
|
49
58
|
for i, ch in enumerate(line):
|
|
50
59
|
if consumed >= cells:
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Regression tests for the vendored Big5-UAO codec (PyPtt/_uao).
|
|
3
|
+
|
|
4
|
+
`_decode` batches ASCII runs (one C-level decode instead of a chr()/append per
|
|
5
|
+
byte). These tests pin the invariant that the batched path produces exactly the
|
|
6
|
+
same output as a naive byte-by-byte decode, across ASCII runs, character
|
|
7
|
+
boundaries, incremental feeding and UAO-specific characters that stdlib `big5`
|
|
8
|
+
gets wrong or cannot represent.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import codecs
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
import PyPtt.screens # noqa: F401 — importing registers the big5uao codec
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def naive_decode(data: bytes, errors: str = 'replace') -> str:
|
|
19
|
+
"""Reference: decode one unit at a time, no ASCII batching."""
|
|
20
|
+
from PyPtt._uao.b2u import b2u_table
|
|
21
|
+
get = b2u_table.get
|
|
22
|
+
out = []
|
|
23
|
+
i, n = 0, len(data)
|
|
24
|
+
while i < n:
|
|
25
|
+
c = data[i]
|
|
26
|
+
if c < 0x80:
|
|
27
|
+
out.append(chr(c))
|
|
28
|
+
i += 1
|
|
29
|
+
continue
|
|
30
|
+
pair = get(c * 0x100 + data[i + 1]) if i + 1 < n else None
|
|
31
|
+
if pair is not None:
|
|
32
|
+
out.append(pair)
|
|
33
|
+
i += 2
|
|
34
|
+
continue
|
|
35
|
+
single = get(c)
|
|
36
|
+
if single is not None:
|
|
37
|
+
out.append(single)
|
|
38
|
+
i += 1
|
|
39
|
+
continue
|
|
40
|
+
out.append('�') # errors='replace'
|
|
41
|
+
i += 1
|
|
42
|
+
return ''.join(out)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
ASCII_CASES = [
|
|
46
|
+
b'',
|
|
47
|
+
b'hello world 12345',
|
|
48
|
+
b' leading and trailing spaces ',
|
|
49
|
+
b'[Ctrl-P] [d]delete [z]digest (100%)',
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.mark.parametrize('data', ASCII_CASES)
|
|
54
|
+
def test_ascii_runs_decode_verbatim(data):
|
|
55
|
+
assert data.decode('big5uao') == data.decode('ascii')
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_mixed_ascii_and_cjk():
|
|
59
|
+
s = ' 9439 3 1/10 littrabble ◎ [問題] 測試中文標題 discussion'
|
|
60
|
+
assert s.encode('big5uao').decode('big5uao') == s
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_uao_specific_characters_round_trip():
|
|
64
|
+
# Characters where stdlib big5 disagrees with UAO or can't encode them.
|
|
65
|
+
for s in ['‧', '~', '¥', '¢', '﹨', '⊕', '◎', '※', '←', '→', '─']:
|
|
66
|
+
assert s.encode('big5uao').decode('big5uao') == s
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.parametrize('errors', ['replace', 'ignore', 'strict'])
|
|
70
|
+
def test_batched_matches_naive_on_valid_content(errors):
|
|
71
|
+
s = '看板《Python》 test 混合 content 123 ‧~ ◎[軟工] tail'
|
|
72
|
+
data = s.encode('big5uao')
|
|
73
|
+
assert data.decode('big5uao', errors) == naive_decode(data)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_incremental_byte_by_byte_matches_batch():
|
|
77
|
+
s = '批踢踢 Python 測試 ‧~¥ ◎[軟工] 12345'
|
|
78
|
+
data = s.encode('big5uao')
|
|
79
|
+
dec = codecs.getincrementaldecoder('big5uao')('replace')
|
|
80
|
+
out = ''.join(dec.decode(bytes([b])) for b in data) + dec.decode(b'', final=True)
|
|
81
|
+
assert out == data.decode('big5uao') == s
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_incremental_splits_multibyte_across_chunks():
|
|
85
|
+
data = '中文'.encode('big5uao')
|
|
86
|
+
dec = codecs.getincrementaldecoder('big5uao')('replace')
|
|
87
|
+
# Split inside the first character's byte pair.
|
|
88
|
+
out = dec.decode(data[:1]) + dec.decode(data[1:], final=True)
|
|
89
|
+
assert out == '中文'
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_lone_0xff_maps_to_uao_private_use():
|
|
93
|
+
assert b'\xff'.decode('big5uao') == ''
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|