@push.rocks/smartlinuxrepo 0.0.0-stage → 1.0.0
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.
- package/.smartconfig.json +65 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/alpine/apkindex.d.ts +39 -0
- package/dist_ts/alpine/apkindex.js +211 -0
- package/dist_ts/alpine/pkginfo.d.ts +45 -0
- package/dist_ts/alpine/pkginfo.js +159 -0
- package/dist_ts/alpine/reader.d.ts +20 -0
- package/dist_ts/alpine/reader.js +89 -0
- package/dist_ts/arch/builder.d.ts +41 -0
- package/dist_ts/arch/builder.js +234 -0
- package/dist_ts/arch/reader.d.ts +16 -0
- package/dist_ts/arch/reader.js +271 -0
- package/dist_ts/arch/signer.d.ts +21 -0
- package/dist_ts/arch/signer.js +66 -0
- package/dist_ts/arch/vercmp.d.ts +12 -0
- package/dist_ts/arch/vercmp.js +170 -0
- package/dist_ts/debian/control.d.ts +22 -0
- package/dist_ts/debian/control.js +137 -0
- package/dist_ts/debian/gostring.d.ts +26 -0
- package/dist_ts/debian/gostring.js +145 -0
- package/dist_ts/debian/indexbuilder.d.ts +47 -0
- package/dist_ts/debian/indexbuilder.js +299 -0
- package/dist_ts/debian/mailaddress.d.ts +12 -0
- package/dist_ts/debian/mailaddress.js +736 -0
- package/dist_ts/debian/reader.d.ts +30 -0
- package/dist_ts/debian/reader.js +110 -0
- package/dist_ts/index.d.ts +23 -0
- package/dist_ts/index.js +18 -0
- package/dist_ts/plugins.d.ts +8 -0
- package/dist_ts/plugins.js +11 -0
- package/dist_ts/readers/ar.d.ts +31 -0
- package/dist_ts/readers/ar.js +87 -0
- package/dist_ts/readers/bytes.d.ts +58 -0
- package/dist_ts/readers/bytes.js +147 -0
- package/dist_ts/readers/bytesource.d.ts +40 -0
- package/dist_ts/readers/bytesource.js +154 -0
- package/dist_ts/readers/decompress.d.ts +43 -0
- package/dist_ts/readers/decompress.js +269 -0
- package/dist_ts/readers/gzipmember.d.ts +32 -0
- package/dist_ts/readers/gzipmember.js +176 -0
- package/dist_ts/readers/tar.d.ts +85 -0
- package/dist_ts/readers/tar.js +731 -0
- package/dist_ts/readers/text.d.ts +16 -0
- package/dist_ts/readers/text.js +96 -0
- package/dist_ts/rpm/builder.d.ts +28 -0
- package/dist_ts/rpm/builder.js +208 -0
- package/dist_ts/rpm/header.d.ts +138 -0
- package/dist_ts/rpm/header.js +546 -0
- package/dist_ts/rpm/reader.d.ts +20 -0
- package/dist_ts/rpm/reader.js +306 -0
- package/dist_ts/rpm/repoconfig.d.ts +15 -0
- package/dist_ts/rpm/repoconfig.js +38 -0
- package/dist_ts/rpm/repodata.d.ts +44 -0
- package/dist_ts/rpm/repodata.js +148 -0
- package/dist_ts/rpm/sign.d.ts +30 -0
- package/dist_ts/rpm/sign.js +183 -0
- package/dist_ts/rpm/updates.d.ts +38 -0
- package/dist_ts/rpm/updates.js +265 -0
- package/dist_ts/rpm/xml.d.ts +18 -0
- package/dist_ts/rpm/xml.js +90 -0
- package/dist_ts/shared/architecture.d.ts +8 -0
- package/dist_ts/shared/architecture.js +17 -0
- package/dist_ts/shared/errors.d.ts +38 -0
- package/dist_ts/shared/errors.js +22 -0
- package/dist_ts/shared/gopath.d.ts +13 -0
- package/dist_ts/shared/gopath.js +53 -0
- package/dist_ts/shared/gostrings.d.ts +14 -0
- package/dist_ts/shared/gostrings.js +29 -0
- package/dist_ts/shared/limits.d.ts +47 -0
- package/dist_ts/shared/limits.js +57 -0
- package/dist_ts/shared/types.d.ts +213 -0
- package/dist_ts/shared/types.js +13 -0
- package/dist_ts/shared/url.d.ts +18 -0
- package/dist_ts/shared/url.js +371 -0
- package/dist_ts/shared/validation.d.ts +21 -0
- package/dist_ts/shared/validation.js +58 -0
- package/dist_ts/signing/keys.d.ts +36 -0
- package/dist_ts/signing/keys.js +89 -0
- package/dist_ts/signing/openpgpframing.d.ts +33 -0
- package/dist_ts/signing/openpgpframing.js +93 -0
- package/dist_ts/signing/signers.d.ts +63 -0
- package/dist_ts/signing/signers.js +248 -0
- package/dist_ts/writers/gotar.d.ts +43 -0
- package/dist_ts/writers/gotar.js +218 -0
- package/license.md +19 -0
- package/package.json +56 -3
- package/readme.md +176 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/alpine/apkindex.ts +241 -0
- package/ts/alpine/pkginfo.ts +183 -0
- package/ts/alpine/reader.ts +96 -0
- package/ts/arch/builder.ts +283 -0
- package/ts/arch/reader.ts +307 -0
- package/ts/arch/signer.ts +70 -0
- package/ts/arch/vercmp.ts +188 -0
- package/ts/debian/control.ts +150 -0
- package/ts/debian/gostring.ts +150 -0
- package/ts/debian/indexbuilder.ts +355 -0
- package/ts/debian/mailaddress.ts +754 -0
- package/ts/debian/reader.ts +126 -0
- package/ts/index.ts +46 -0
- package/ts/plugins.ts +13 -0
- package/ts/readers/ar.ts +102 -0
- package/ts/readers/bytes.ts +182 -0
- package/ts/readers/bytesource.ts +164 -0
- package/ts/readers/decompress.ts +332 -0
- package/ts/readers/gzipmember.ts +209 -0
- package/ts/readers/tar.ts +835 -0
- package/ts/readers/text.ts +91 -0
- package/ts/rpm/builder.ts +253 -0
- package/ts/rpm/header.ts +628 -0
- package/ts/rpm/reader.ts +317 -0
- package/ts/rpm/repoconfig.ts +45 -0
- package/ts/rpm/repodata.ts +219 -0
- package/ts/rpm/sign.ts +200 -0
- package/ts/rpm/updates.ts +314 -0
- package/ts/rpm/xml.ts +101 -0
- package/ts/shared/architecture.ts +16 -0
- package/ts/shared/errors.ts +78 -0
- package/ts/shared/gopath.ts +54 -0
- package/ts/shared/gostrings.ts +31 -0
- package/ts/shared/limits.ts +93 -0
- package/ts/shared/types.ts +251 -0
- package/ts/shared/url.ts +387 -0
- package/ts/shared/validation.ts +71 -0
- package/ts/signing/keys.ts +110 -0
- package/ts/signing/openpgpframing.ts +115 -0
- package/ts/signing/signers.ts +315 -0
- package/ts/writers/gotar.ts +256 -0
- package/README.md +0 -3
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pacman's version comparison: libalpm `alpm_pkg_vercmp` with `parseEVR` and
|
|
3
|
+
* `rpmvercmp` (`lib/libalpm/version.c`, read from pacman 6.0.2 and checked
|
|
4
|
+
* against pacman's `vercmp` tool in the Docker gate). Gitea 28.2.0 uses a
|
|
5
|
+
* simplified comparator (`services/packages/arch/vercmp.go`) that splits on
|
|
6
|
+
* `.` only and can pick what pacman sees as a downgrade; the codec follows
|
|
7
|
+
* alpm. Characters are classified as in the C locale.
|
|
8
|
+
*
|
|
9
|
+
* Returns 1 when `a` is newer, -1 when `b` is newer, 0 when pacman treats
|
|
10
|
+
* them as the same version.
|
|
11
|
+
*/
|
|
12
|
+
export function compareArchVersions(aArg: string, bArg: string): -1 | 0 | 1 {
|
|
13
|
+
if (aArg === bArg) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const a = parseEvr(toBytes(aArg));
|
|
17
|
+
const b = parseEvr(toBytes(bArg));
|
|
18
|
+
let result = rpmvercmp(a.epoch, b.epoch);
|
|
19
|
+
if (result === 0) {
|
|
20
|
+
result = rpmvercmp(a.version, b.version);
|
|
21
|
+
if (result === 0 && a.release !== null && b.release !== null) {
|
|
22
|
+
result = rpmvercmp(a.release, b.release);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface IEvr {
|
|
29
|
+
epoch: Uint8Array;
|
|
30
|
+
version: Uint8Array;
|
|
31
|
+
release: Uint8Array | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const ZERO = Uint8Array.of(0x30);
|
|
35
|
+
|
|
36
|
+
function toBytes(valueArg: string): Uint8Array {
|
|
37
|
+
const bytes = new TextEncoder().encode(valueArg);
|
|
38
|
+
// a C string ends at the first NUL
|
|
39
|
+
const nul = bytes.indexOf(0);
|
|
40
|
+
return nul < 0 ? bytes : bytes.subarray(0, nul);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** `parseEVR`: `[epoch:]version[-release]`; the release starts after the last `-`. */
|
|
44
|
+
function parseEvr(evrArg: Uint8Array): IEvr {
|
|
45
|
+
let s = 0;
|
|
46
|
+
while (s < evrArg.length && isDigit(evrArg[s])) {
|
|
47
|
+
s++;
|
|
48
|
+
}
|
|
49
|
+
// strrchr(s, '-') searches from the epoch terminator on
|
|
50
|
+
let dash = -1;
|
|
51
|
+
for (let i = evrArg.length - 1; i >= s; i--) {
|
|
52
|
+
if (evrArg[i] === 0x2d) {
|
|
53
|
+
dash = i;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
let epoch: Uint8Array;
|
|
58
|
+
let versionStart: number;
|
|
59
|
+
if (s < evrArg.length && evrArg[s] === 0x3a) {
|
|
60
|
+
epoch = s === 0 ? ZERO : evrArg.subarray(0, s);
|
|
61
|
+
versionStart = s + 1;
|
|
62
|
+
} else {
|
|
63
|
+
epoch = ZERO;
|
|
64
|
+
versionStart = 0;
|
|
65
|
+
}
|
|
66
|
+
if (dash >= 0) {
|
|
67
|
+
return { epoch, version: evrArg.subarray(versionStart, dash), release: evrArg.subarray(dash + 1) };
|
|
68
|
+
}
|
|
69
|
+
return { epoch, version: evrArg.subarray(versionStart), release: null };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `rpmvercmp` over two C strings. */
|
|
73
|
+
function rpmvercmp(aArg: Uint8Array, bArg: Uint8Array): -1 | 0 | 1 {
|
|
74
|
+
if (compareBytes(aArg, 0, aArg.length, bArg, 0, bArg.length) === 0) {
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
const at = (bytesArg: Uint8Array, indexArg: number): number => (indexArg < bytesArg.length ? bytesArg[indexArg] : 0);
|
|
78
|
+
let one = 0;
|
|
79
|
+
let two = 0;
|
|
80
|
+
let ptr1 = 0;
|
|
81
|
+
let ptr2 = 0;
|
|
82
|
+
while (at(aArg, one) !== 0 && at(bArg, two) !== 0) {
|
|
83
|
+
while (at(aArg, one) !== 0 && !isAlnum(at(aArg, one))) {
|
|
84
|
+
one++;
|
|
85
|
+
}
|
|
86
|
+
while (at(bArg, two) !== 0 && !isAlnum(at(bArg, two))) {
|
|
87
|
+
two++;
|
|
88
|
+
}
|
|
89
|
+
if (!(at(aArg, one) !== 0 && at(bArg, two) !== 0)) {
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
// separator runs of different length decide
|
|
93
|
+
if (one - ptr1 !== two - ptr2) {
|
|
94
|
+
return one - ptr1 < two - ptr2 ? -1 : 1;
|
|
95
|
+
}
|
|
96
|
+
ptr1 = one;
|
|
97
|
+
ptr2 = two;
|
|
98
|
+
let isNum: boolean;
|
|
99
|
+
if (isDigit(at(aArg, ptr1))) {
|
|
100
|
+
while (at(aArg, ptr1) !== 0 && isDigit(at(aArg, ptr1))) {
|
|
101
|
+
ptr1++;
|
|
102
|
+
}
|
|
103
|
+
while (at(bArg, ptr2) !== 0 && isDigit(at(bArg, ptr2))) {
|
|
104
|
+
ptr2++;
|
|
105
|
+
}
|
|
106
|
+
isNum = true;
|
|
107
|
+
} else {
|
|
108
|
+
while (at(aArg, ptr1) !== 0 && isAlpha(at(aArg, ptr1))) {
|
|
109
|
+
ptr1++;
|
|
110
|
+
}
|
|
111
|
+
while (at(bArg, ptr2) !== 0 && isAlpha(at(bArg, ptr2))) {
|
|
112
|
+
ptr2++;
|
|
113
|
+
}
|
|
114
|
+
isNum = false;
|
|
115
|
+
}
|
|
116
|
+
if (one === ptr1) {
|
|
117
|
+
return -1;
|
|
118
|
+
}
|
|
119
|
+
// numeric segments are newer than alpha segments
|
|
120
|
+
if (two === ptr2) {
|
|
121
|
+
return isNum ? 1 : -1;
|
|
122
|
+
}
|
|
123
|
+
let segment1 = one;
|
|
124
|
+
let segment2 = two;
|
|
125
|
+
if (isNum) {
|
|
126
|
+
while (at(aArg, segment1) === 0x30 && segment1 < ptr1) {
|
|
127
|
+
segment1++;
|
|
128
|
+
}
|
|
129
|
+
while (at(bArg, segment2) === 0x30 && segment2 < ptr2) {
|
|
130
|
+
segment2++;
|
|
131
|
+
}
|
|
132
|
+
if (ptr1 - segment1 > ptr2 - segment2) {
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
if (ptr2 - segment2 > ptr1 - segment1) {
|
|
136
|
+
return -1;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const rc = compareBytes(aArg, segment1, ptr1, bArg, segment2, ptr2);
|
|
140
|
+
if (rc !== 0) {
|
|
141
|
+
return rc;
|
|
142
|
+
}
|
|
143
|
+
one = ptr1;
|
|
144
|
+
two = ptr2;
|
|
145
|
+
}
|
|
146
|
+
if (at(aArg, one) === 0 && at(bArg, two) === 0) {
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
// a remaining alpha string never beats an empty one
|
|
150
|
+
if ((at(aArg, one) === 0 && !isAlpha(at(bArg, two))) || isAlpha(at(aArg, one))) {
|
|
151
|
+
return -1;
|
|
152
|
+
}
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** `strcmp` over two byte ranges. */
|
|
157
|
+
function compareBytes(
|
|
158
|
+
aArg: Uint8Array,
|
|
159
|
+
aStartArg: number,
|
|
160
|
+
aEndArg: number,
|
|
161
|
+
bArg: Uint8Array,
|
|
162
|
+
bStartArg: number,
|
|
163
|
+
bEndArg: number,
|
|
164
|
+
): -1 | 0 | 1 {
|
|
165
|
+
const length = Math.min(aEndArg - aStartArg, bEndArg - bStartArg);
|
|
166
|
+
for (let i = 0; i < length; i++) {
|
|
167
|
+
const x = aArg[aStartArg + i];
|
|
168
|
+
const y = bArg[bStartArg + i];
|
|
169
|
+
if (x !== y) {
|
|
170
|
+
return x < y ? -1 : 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const aLength = aEndArg - aStartArg;
|
|
174
|
+
const bLength = bEndArg - bStartArg;
|
|
175
|
+
return aLength === bLength ? 0 : aLength < bLength ? -1 : 1;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function isDigit(byteArg: number): boolean {
|
|
179
|
+
return byteArg >= 0x30 && byteArg <= 0x39;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function isAlpha(byteArg: number): boolean {
|
|
183
|
+
return (byteArg >= 0x41 && byteArg <= 0x5a) || (byteArg >= 0x61 && byteArg <= 0x7a);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function isAlnum(byteArg: number): boolean {
|
|
187
|
+
return isDigit(byteArg) || isAlpha(byteArg);
|
|
188
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { LinuxRepoError, limitExceeded } from '../shared/errors.js';
|
|
2
|
+
import { isValidUrl } from '../shared/url.js';
|
|
3
|
+
import type { IDebianPackageMetadata } from '../shared/types.js';
|
|
4
|
+
import { fromByteString, goTrimSpace, toByteString } from './gostring.js';
|
|
5
|
+
import { parseMailAddress } from './mailaddress.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Gitea 28.2.0 `modules/packages/debian/metadata.go` patterns. In both Go and
|
|
9
|
+
* JS `+-.` and `+-~` inside a class are ranges (0x2B-0x2E and 0x2B-0x7E), so
|
|
10
|
+
* the name accepts `,` and the revision accepts almost all printable ASCII.
|
|
11
|
+
*/
|
|
12
|
+
const NAME_PATTERN = /^[a-z0-9][a-z0-9+-.]+$/;
|
|
13
|
+
const VERSION_PATTERN = /^(?:(0|[1-9][0-9]*):)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?$/;
|
|
14
|
+
/** Gitea's distribution and component rule (`metadata.go:67`, applied at upload). */
|
|
15
|
+
export const DEBIAN_SYMBOL_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9.~+_-]*$/;
|
|
16
|
+
|
|
17
|
+
export interface IDebianControl {
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
architecture: string;
|
|
21
|
+
metadata: IDebianPackageMetadata;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A port of Gitea 28.2.0 `ParseControlFile` (`metadata.go:165-265`) over the
|
|
26
|
+
* bytes of the `control` entry. Lines are split as `bufio.ScanLines` does
|
|
27
|
+
* (`\n`, one trailing `\r` dropped); the first blank line after content ends
|
|
28
|
+
* the stanza. Every derived value is Gitea's: the raw stanza trimmed, the
|
|
29
|
+
* Maintainer name through `net/mail`, the Homepage only when Gitea's URL
|
|
30
|
+
* check accepts it, the Description with its continuation lines appended
|
|
31
|
+
* verbatim, and the Depends text split on `,` (`['']` when there is none).
|
|
32
|
+
*
|
|
33
|
+
* Gitea's scanner refuses lines over 64 KiB; the codec accepts lines up to
|
|
34
|
+
* `maxLineBytes` (`maxHeaderBytes`), which is at least that.
|
|
35
|
+
*/
|
|
36
|
+
export function parseDebianControlFile(bytesArg: Uint8Array, maxLineBytesArg: number): IDebianControl {
|
|
37
|
+
const text = toByteString(bytesArg);
|
|
38
|
+
let name = '';
|
|
39
|
+
let version = '';
|
|
40
|
+
let architecture = '';
|
|
41
|
+
let maintainer = '';
|
|
42
|
+
let projectUrl = '';
|
|
43
|
+
let key = '';
|
|
44
|
+
let depends = '';
|
|
45
|
+
let control = '';
|
|
46
|
+
let description = '';
|
|
47
|
+
|
|
48
|
+
let offset = 0;
|
|
49
|
+
while (offset < text.length) {
|
|
50
|
+
const newline = text.indexOf('\n', offset);
|
|
51
|
+
const end = newline < 0 ? text.length : newline;
|
|
52
|
+
let line = text.slice(offset, end);
|
|
53
|
+
offset = newline < 0 ? text.length : newline + 1;
|
|
54
|
+
if (line.endsWith('\r')) {
|
|
55
|
+
line = line.slice(0, -1);
|
|
56
|
+
}
|
|
57
|
+
if (line.length > maxLineBytesArg) {
|
|
58
|
+
throw limitExceeded('maxHeaderBytes', maxLineBytesArg, 'Debian control line');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const trimmed = goTrimSpace(line);
|
|
62
|
+
if (trimmed === '') {
|
|
63
|
+
// one stanza only: the blank line after content ends it (metadata.go:182-191)
|
|
64
|
+
if (control.length === 0) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
control += `${line}\n`;
|
|
70
|
+
|
|
71
|
+
if (line[0] === ' ' || line[0] === '\t') {
|
|
72
|
+
// a folded continuation line of the previous field
|
|
73
|
+
if (key === 'Description') {
|
|
74
|
+
description += line;
|
|
75
|
+
} else if (key === 'Depends') {
|
|
76
|
+
depends += trimmed;
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const colon = trimmed.indexOf(':');
|
|
81
|
+
if (colon < 0) {
|
|
82
|
+
// Gitea keeps the previous key here (metadata.go:207-210)
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
key = trimmed.slice(0, colon);
|
|
86
|
+
const value = goTrimSpace(trimmed.slice(colon + 1));
|
|
87
|
+
switch (key) {
|
|
88
|
+
case 'Package':
|
|
89
|
+
name = value;
|
|
90
|
+
break;
|
|
91
|
+
case 'Version':
|
|
92
|
+
version = value;
|
|
93
|
+
break;
|
|
94
|
+
case 'Architecture':
|
|
95
|
+
architecture = value;
|
|
96
|
+
break;
|
|
97
|
+
case 'Maintainer': {
|
|
98
|
+
const address = parseMailAddress(value);
|
|
99
|
+
maintainer = address === null || address.name === '' ? value : address.name;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case 'Description':
|
|
103
|
+
description = value;
|
|
104
|
+
break;
|
|
105
|
+
case 'Depends':
|
|
106
|
+
// appended without a separator, as Gitea does for a repeated field
|
|
107
|
+
depends += value;
|
|
108
|
+
break;
|
|
109
|
+
case 'Homepage':
|
|
110
|
+
if (isValidUrl(fromByteString(value))) {
|
|
111
|
+
projectUrl = value;
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (name === '') {
|
|
118
|
+
throw new LinuxRepoError('missing-field', 'the control file has no Package field', { field: 'Package' });
|
|
119
|
+
}
|
|
120
|
+
if (!NAME_PATTERN.test(name)) {
|
|
121
|
+
throw new LinuxRepoError('invalid-field', 'the package name is invalid', { field: 'Package', value: fromByteString(name) });
|
|
122
|
+
}
|
|
123
|
+
if (version === '') {
|
|
124
|
+
throw new LinuxRepoError('missing-field', 'the control file has no Version field', { field: 'Version' });
|
|
125
|
+
}
|
|
126
|
+
if (!VERSION_PATTERN.test(version)) {
|
|
127
|
+
throw new LinuxRepoError('invalid-field', 'the package version is invalid', {
|
|
128
|
+
field: 'Version',
|
|
129
|
+
value: fromByteString(version),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (architecture === '') {
|
|
133
|
+
throw new LinuxRepoError('missing-field', 'the control file has no Architecture field', {
|
|
134
|
+
field: 'Architecture',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
name,
|
|
140
|
+
version,
|
|
141
|
+
architecture: fromByteString(architecture),
|
|
142
|
+
metadata: {
|
|
143
|
+
control: fromByteString(goTrimSpace(control)),
|
|
144
|
+
maintainer: fromByteString(maintainer),
|
|
145
|
+
projectUrl: fromByteString(projectUrl),
|
|
146
|
+
description: fromByteString(description),
|
|
147
|
+
dependencies: depends.split(',').map((dependency) => fromByteString(goTrimSpace(dependency))),
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { decodeGoUtf8, decodeLatin1 } from '../readers/text.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Go string semantics for the Debian ports. A Go string is a byte sequence;
|
|
5
|
+
* here it is a "byte string": a JS string whose every code unit is one byte
|
|
6
|
+
* (0-255). The ported Gitea and Go code indexes, slices and compares bytes
|
|
7
|
+
* exactly as Go does, and only the finished values are decoded into Unicode
|
|
8
|
+
* with `fromByteString` (Go's JSON coercion of invalid UTF-8).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export function toByteString(bytesArg: Uint8Array): string {
|
|
12
|
+
return decodeLatin1(bytesArg);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function byteStringToBytes(valueArg: string): Uint8Array {
|
|
16
|
+
return Uint8Array.from(Buffer.from(valueArg, 'latin1'));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The byte string of a JS string's UTF-8 encoding. */
|
|
20
|
+
export function utf8ByteString(valueArg: string): string {
|
|
21
|
+
return Buffer.from(valueArg, 'utf8').toString('latin1');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Decodes a byte string into Unicode, replacing every invalid byte with U+FFFD as Go's JSON encoder does. */
|
|
25
|
+
export function fromByteString(valueArg: string): string {
|
|
26
|
+
return decodeGoUtf8(byteStringToBytes(valueArg));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const RUNE_ERROR = 0xfffd;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Go `utf8.DecodeRuneInString(s[i:])`: the rune at byte offset `i` and its
|
|
33
|
+
* width. The width is 0 at the end of the string; an invalid or truncated
|
|
34
|
+
* sequence is `RUNE_ERROR` with width 1.
|
|
35
|
+
*/
|
|
36
|
+
export function decodeRune(valueArg: string, indexArg: number): [number, number] {
|
|
37
|
+
if (indexArg >= valueArg.length) {
|
|
38
|
+
return [RUNE_ERROR, 0];
|
|
39
|
+
}
|
|
40
|
+
const b0 = valueArg.charCodeAt(indexArg);
|
|
41
|
+
if (b0 < 0x80) {
|
|
42
|
+
return [b0, 1];
|
|
43
|
+
}
|
|
44
|
+
let need: number;
|
|
45
|
+
let rune: number;
|
|
46
|
+
let low = 0x80;
|
|
47
|
+
let high = 0xbf;
|
|
48
|
+
if (b0 >= 0xc2 && b0 <= 0xdf) {
|
|
49
|
+
need = 1;
|
|
50
|
+
rune = b0 & 0x1f;
|
|
51
|
+
} else if (b0 >= 0xe0 && b0 <= 0xef) {
|
|
52
|
+
need = 2;
|
|
53
|
+
rune = b0 & 0x0f;
|
|
54
|
+
if (b0 === 0xe0) {
|
|
55
|
+
low = 0xa0;
|
|
56
|
+
} else if (b0 === 0xed) {
|
|
57
|
+
high = 0x9f;
|
|
58
|
+
}
|
|
59
|
+
} else if (b0 >= 0xf0 && b0 <= 0xf4) {
|
|
60
|
+
need = 3;
|
|
61
|
+
rune = b0 & 0x07;
|
|
62
|
+
if (b0 === 0xf0) {
|
|
63
|
+
low = 0x90;
|
|
64
|
+
} else if (b0 === 0xf4) {
|
|
65
|
+
high = 0x8f;
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
return [RUNE_ERROR, 1];
|
|
69
|
+
}
|
|
70
|
+
if (indexArg + need >= valueArg.length) {
|
|
71
|
+
return [RUNE_ERROR, 1];
|
|
72
|
+
}
|
|
73
|
+
for (let k = 1; k <= need; k++) {
|
|
74
|
+
const b = valueArg.charCodeAt(indexArg + k);
|
|
75
|
+
const min = k === 1 ? low : 0x80;
|
|
76
|
+
const max = k === 1 ? high : 0xbf;
|
|
77
|
+
if (b < min || b > max) {
|
|
78
|
+
return [RUNE_ERROR, 1];
|
|
79
|
+
}
|
|
80
|
+
rune = (rune << 6) | (b & 0x3f);
|
|
81
|
+
}
|
|
82
|
+
return [rune, need + 1];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Go `unicode.IsSpace`. */
|
|
86
|
+
export function isGoSpace(runeArg: number): boolean {
|
|
87
|
+
switch (runeArg) {
|
|
88
|
+
case 0x09:
|
|
89
|
+
case 0x0a:
|
|
90
|
+
case 0x0b:
|
|
91
|
+
case 0x0c:
|
|
92
|
+
case 0x0d:
|
|
93
|
+
case 0x20:
|
|
94
|
+
case 0x85:
|
|
95
|
+
case 0xa0:
|
|
96
|
+
case 0x1680:
|
|
97
|
+
case 0x2028:
|
|
98
|
+
case 0x2029:
|
|
99
|
+
case 0x202f:
|
|
100
|
+
case 0x205f:
|
|
101
|
+
case 0x3000:
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return runeArg >= 0x2000 && runeArg <= 0x200a;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Go `strings.TrimSpace` on a byte string. */
|
|
108
|
+
export function goTrimSpace(valueArg: string): string {
|
|
109
|
+
let start = 0;
|
|
110
|
+
while (start < valueArg.length) {
|
|
111
|
+
const [rune, width] = decodeRune(valueArg, start);
|
|
112
|
+
if (!isGoSpace(rune)) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
start += width;
|
|
116
|
+
}
|
|
117
|
+
let end = valueArg.length;
|
|
118
|
+
while (end > start) {
|
|
119
|
+
const [rune, width] = decodeLastRune(valueArg, start, end);
|
|
120
|
+
if (!isGoSpace(rune)) {
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
end -= width;
|
|
124
|
+
}
|
|
125
|
+
return valueArg.slice(start, end);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Go `utf8.DecodeLastRuneInString(s[start:end])`. */
|
|
129
|
+
function decodeLastRune(valueArg: string, startArg: number, endArg: number): [number, number] {
|
|
130
|
+
const last = valueArg.charCodeAt(endArg - 1);
|
|
131
|
+
if (last < 0x80) {
|
|
132
|
+
return [last, 1];
|
|
133
|
+
}
|
|
134
|
+
// a rune is at most 4 bytes: find the start byte and check it decodes to exactly the tail
|
|
135
|
+
const lowest = Math.max(startArg, endArg - 4);
|
|
136
|
+
let begin = endArg - 1;
|
|
137
|
+
while (begin > lowest && (valueArg.charCodeAt(begin) & 0xc0) === 0x80) {
|
|
138
|
+
begin -= 1;
|
|
139
|
+
}
|
|
140
|
+
const [rune, width] = decodeRune(valueArg.slice(0, endArg), begin);
|
|
141
|
+
if (begin + width !== endArg) {
|
|
142
|
+
return [RUNE_ERROR, 1];
|
|
143
|
+
}
|
|
144
|
+
return [rune, width];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Bytewise comparison of two JS strings by their UTF-8 encodings (Go `sort.Strings` order). */
|
|
148
|
+
export function compareUtf8(aArg: string, bArg: string): number {
|
|
149
|
+
return Buffer.compare(Buffer.from(aArg, 'utf8'), Buffer.from(bArg, 'utf8'));
|
|
150
|
+
}
|