Skip to content

Commit 2728dcc

Browse files
committed
Windows: add build step that generates license.rtf from LICENSE
1 parent 930fabe commit 2728dcc

2 files changed

Lines changed: 342 additions & 3 deletions

File tree

‎tools/license2rtf.js‎

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
2+
var assert = require('assert'),
3+
Stream = require('stream'),
4+
inherits = require('util').inherits;
5+
6+
7+
/*
8+
* This filter consumes a stream of characters and emits one string per line.
9+
*/
10+
function LineSplitter() {
11+
var self = this,
12+
buffer = "";
13+
14+
Stream.call(this);
15+
this.writable = true;
16+
17+
this.write = function(data) {
18+
var lines = (buffer + data).split(/\r\n|\n\r|\n|\r/);
19+
for (var i = 0; i < lines.length - 1; i++) {
20+
self.emit('data', lines[i]);
21+
}
22+
buffer = lines[lines.length - 1];
23+
return true;
24+
};
25+
26+
this.end = function(data) {
27+
this.write(data || '');
28+
if (buffer) {
29+
self.emit('data', buffer);
30+
}
31+
self.emit('end');
32+
};
33+
}
34+
inherits(LineSplitter, Stream);
35+
36+
37+
/*
38+
* This filter consumes lines and emits paragraph objects.
39+
*/
40+
function ParagraphParser() {
41+
var self = this,
42+
block_is_license_block = false,
43+
block_has_c_style_comment,
44+
is_first_line_in_paragraph,
45+
paragraph_line_indent,
46+
paragraph;
47+
48+
Stream.call(this);
49+
this.writable = true;
50+
51+
resetBlock(false);
52+
53+
this.write = function(data) {
54+
parseLine(data + '');
55+
return true;
56+
};
57+
58+
this.end = function(data) {
59+
if (data) {
60+
parseLine(data + '');
61+
}
62+
flushParagraph();
63+
self.emit('end');
64+
};
65+
66+
function resetParagraph() {
67+
is_first_line_in_paragraph = true;
68+
paragraph_line_indent = -1;
69+
70+
paragraph = {
71+
li: '',
72+
in_license_block: block_is_license_block,
73+
lines: []
74+
};
75+
}
76+
77+
function resetBlock(is_license_block) {
78+
block_is_license_block = is_license_block;
79+
block_has_c_style_comment = false;
80+
resetParagraph();
81+
}
82+
83+
function flushParagraph() {
84+
if (paragraph.lines.length || paragraph.li) {
85+
self.emit('data', paragraph);
86+
}
87+
resetParagraph();
88+
}
89+
90+
function parseLine(line) {
91+
// Strip trailing whitespace
92+
line = line.replace(/\s*$/, '');
93+
94+
// Detect block separator
95+
if (/^\s*(=|"){3,}\s*$/.test(line)) {
96+
flushParagraph();
97+
resetBlock(!block_is_license_block);
98+
return;
99+
}
100+
101+
// Strip comments around block
102+
if (block_is_license_block) {
103+
if (!block_has_c_style_comment)
104+
block_has_c_style_comment = /^\s*(\/\*)/.test(line);
105+
if (block_has_c_style_comment) {
106+
var prev = line;
107+
line = line.replace(/^(\s*?)(?:\s?\*\/|\/\*\s|\s\*\s?)/, '$1');
108+
if (prev == line)
109+
line = line.replace(/^\s{2}/, '');
110+
if (/\*\//.test(prev))
111+
block_has_c_style_comment = false;
112+
} else {
113+
// Strip C++ and perl style comments.
114+
line = line.replace(/^(\s*)(?:\/\/\s?|#\s?)/, '$1');
115+
}
116+
}
117+
118+
// Detect blank line (paragraph separator)
119+
if (!/\S/.test(line)) {
120+
flushParagraph();
121+
return;
122+
}
123+
124+
// Detect separator "lines" within a block. These mark a paragraph break
125+
// and are stripped from the output.
126+
if (/^\s*[=*\-]{5,}\s*$/.test(line)) {
127+
flushParagraph();
128+
return;
129+
}
130+
131+
// Find out indentation level and the start of a lied or numbered list;
132+
var result = /^(\s*)(\d+\.|\*|-)?\s*/.exec(line);
133+
assert.ok(result);
134+
// The number of characters that will be stripped from the beginning of
135+
// the line.
136+
var line_strip_length = result[0].length;
137+
// The indentation size that will be used to detect indentation jumps.
138+
// Fudge by 1 space.
139+
var line_indent = Math.floor(result[0].length / 2) * 2;
140+
// The indentation level that will be exported
141+
var level = Math.floor(result[1].length / 2);
142+
// The list indicator that precedes the actual content, if any.
143+
var line_li = result[2];
144+
145+
// Flush the paragraph when there is a li or an indentation jump
146+
if (line_li || (line_indent != paragraph_line_indent &&
147+
paragraph_line_indent != -1)) {
148+
flushParagraph();
149+
paragraph.li = line_li;
150+
}
151+
152+
// Set the paragraph indent that we use to detect indentation jumps. When
153+
// we just detected a list indicator, wait
154+
// for the next line to arrive before setting this.
155+
if (!line_li && paragraph_line_indent != -1) {
156+
paragraph_line_indent = line_indent;
157+
}
158+
159+
// Set the output indent level if it has not been set yet.
160+
if (paragraph.level === undefined)
161+
paragraph.level = level;
162+
163+
// Strip leading whitespace and li.
164+
line = line.slice(line_strip_length);
165+
166+
if (line)
167+
paragraph.lines.push(line);
168+
169+
is_first_line_in_paragraph = false;
170+
}
171+
}
172+
inherits(ParagraphParser, Stream);
173+
174+
175+
/*
176+
* This filter consumes paragraph objects and emits modified paragraph objects.
177+
* The lines within the paragraph are unwrapped where appropriate.
178+
*/
179+
function Unwrapper() {
180+
var self = this;
181+
182+
Stream.call(this);
183+
this.writable = true;
184+
185+
this.write = function(paragraph) {
186+
var lines = paragraph.lines,
187+
break_after = [],
188+
i;
189+
190+
for (i = 0; i < lines.length - 1; i++) {
191+
var line = lines[i];
192+
193+
// When a line is really short, the line was probably kept separate for a
194+
// reason.
195+
if (line.length < 50) {
196+
// If the first word on the next line really didn't fit after the line,
197+
// it probably was just ordinary wrapping after all.
198+
var next_first_word_length = lines[i + 1].replace(/\s.*$/, '').length;
199+
if (line.length + next_first_word_length < 60) {
200+
break_after[i] = true;
201+
}
202+
}
203+
}
204+
205+
for (i = 0; i < lines.length - 1; ) {
206+
if (!break_after[i]) {
207+
lines[i] += ' ' + lines.splice(i + 1, 1)[0];
208+
} else {
209+
i++;
210+
}
211+
}
212+
213+
self.emit('data', paragraph);
214+
};
215+
216+
this.end = function(data) {
217+
if (data)
218+
self.write(data);
219+
self.emit('end');
220+
};
221+
}
222+
inherits(Unwrapper, Stream);
223+
224+
225+
/*
226+
* This filter generates an rtf document from a stream of paragraph objects.
227+
*/
228+
function RtfGenerator() {
229+
var self = this,
230+
did_write_anything = false;
231+
232+
Stream.call(this);
233+
this.writable = true;
234+
235+
this.write = function(paragraph) {
236+
if (!did_write_anything) {
237+
emitHeader();
238+
did_write_anything = true;
239+
}
240+
241+
var li = paragraph.li,
242+
level = paragraph.level + (li ? 1 : 0),
243+
lic = paragraph.in_license_block;
244+
245+
var rtf = "\\pard";
246+
rtf += '\\sa150\\sl300\\slmult1';
247+
if (level > 0)
248+
rtf += '\\li' + (level * 240);
249+
if (li) {
250+
rtf += '\\tx' + (level) * 240;
251+
rtf += '\\fi-240';
252+
}
253+
if (lic)
254+
rtf += '\\ri240';
255+
if (!lic)
256+
rtf += '\\b';
257+
if (li)
258+
rtf += ' ' + li + '\\tab';
259+
rtf += ' ';
260+
rtf += paragraph.lines.map(rtfEscape).join('\\line ');
261+
if (!lic)
262+
rtf += '\\b0';
263+
rtf += '\\par\n';
264+
265+
self.emit('data', rtf);
266+
};
267+
268+
this.end = function(data) {
269+
if (data)
270+
self.write(data);
271+
if (did_write_anything)
272+
emitFooter();
273+
self.emit('end');
274+
};
275+
276+
function toHex(number, length) {
277+
var hex = (~~number).toString(16);
278+
while (hex.length < length)
279+
hex = '0' + hex;
280+
return hex;
281+
}
282+
283+
function rtfEscape(string) {
284+
return string
285+
.replace(/[\\\{\}]/g, function(m) {
286+
return '\\' + m;
287+
})
288+
.replace(/\t/g, function() {
289+
return '\\tab ';
290+
})
291+
.replace(/[\x00-\x1f\x7f-\xff]/g, function(m) {
292+
return '\\\'' + toHex(m.charCodeAt(0), 2);
293+
})
294+
.replace(/\ufeff/g, '')
295+
.replace(/[\u0100-\uffff]/g, function(m) {
296+
return '\\u' + toHex(m.charCodeAt(0), 4) + '?';
297+
});
298+
}
299+
300+
function emitHeader() {
301+
self.emit('data', '{\\rtf1\\ansi\\ansicpg1252\\uc1\\deff0\\deflang1033' +
302+
'{\\fonttbl{\\f0\\fswiss\\fcharset0 Tahoma;}}\\fs20\n' +
303+
'{\\*\\generator txt2rtf 0.0.1;}\n');
304+
}
305+
306+
function emitFooter() {
307+
self.emit('data', '}');
308+
}
309+
}
310+
inherits(RtfGenerator, Stream);
311+
312+
313+
var stdin = process.stdin,
314+
stdout = process.stdout,
315+
line_splitter = new LineSplitter(),
316+
paragraph_parser = new ParagraphParser(),
317+
unwrapper = new Unwrapper(),
318+
rtf_generator = new RtfGenerator();
319+
320+
stdin.setEncoding('utf-8');
321+
stdin.resume();
322+
323+
stdin.pipe(line_splitter);
324+
line_splitter.pipe(paragraph_parser);
325+
paragraph_parser.pipe(unwrapper);
326+
unwrapper.pipe(rtf_generator);
327+
rtf_generator.pipe(stdout);

‎vcbuild.bat‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ set nosnapshot=
2424
set test=
2525
set test_args=
2626
set msi=
27+
set licensertf=
2728
set upload=
2829
set jslint=
2930

@@ -39,14 +40,15 @@ if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok
3940
if /i "%1"=="nobuild" set nobuild=1&goto arg-ok
4041
if /i "%1"=="nosign" set nosign=1&goto arg-ok
4142
if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok
43+
if /i "%1"=="licensertf" set licensertf=1&goto arg-ok
4244
if /i "%1"=="test-uv" set test=test-uv&goto arg-ok
4345
if /i "%1"=="test-internet" set test=test-internet&goto arg-ok
4446
if /i "%1"=="test-pummel" set test=test-pummel&goto arg-ok
4547
if /i "%1"=="test-simple" set test=test-simple&goto arg-ok
4648
if /i "%1"=="test-message" set test=test-message&goto arg-ok
4749
if /i "%1"=="test-all" set test=test-all&goto arg-ok
4850
if /i "%1"=="test" set test=test&goto arg-ok
49-
if /i "%1"=="msi" set msi=1&goto arg-ok
51+
if /i "%1"=="msi" set msi=1&set licensertf=1&goto arg-ok
5052
if /i "%1"=="upload" set upload=1&goto arg-ok
5153
if /i "%1"=="jslint" set jslint=1&goto arg-ok
5254

@@ -74,7 +76,7 @@ echo Project files generated.
7476

7577
:msbuild
7678
@rem Skip project generation if requested.
77-
if defined nobuild goto msi
79+
if defined nobuild goto sign
7880

7981
@rem Bail out early if not running in VS build env.
8082
if defined VCINSTALLDIR goto msbuild-found
@@ -93,9 +95,19 @@ goto run
9395
msbuild node.sln /m /t:%target% /p:Configuration=%config% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
9496
if errorlevel 1 goto exit
9597

96-
if defined nosign goto msi
98+
:sign
99+
@rem Skip signing if the `nosign` option was specified.
100+
if defined nosign goto licensertf
101+
97102
signtool sign /a Release\node.exe
98103

104+
:licensertf
105+
@rem Skip license.rtf generation if not requested.
106+
if not defined licensertf goto msi
107+
108+
%config%\node tools\license2rtf.js < LICENSE > %config%\license.rtf
109+
if errorlevel 1 echo Failed to generate license.rtf&goto exit
110+
99111
:msi
100112
@rem Skip msi generation if not requested
101113
if not defined msi goto run

0 commit comments

Comments
 (0)