-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathOutput Interception.i7x
684 lines (545 loc) · 38.5 KB
/
Output Interception.i7x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
Version 1 of Output Interception (for Glulx only) by Brady Garvin begins here.
"Facilities for detecting and recording output."
Include Runtime Checks by Brady Garvin.
Include Low-Level Operations by Brady Garvin.
Include Low-Level Text by Brady Garvin.
Include Low-Level Linked Lists by Brady Garvin.
Include Low-Level Hash Tables by Brady Garvin.
Include Glulx Text Decoding by Brady Garvin.
Include Glulx Runtime Instrumentation Framework by Brady Garvin.
Include Glk Interception by Brady Garvin.
Use authorial modesty.
Book "Copyright and License"
[Copyright 2013 Brady J. Garvin]
[This extension is released under the Creative Commons Attribution 3.0 Unported License (CC BY 3.0) so that it can qualify as a public Inform extension. See the LICENSE file included in the release for further details.]
Book "Extension Information"
Chapter "Use Options" - unindexed
Use a echo stream interception hash table size of at least 31 translates as (- Constant OI_ECHO_STREAM_HASH_SIZE={N}; -).
To decide what number is the echo stream interception hash table size: (- OI_ECHO_STREAM_HASH_SIZE -).
Chapter "Rulebooks"
The output interception rules are [rulebook is] a rulebook.
Book "Output Interception"
Chapter "Handler Variables"
Section "Output Interception Types"
An output interception type is a kind of value. The output interception types are invalid textual output, valid textual output, save file output, endless output, style change, endless style change, and cursor movement. The specification of an output interception type is "The output interception types represent the various kinds of output that Output Interception can detect. 'Invalid textual output' signifies that the story is about to write an invalid string to a stream, 'valid textual output' that it is about to write a valid string (available at 'the address of the intercepted output', a Unicode array whose length in words is 'the length of the intercepted output'), 'save file output' that it is about to write a save file, 'endless output' that it has created an echo cycle and is attempting to send an infinite amount of output, 'style change' and 'endless style change' for changes to output style, perhaps in an echo cycle, and finally 'cursor movement', which signifies no printed output, but a change in where that output will appear."
Section "Mutable Handler Variables" - unindexed
The variable holding the type of the intercepted output is an output interception type that varies.
The variable holding the input-output system of the intercepted output is a number that varies.
The variable holding the stream of the intercepted output is a number that varies.
The variable holding the address of the intercepted output is a number that varies.
The variable holding the length of the intercepted output is a number that varies.
Section "Other Private Storage" - unindexed
Include (-
Array oi_loneCharacter --> 1;
-).
To decide what number is the address for storing an intercepted lone character: (- oi_loneCharacter -).
Section "Convenience Handler Variable Mutators" - unindexed
To set the intercepted output to the lone Latin-1 character (C - a number):
let the character be C;
if the character is greater than 255:
now the character is 63 [question mark];
set the intercepted output to the lone Unicode character the character.
To set the intercepted output to the lone Unicode character (C - a number):
write the integer C to address the address for storing an intercepted lone character;
now the variable holding the address of the intercepted output is the address for storing an intercepted lone character;
now the variable holding the length of the intercepted output is one.
To set the intercepted output to the C-style Latin-1 string at address (A - a number):
now the variable holding the address of the intercepted output is A;
let the search extent be the size of memory minus A;
now the variable holding the length of the intercepted output is the index of the byte zero in the search extent bytes at address A.
To set the intercepted output to the C-style Unicode string at address (A - a number):
now the variable holding the address of the intercepted output is A;
let the search extent be the size of memory minus A;
now the variable holding the length of the intercepted output is the index of the integer zero in the search extent divided by four integers at address A.
Section "Immutable Handler Variables"
To decide what output interception type is the type of the intercepted output: (- (+ the variable holding the type of the intercepted output +) -).
To decide what number is the input-output system of the intercepted output: (- (+ the variable holding the input-output system of the intercepted output +) -).
To decide what number is the stream of the intercepted output: (- (+ the variable holding the stream of the intercepted output +) -).
To decide what number is the address of the intercepted output: (- (+ the variable holding the address of the intercepted output +) -).
To decide what number is the length of the intercepted output: (- (+ the variable holding the length of the intercepted output +) -).
Chapter "Latin-1-to-Unicode Expansion" - unindexed
To decide what number is the address of a new Latin-1-to-Unicode expansion of the (N - a number) bytes at address (A - a number):
let the result be a possibly zero-length memory allocation of N times four bytes;
let the target be the result;
repeat with the index running over the half-open interval from zero to N:
let the character be the byte at address A plus the index;
write the integer the character to address the target;
increase the target by four;
decide on the result.
Chapter "Interception Dispatch" - unindexed
To intercept the output (this is intercepting the output):
traverse the output interception rulebook;
if the input-output system of the intercepted output is the Glk input-output system:
let the echo stream list be the a new list of streams echoing the stream of the intercepted output;
if the echo stream list is an invalid linked list:
if the type of the intercepted output is style change:
now the variable holding the type of the intercepted output is endless style change;
otherwise:
now the variable holding the type of the intercepted output is endless output;
traverse the output interception rulebook;
otherwise:
repeat with the echo stream running through the number keys of the echo stream list:
now the variable holding the stream of the intercepted output is the echo stream;
traverse the output interception rulebook;
delete the echo stream list.
A GRIF shielding rule (this is the shield intercepting the output against instrumentation rule):
shield intercepting the output against instrumentation.
Chapter "Indirect Output" - unindexed
Section "Temporary Space" - unindexed
Include (-
Array oi_streamPops --> 8;
-).
To decide what number is where stack pops from streaming instructions are temporarily saved for output interception: (- oi_streamPops -).
Section "Convenience Phrases for Indirect Output" - unindexed
Include (-
[ oi_getIOSystem;
@getiosys sp 0;
@return sp;
];
-).
To decide what number is the current input-output system: (- oi_getIOSystem() -).
To decide what number is the null input-output system: (- 0 -).
To decide what number is the filter input-output system: (- 1 -).
To decide what number is the Glk input-output system: (- 2 -).
To set the stream of the intercepted output to the current stream during indirect output:
if the current input-output system is the Glk input-output system:
prepare a spontaneous Glk invocation;
write the function selector 72 [glk_stream_get_current] to the current Glk invocation;
write the argument count zero to the current Glk invocation;
delegate the current Glk invocation to the Glk layer after output interception;
now the variable holding the stream of the intercepted output is the result of the Glk invocation just delegated;
otherwise:
now the variable holding the stream of the intercepted output is zero.
Section "Instrumentation Callbacks" - unindexed
To intercept the output of a streamchar with argument (C - a number) (this is intercepting the output of a streamchar):
now the variable holding the type of the intercepted output is valid textual output;
now the variable holding the input-output system of the intercepted output is the current input-output system;
set the stream of the intercepted output to the current stream during indirect output;
set the intercepted output to the lone Latin-1 character the bitwise and of C and 255;
intercept the output.
To intercept the output of a streamunichar with argument (C - a number) (this is intercepting the output of a streamunichar):
now the variable holding the type of the intercepted output is valid textual output;
now the variable holding the input-output system of the intercepted output is the current input-output system;
set the stream of the intercepted output to the current stream during indirect output;
set the intercepted output to the lone Unicode character C;
intercept the output.
The streamnum argument to intercept is a number that varies.
To intercept the output of a streamnum with argument (N - a number) (this is intercepting the output of a streamnum):
now the variable holding the type of the intercepted output is valid textual output;
now the variable holding the input-output system of the intercepted output is the current input-output system;
set the stream of the intercepted output to the current stream during indirect output;
now the streamnum argument to intercept is N;
let the digits be a new synthetic text copied from "[the streamnum argument to intercept]";
now the variable holding the address of the intercepted output is the address of a new Latin-1-to-Unicode expansion of the length of the digits bytes at address the character array address of the synthetic text the digits;
now the variable holding the length of the intercepted output is the length of the digits;
delete the synthetic text the digits;
intercept the output;
free the possibly zero-length memory allocation at address the address of the intercepted output.
To begin capturing Unicode in the (N - a number) integers at address (A - a number): (-
@getiosys sp sp; ! save the input-output system
@setiosys 2 0; ! switch to Glk
@glk 72 0 sp; ! save the current stream
glk_stream_set_current(glk_stream_open_memory_uni({A},{N},filemode_Write,0));
-).
To end capturing Unicode in integers at an address: (-
glk_stream_close(glk_stream_get_current(),0);
@glk 71 1 0; ! restore the current stream
@stkswap; ! restore the input-output system
@setiosys sp sp; ! restore the input-output system
-).
To intercept the output of a streamstr with argument (A - a number) (this is intercepting the output of a streamstr):
now the variable holding the input-output system of the intercepted output is the current input-output system;
set the stream of the intercepted output to the current stream during indirect output;
now the variable holding the length of the intercepted output is the character length of the substitution-free A converted to some text;
if the length of the intercepted output is -1:
now the variable holding the type of the intercepted output is invalid textual output;
intercept the output;
otherwise:
now the variable holding the type of the intercepted output is valid textual output;
let the indicator be the byte at address A;
if the indicator is:
-- 224: [null-terminated Latin-1]
now the variable holding the address of the intercepted output is the address of a new Latin-1-to-Unicode expansion of the length of the intercepted output bytes at address A plus one;
intercept the output;
free the possibly zero-length memory allocation at address the address of the intercepted output;
-- 225: [compressed]
now the variable holding the address of the intercepted output is a possibly zero-length memory allocation of the length of the intercepted output times four bytes;
begin capturing Unicode in the length of the intercepted output integers at address the address of the intercepted output;
repeat with the decoding vertex running with paranoia through the compressed A converted to some text:
say "[the decoding vertex]";
end capturing Unicode in integers at an address;
intercept the output;
free the possibly zero-length memory allocation at address the address of the intercepted output;
-- 226: [null-terminated Unicode]
now the variable holding the address of the intercepted output is A plus one;
intercept the output.
To intercept the output of a save with argument (S - a number) (this is intercepting the output of a save):
now the variable holding the type of the intercepted output is save file output;
now the variable holding the input-output system of the intercepted output is the current input-output system;
now the variable holding the stream of the intercepted output is S;
intercept the output.
Section "Instrumentation Callback Shielding"
A GRIF shielding rule (this is the shield output interception calls against instrumentation rule):
shield intercepting the output of a streamchar against instrumentation;
shield intercepting the output of a streamunichar against instrumentation;
shield intercepting the output of a streamnum against instrumentation;
shield intercepting the output of a streamstr against instrumentation;
shield intercepting the output of a save against instrumentation.
Section "Instrumentation"
A GRIF instrumentation rule (this is the intercept the output of streamchar instructions rule):
repeat with the instruction vertex running through occurrences of the operation code op-streamchar in the scratch space:
cleanse the instruction vertex of stack pops using the array at address where stack pops from streaming instructions are temporarily saved for output interception;
let the addressing mode be the addressing mode of parameter zero of the instruction vertex;
let the parameter be parameter zero of the instruction vertex;
let the interception instruction vertex be a new artificial instruction vertex for a one-argument call to the function at address the function address of intercepting the output of a streamchar with mode the addressing mode and parameter the parameter and return mode the zero-or-discard addressing mode;
insert the interception instruction vertex before the instruction vertex.
A GRIF instrumentation rule (this is the intercept the output of streamunichar instructions rule):
repeat with the instruction vertex running through occurrences of the operation code op-streamunichar in the scratch space:
cleanse the instruction vertex of stack pops using the array at address where stack pops from streaming instructions are temporarily saved for output interception;
let the addressing mode be the addressing mode of parameter zero of the instruction vertex;
let the parameter be parameter zero of the instruction vertex;
let the interception instruction vertex be a new artificial instruction vertex for a one-argument call to the function at address the function address of intercepting the output of a streamunichar with mode the addressing mode and parameter the parameter and return mode the zero-or-discard addressing mode;
insert the interception instruction vertex before the instruction vertex.
A GRIF instrumentation rule (this is the intercept the output of streamnum instructions rule):
repeat with the instruction vertex running through occurrences of the operation code op-streamnum in the scratch space:
cleanse the instruction vertex of stack pops using the array at address where stack pops from streaming instructions are temporarily saved for output interception;
let the addressing mode be the addressing mode of parameter zero of the instruction vertex;
let the parameter be parameter zero of the instruction vertex;
let the interception instruction vertex be a new artificial instruction vertex for a one-argument call to the function at address the function address of intercepting the output of a streamnum with mode the addressing mode and parameter the parameter and return mode the zero-or-discard addressing mode;
insert the interception instruction vertex before the instruction vertex.
A GRIF instrumentation rule (this is the intercept the output of streamstr instructions rule):
repeat with the instruction vertex running through occurrences of the operation code op-streamstr in the scratch space:
cleanse the instruction vertex of stack pops using the array at address where stack pops from streaming instructions are temporarily saved for output interception;
let the addressing mode be the addressing mode of parameter zero of the instruction vertex;
let the parameter be parameter zero of the instruction vertex;
let the interception instruction vertex be a new artificial instruction vertex for a one-argument call to the function at address the function address of intercepting the output of a streamstr with mode the addressing mode and parameter the parameter and return mode the zero-or-discard addressing mode;
insert the interception instruction vertex before the instruction vertex.
A GRIF instrumentation rule (this is the intercept the output of save instructions rule):
repeat with the instruction vertex running through occurrences of the operation code op-save in the scratch space:
cleanse the instruction vertex of stack pops using the array at address where stack pops from streaming instructions are temporarily saved for output interception;
let the addressing mode be the addressing mode of parameter zero of the instruction vertex;
let the parameter be parameter zero of the instruction vertex;
let the interception instruction vertex be a new artificial instruction vertex for a one-argument call to the function at address the function address of intercepting the output of a save with mode the addressing mode and parameter the parameter and return mode the zero-or-discard addressing mode;
insert the interception instruction vertex before the instruction vertex.
Chapter "Direct Output" - unindexed
Section "Echo Stream Tracing" - unindexed
The output interception window stream hash table is a hash table that varies.
The output interception echo stream hash table is a hash table that varies.
[Decides on an invalid linked list if there is an echo cycle.]
To decide what linked list is a new list of streams echoing (S - a number):
let the pending invocation be an invalid Glk invocation;
if the Glk layers are in the pre-call state:
now the pending invocation is a new copy of the current Glk invocation;
otherwise:
prepare a spontaneous Glk invocation;
let the result be an empty linked list;
let the window stream hash table be a new hash table with the echo stream interception hash table size buckets;
let the window be zero;
repeat until a break:
write the function selector 32 [glk_window_iterate] to the current Glk invocation;
write the argument count two to the current Glk invocation;
write the window to argument number zero of the current Glk invocation;
write zero to argument number one of the current Glk invocation;
delegate the current Glk invocation to the Glk layer after output interception;
now the window is the result of the Glk invocation just delegated;
if the window is zero:
break;
prepare a spontaneous Glk invocation;
write the function selector 44 [glk_window_get_stream] to the current Glk invocation;
write the argument count one to the current Glk invocation;
write the window to argument number zero of the current Glk invocation;
delegate the current Glk invocation to the Glk layer after output interception;
let the stream be the result of the Glk invocation just delegated;
prepare a spontaneous Glk invocation;
insert the key the stream and the value the window into the window stream hash table;
let the current stream be S;
repeat until a break:
now the window is the first number value matching the key the current stream in the window stream hash table or zero if there are no matches;
if the window is zero:
break;
prepare a spontaneous Glk invocation;
write the function selector 46 [glk_window_get_echo_stream] to the current Glk invocation;
write the argument count one to the current Glk invocation;
write the window to argument number zero of the current Glk invocation;
delegate the current Glk invocation to the Glk layer after output interception;
now the current stream is the result of the Glk invocation just delegated;
if the current stream is zero:
break;
if the result contains the key the current stream:
delete the result;
delete the window stream hash table;
unless the pending invocation is an invalid Glk invocation:
prepare another Glk invocation from the pending invocation;
delete the pending invocation;
decide on an invalid linked list;
push the key the current stream onto the result;
delete the window stream hash table;
unless the pending invocation is an invalid Glk invocation:
prepare another Glk invocation from the pending invocation;
delete the pending invocation;
decide on the result.
Section "Convenience Phrases for Direct Output" - unindexed
To set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length:
if the length of the intercepted output is -1:
now the variable holding the type of the intercepted output is invalid textual output;
otherwise:
now the variable holding the type of the intercepted output is valid textual output;
now the variable holding the input-output system of the intercepted output is the Glk input-output system.
To set the stream of the intercepted output to the current stream during direct output:
let the pending invocation be a new copy of the current Glk invocation;
write the function selector 72 [glk_stream_get_current] to the current Glk invocation;
write the argument count zero to the current Glk invocation;
delegate the current Glk invocation to the Glk layer after output interception;
now the variable holding the stream of the intercepted output is the result of the Glk invocation just delegated;
prepare another Glk invocation from the pending invocation;
delete the pending invocation.
[//// Question: are cursor movements echoed? Right now we assume not.]
To set the stream of the intercepted output to the window stream of (W - a number) during direct output:
let the pending invocation be a new copy of the current Glk invocation;
write the function selector 44 [glk_window_get_stream] to the current Glk invocation;
write the argument count one to the current Glk invocation;
write W to argument number zero of the current Glk invocation;
delegate the current Glk invocation to the Glk layer after output interception;
now the variable holding the stream of the intercepted output is the result of the Glk invocation just delegated;
prepare another Glk invocation from the pending invocation;
delete the pending invocation.
Section "Glk Interception Layer" - unindexed
The Glk layer after output interception is a Glk layer that varies.
To intercept output via direct Glk calls (this is intercepting output via direct Glk calls):
if the function selector of the current Glk invocation is:
-- 43: [glk_window_move_cursor]
ensure that the current Glk invocation has at least one argument;
let the window be argument number zero of the current Glk invocation;
set the stream of the intercepted output to the window stream of the window during direct output;
now the variable holding the input-output system of the intercepted output is the Glk input-output system;
now the variable holding the type of the intercepted output is cursor movement;
intercept the output;
-- 128: [glk_put_char]
ensure that the current Glk invocation has at least one argument;
set the stream of the intercepted output to the current stream during direct output;
set the intercepted output to the lone Latin-1 character argument number zero of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 296: [glk_put_char_uni]
ensure that the current Glk invocation has at least one argument;
set the stream of the intercepted output to the current stream during direct output;
set the intercepted output to the lone Unicode character argument number zero of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 129: [glk_put_char_stream]
ensure that the current Glk invocation has at least two arguments;
now the variable holding the stream of the intercepted output is argument number zero of the current Glk invocation;
set the intercepted output to the lone Latin-1 character argument number one of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 299: [glk_put_char_stream_uni]
ensure that the current Glk invocation has at least two arguments;
now the variable holding the stream of the intercepted output is argument number zero of the current Glk invocation;
set the intercepted output to the lone Unicode character argument number one of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 130: [glk_put_string]
ensure that the current Glk invocation has at least one argument;
set the stream of the intercepted output to the current stream during direct output;
set the intercepted output to the C-style Latin-1 string at address argument number zero of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 297: [glk_put_string_uni]
ensure that the current Glk invocation has at least one argument;
set the stream of the intercepted output to the current stream during direct output;
set the intercepted output to the C-style Unicode string at address argument number zero of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 131: [glk_put_string_stream]
ensure that the current Glk invocation has at least two arguments;
now the variable holding the stream of the intercepted output is argument number zero of the current Glk invocation;
set the intercepted output to the C-style Latin-1 string at address argument number one of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 300: [glk_put_string_stream_uni]
ensure that the current Glk invocation has at least two arguments;
now the variable holding the stream of the intercepted output is argument number zero of the current Glk invocation;
set the intercepted output to the C-style Unicode string at address argument number one of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 132: [glk_put_buffer]
ensure that the current Glk invocation has at least two arguments;
set the stream of the intercepted output to the current stream during direct output;
now the variable holding the address of the intercepted output is the address of a new Latin-1-to-Unicode expansion of the argument number one of the current Glk invocation bytes at address argument number zero of the current Glk invocation;
now the variable holding the length of the intercepted output is argument number one of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
free the possibly zero-length memory allocation at address the address of the intercepted output;
-- 298: [glk_put_buffer_uni]
ensure that the current Glk invocation has at least two arguments;
set the stream of the intercepted output to the current stream during direct output;
now the variable holding the address of the intercepted output is argument number zero of the current Glk invocation;
now the variable holding the length of the intercepted output is argument number one of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 133: [glk_put_buffer_stream]
ensure that the current Glk invocation has at least three arguments;
now the variable holding the stream of the intercepted output is argument number zero of the current Glk invocation;
now the variable holding the address of the intercepted output is the address of a new Latin-1-to-Unicode expansion of the argument number two of the current Glk invocation bytes at address argument number one of the current Glk invocation;
now the variable holding the length of the intercepted output is argument number two of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
free the possibly zero-length memory allocation at address the address of the intercepted output;
-- 301: [glk_put_buffer_stream_uni]
ensure that the current Glk invocation has at least two arguments;
now the variable holding the stream of the intercepted output is argument number zero of the current Glk invocation;
now the variable holding the address of the intercepted output is argument number one of the current Glk invocation;
now the variable holding the length of the intercepted output is argument number two of the current Glk invocation;
set the type and input-output system of the intercepted output to textual output under Glk with validity determined by the output length;
intercept the output;
-- 134: [glk_set_style]
ensure that the current Glk invocation has at least one argument;
set the stream of the intercepted output to the current stream during direct output;
now the variable holding the input-output system of the intercepted output is the Glk input-output system;
now the variable holding the type of the intercepted output is style change;
intercept the output;
-- 135: [glk_set_style_stream]
ensure that the current Glk invocation has at least two arguments;
now the variable holding the stream of the intercepted output is argument number zero of the current Glk invocation;
now the variable holding the input-output system of the intercepted output is the Glk input-output system;
now the variable holding the type of the intercepted output is style change;
intercept the output;
delegate the current Glk invocation to the Glk layer after output interception.
Section "Glk Interception Layering Rule"
A last Glk layering rule (this is the intercept output via direct Glk calls rule):
install intercepting output via direct Glk calls as a Glk layer whose notifications are handled by the default value of phrase Glk layer notification -> nothing and let the Glk layer after output interception be the layer it should delegate to.
Chapter "Stream Classification"
To decide whether (S - a number) is a window stream according to output interception:
let the pending invocation be an invalid Glk invocation;
if the Glk layers are in the pre-call state:
now the pending invocation is a new copy of the current Glk invocation;
otherwise:
prepare a spontaneous Glk invocation;
let the window be zero;
repeat until a break:
write the function selector 32 [glk_window_iterate] to the current Glk invocation;
write the argument count two to the current Glk invocation;
write the window to argument number zero of the current Glk invocation;
write zero to argument number one of the current Glk invocation;
delegate the current Glk invocation to the Glk layer after output interception;
now the window is the result of the Glk invocation just delegated;
if the window is zero:
unless the pending invocation is an invalid Glk invocation:
prepare another Glk invocation from the pending invocation;
delete the pending invocation;
decide no;
prepare a spontaneous Glk invocation;
write the function selector 44 [glk_window_get_stream] to the current Glk invocation;
write the argument count one to the current Glk invocation;
write the window to argument number zero of the current Glk invocation;
delegate the current Glk invocation to the Glk layer after output interception;
if S is the result of the Glk invocation just delegated:
unless the pending invocation is an invalid Glk invocation:
prepare another Glk invocation from the pending invocation;
delete the pending invocation;
decide yes;
prepare a spontaneous Glk invocation.
Output Interception ends here.
---- DOCUMENTATION ----
Chapter: Synopsis
Output Interception is a support extension for debugging extensions that need to
unintrusively observe a story's textual output.
Details are in the following chapters.
Chapter: Usage
Every time the story is about to print any output, even to memory or a file,
Output Interception traverses
the output interception rulebook
so that rules in that rulebook can preview the data and respond if necessary.
Output interception rules should always consult
the type of the intercepted output
first; this is a phrase deciding on a value of the kind
an output interception type
At the moment there are five possibilities:
invalid textual output
means that the story is about to print invalid text. Unless the interpreter is
particularly forgiving, the story will probably crash.
valid textual output
on the other hand, means that the story is about the print valid text. That
text is stored as a sequence of Unicode code points at
the address of the intercepted output
and the code point count is given by
the length of the intercepted output
A rule can inspect the code points using the memory-accessing phrases from
Low-Level Operations. It must not attempt to change them.
The third possibility,
save file output
means that the story is emitting a save file. The contents of the save file are
not available for inspection because the save hasn't occurred yet, and any
actions taken by the interception rules will alter them.
The fourth,
endless output
means that the story has manipulated the I/O system in such a way that the
upcoming output will be infinite (for instance, by building an echo stream cycle
under Glk). A story crash---or possibly an interpreter crash---is imminent.
And the fifth and last possibility,
cursor movement
means that output on the given stream will now appear at a different location.
After determining the interception type, an output interception rule may also be
interested in
the input-output system of the intercepted output
The input-output systems that can be referred to by name are
the null input-output system
the filter input-output system
and
the Glk input-output system
See the Glulx specification for more details. Most stories use Glk exclusively,
but some interpreters, such as FyreVM, support additional I/O systems, which the
story may test for.
And finally, if output was written through the Glk I/O system,
the stream of the intercepted output
will be the opaque reference to the stream where the output was written. We can
compare these values between calls to determine whether two pieces of output
were sent to the same place. We can also ask
if (S - a number) is a window stream according to output interception:
....
if we only want to deal with streams that print to windows.
Chapter: Requirements, Limitations, and Bugs
This version was tested with Inform 6G60. It will probably function on newer
versions, and it may function under slightly older versions, though there is no
guarantee.
I/O is written as input-output in the code since Inform sometimes treats the
slash as semantic.
Prompts printed by the interpreter for saves and restores are not intercepted.
The null I/O system, filter I/O, and Glk are the only I/O system currently
recognized. FyreVM support may be added in the future if there is enough
interest.
The Glulx specification allows the string decoding table to contain calls to
Glulx functions (see Section 1.6.1.4 of the Glulx specification). The validity
of these calls will not checked, nor will their output be captured correctly.
As best I can tell, this limitation only affects authors who store routine
addresses in I6 printing variables, and code of this sort will break on the
Z-machine, so it's not a good idea anyway.
Section: Regarding bugs
If you encounter a bug, check first on the project website
(https://github.com/i7/i7grip) to see whether a newer version of this extension
is available. If, even using the latest version, the fault remains, please file
a bug report: On the website, choose "Issues" from the toolbar and click on "New
Issue".
I will try to respond quickly, at least with an estimate of when the bug might
be fixed, though sometimes I am away from the internet for a week or two at a
time.
Chapter: Acknowledgements
Output Interception was prepared as part of the Glulx Runtime Instrumentation
Project (https://github.com/i7/i7grip). For this first edition of the project,
special thanks go to these people, in chronological order:
- Graham Nelson, Emily Short, and others, not only for Inform, but also for the
countless hours the high-quality technical documentation saved me and for the
work that made the Glulx VM possible,
- Andrew Plotkin for the Glulx VM and the Glk library, as well as their clear,
always up-to-date specifications,
- Jacqueline Lott, David Welbourn, and all of the other attendees for Club
Floyd, my first connection to the interactive fiction community,
- Jesse McGrew and Emily Short for getting me involved with Inform 7,
- all of the Inform 7 developers for their hard work, the ceaseless flow of
improvements, and their willingness to take me on as a collaborator,
- Ron Newcomb and Esteban Montecristo for the idea to write Call Stack Tracking
and Verbose Diagnostics,
- Roger Carbol, Jesse McGrew, Michael Martin, Dan Shiovitz, Johnny Rivera, and
everyone else for their helpful comments on ifMUD's I6 and I7 channels,
- Esteban Montecristo, for invaluable alpha testing,
- and all of the beta testers who are reading this.