Releases: cleobuline/hc
Release list
HC-0.7.7
HC-0.7.6
HC-0.7.6 — the old expression engine is gone, and stack icons come back
The old expression engine that HC still fell back on is now removed:
1,800 lines out, and the test suite gives the same answers without them.
Real stacks found four more gaps along the way, and all four are fixed.
And HC can now open a MacBinary file (.bin), which keeps a classic
Mac file whole: the stack and its icons.
At a glance
| 0.7.5 | 0.7.6 | |
|---|---|---|
| the old expression engine | still there, could be switched off | removed |
one click during if the mouseClick then add 1 to n |
counted on every pass of the loop (235 for one click) | counted once, as in HyperCard |
the top of card window |
"a number is expected here" | served, measured from the screen |
f(1,,3), myHandler 1,,3 |
"expression expected" | an empty parameter, as send already did |
PopUpMenu(list,,top,left) |
unknown function | imitated: a real popup menu |
opening a .bin |
not recognised | the stack and its icons |
the hcVersion, "About HC" |
0.7.5 |
0.7.6 |
Installing: macOS will say the app is damaged. It is not.
This release is not notarised. macOS quarantines anything downloaded from
the internet, and Gatekeeper refuses an app it cannot trace to a notarised
Developer ID — with a message that says the wrong thing:
"HC is damaged and can't be opened. You should move it to the Trash."
The app is not damaged. It is unnotarised, which is a different problem, and
the wording sends people to the Trash for no reason.
Copy HC.app to /Applications, then clear the quarantine flag once:
xattr -dr com.apple.quarantine /Applications/HC.appIt opens normally after that, and the flag does not come back. An app you
build yourself from this repository is never quarantined — it is only the
download that triggers this.
Pendu.stack still sits next to the app in the DMG.
The binary is universal — x86_64 arm64. Deployment target is macOS 10.13.
1. Stack icons, through MacBinary
A classic Mac file has two parts: the data, which holds the stack, and
the resource fork, which holds its icons, pictures and externals. Almost
every way of copying a file off a classic Mac keeps only the first part. That
is why imported stacks showed empty boxes on their icon buttons.
A MacBinary archive (.bin) keeps both parts in one file. Make it
inside the emulator — StuffIt and most utilities of the time offer it —
then open it in HC with File → Open…, the same command as for any other
stack. HC recognises the three kinds on its own:
- an HC stack;
- a HyperCard stack (data part only);
- a
.binholding a HyperCard stack.
The ICON resources become the stack's icons, under their original numbers
and names, so buttons find them again. Tried on Apple's "Stack Templates":
all 53 icon buttons show their picture — 6 icons from the stack itself, 47
from HyperCard's own set, which HC already ships.
If the .bin does not hold a stack, HC says so and names the file type it
found. If the resource fork is damaged, the stack still opens, and the import
dialog says why some buttons are empty.
Not read yet: colour icons (cicn), pictures (PICT), sounds. A .bin
double-clicked in the Finder or dropped on the Dock icon is not tried;
File → Open… is the way.
2. The old expression engine is removed
In 0.7.5 the old engine could be switched off, and the suite lost nothing
without it. The engine itself is now gone: 28 functions, plus 8 that were
already dead, leftovers of an even older line executor. Nothing called them
any more. They called one another in a closed loop, which is why the compiler
never flagged them as unused.
Several real stacks were played in HC after the removal: nothing to report.
If a stack somewhere relied on a turn of phrase that only the old engine
understood, it will now say so with a named error — "object not found",
"unknown function" — instead of quietly answering.
3. the mouseClick counts each click once
repeat until the ticks - t > 300
if the mouseClick then add 1 to n
end repeat
One click during this loop gave 235: the same click was seen again on
every pass. It now gives 1, and four clicks give 4.
Measured in HyperCard under Basilisk II, with the same script: the same
answers, and a click held down also counts once. The reference says that
the mouseClick waits for the button to be released; HyperCard does not, and
neither does HC.
4. The card window knows where it is
put (bottom of target + top of card window + 1) into tp
This line, from a 1992 stack, raised "a number is expected here", for two
reasons:
- after
card, a rank can be computed —card i + 1is the next card — so
top of card window + 1looked for the card ranked "window + 1".card windownow stops at the wordwindow; - of the card window, only
width,height,rectandlocwere served,
andrectalways read0,0,width,height.
left, top, right, bottom, topLeft, bottomRight and rect are now
measured from the top-left corner of the screen with the menu bar, as the
HyperCard reference describes. That is what lets a script turn a point on
the card into a point on the screen.
5. Minkowski Stack 1: empty parameters, and PopUpMenu
put PopUpMenu(list,,tp,lp) into it
- An empty parameter. The place between two commas is an empty string,
in a function call as in a message.sendalready worked this way. PopUpMenuis an external function (XFCN) by Andrew Gilmartin, whose
68000 code lived in the stack's resource fork and cannot run here. HC
imitates it: a real popup menu, placed where the script asks, returning
the number of the chosen item. A stack that defines its ownPopUpMenu
function keeps precedence.
Palettes (palette "Name", the PLTE resource) are not supported,
by decision: HC could read them but never edit them. A stack that opens one
gets an error at that line, and the rest of it runs.
6. What is not measured is written down as not measured
- The card window values themselves have not been compared with HyperCard.
the loc of card windowis the top-left corner of the window according to
the reference, and the centre of the card in HC: not measured,
unchanged. - A trailing comma —
f(1,),myHandler 1,— counts as one more empty
parameter, as withsend. Not measured in HyperCard. - What the original
PopUpMenureturned when nothing was chosen (0 here),
and what it did with the Menu Manager's other special characters: not
known.
Test status
- C kernel: 274 checks, all green, including under AddressSanitizer,
UndefinedBehaviorSanitizer and LeakSanitizer. New ones include:macbinaire: a MacBinary file and a resource fork built by the test,
damaged files refused, 7,000 damaged variants read without a fault;popupmenuandargvide: the Minkowski line, and empty parameters by
every route;fenetrecarte: the card window, with and without a placed window.
- Zero warnings, gcc at three optimisation levels and clang with Xcode's
families. Zero static-analyser warnings.
Known, and not fixed here
- Palettes (
PLTE), colour icons, pictures and sounds from a resource fork. - A fault inside a user function stops the function but not its caller, which
carries on with an empty value. - Still standing from 0.7:
- named windows, patterns;
- HyperCard 1.x stacks, private-access stacks;
- the proportional period fonts;
CFBundleVersionis still hard-coded to1;- not notarised: see "Installing".
Changes in this release
- #98: the old expression engine
removed;the mouseClick; the card window; empty parameters and
PopUpMenu; icons through MacBinary - this release: version 0.7.6 in all three places, and this note
Full changelog: HC-0.7.5...HC-0.7.6
HC-0.7.5
HC-0.7.5 — the old expression engine can be switched off, and nothing is lost
HC runs HyperTalk on its own engine, but until now it still fell back on an older expression engine whenever it got stuck. This release switches that old engine off on demand, finds out exactly what it was still answering, and moves all of it into the new engine. The whole test suite now gives the same answers with the old engine switched off.
To check that against the real thing, a new torture stack for twisted expressions was played in HyperCard under Basilisk II. It found four places where HC and HyperCard disagreed, and all four are fixed.
And the DMG now comes with a game: Le Pendu, a hangman stack.
At a glance
The four that disagreed are fixed. Fixing the item count turned up a related
fault: writing past the end of a list or a text that ends with its separator
added one separator too many. put "X" into line 4 of two lines ending with a
return gave five lines instead of four. This one predates the release, and it
is fixed for items and lines alike.
HyperCard stops the whole script on an error. The first version of the
stack ran its tests in one loop, and HyperCard stopped at the first expected
error: no more tests, no summary. The error came from inside a do, and it
took the calling loop down with it. The stack now plays one test per idle,
so it survives on both sides.
3. Le Pendu
A hangman game, in French, to try HC on something other than tests:
- a word is drawn at random when the stack opens;
- click the letters, or type them on the keyboard;
- seven mistakes and the little man is hanged.
The gallows is drawn in text. It uses the sounds that already ship inside
HC.app: boing on a mistake, cocorico when you win, canard when you
lose.
The whole game lives in the stack script. To add words, edit the lesMots
function in UPPER CASE, without accents. The stack is built from versioned
sources (tests/donnees/pendu_pile.txt), and a test plays a won game, a lost
game, a repeated letter and a lower-case key before every release.
4. What is not measured is written down as not measured
- A wrong number of arguments is measured for
length()andsqrt(4, 9). The other built-in functions follow the same rule, not measured one by one. the max of "3,9,2"and a stack function in the "the" form (the myFunction of "ok") are refused by HyperCard. HC still answers them, for now.- HyperCard's stop-everything on an error is measured for an error inside a
do. For an error in a command or in a function, it is not measured. The bench is indocs/mesures/erreur_abandon.txt. - Whether HyperCard resets
the itemDelimiterto a comma when a script ends is not measured. HC keeps it. - The object section of the new torture stack has not yet been played in HyperCard on the intended layout.
Test status
- C kernel: 270 checks, all green, including under AddressSanitizer,
UndefinedBehaviorSanitizer and LeakSanitizer. New ones include:
sansv1: every case, with and without the old engine;torture3: the twisted-expression stack, played both ways;separateurfinal: the trailing separator, read and written;pendu: the hangman, played before it ships.
- Zero warnings, gcc at three optimisation levels and clang with Xcode's families. Zero static-analyser warnings.
- The twisted-expression stack, in HC: 125 passed, 0 failed, 2 still to measure. In HyperCard, every expression section agrees.
Known, and not fixed here
- A fault inside a user function stops the function but not its caller, which
carries on with an empty value. HyperCard stops everything, at least for
do. - After a fault raised by a command, the handler carries on and the dialog comes at the end.
visual effectfollowed bygo, withoutlock screen, still plays after the script rather than insidego.- Still standing from 0.7:
- named windows, patterns, resource-fork icons;
- HyperCard 1.x stacks, private-access stacks;
- the proportional period fonts;
CFBundleVersionis still hard-coded to1;- not notarised: see "Installing".
Changes in this release
- #93: what the old engine was
still doing, moved into the new one; the twisted-expression torture stack;
items,
roundand argument counts aligned with HyperCard - #96: Le Pendu, in the DMG
- this release: version 0.7.5 in all three places, and this note
Full changelog: HC-0.7.4...HC-0.7.5
HC-0.7.5 — the old expression engine can be switched off, and nothing is lostHC runs HyperTalk on its own engine, but until now it still fell back on an older expression engine whenever it got stuck. This release switches that old engine off on demand, finds out exactly what it was still answering, and moves all of it into the new engine. The whole test suite now gives the same answers with the old engine switched off.
To check that against the real thing, a new torture stack for twisted expressions was played in HyperCard under Basilisk II. It found four places where HC and HyperCard disagreed, and all four are fixed.
And the DMG now comes with a game: Le Pendu, a hangman stack.
At a glance
0.7.4 0.7.5
the number of items of "a,b," 3 2, as in HyperCard
round(2.5), round(-2.5) 3, -3 2, -2: round half to even, as in HyperCard
length(), sqrt(4, 9) 0, 2 an error, as in HyperCard
sqrt("abc"), exp2("x") 0, 1 an error, like "abc" + 1
the number of cards of bg 2, of buttons of card 3, of bgs of this stack right only through the old engine answered by the new engine
the number of cards of bg 9, with no such background the count of the whole stack an error, as in HyperCard
the charToNum of "a", the random of 6 old engine new engine, same code as charToNum("a")
the zorglub of 3 the text zorglub of 3 an error naming zorglub
put "X" into line 4 of v, where v ends with a return 5 lines, line 4 empty 4 lines
what the DMG contains HC.app HC.app and Pendu.stack
the hcVersion, "About HC" 0.7.4 0.7.5
Installing: macOS will say the app is damaged. It is not.
This release is not notarised. macOS quarantines anything downloaded from the internet, and Gatekeeper refuses an app it cannot trace to a notarised Developer ID — with a message that says the wrong thing:
"HC is damaged and can't be opened. You ...
HC-0.7.4
HC-0.7.4 — the torture stack agrees with HyperCard, line for line
One result sums this release up. HC's torture stack is 108 checks, each carrying an answer measured in real HyperCard running under Basilisk II. It now gives the same report on both sides:
passed failed still to measure
in HyperCard (Basilisk II) 108 0 0
in HC 108 0 0
It took three fixes to get there, and all three come down to which layer HyperCard looks at first. This release also fixes a crash that an outside audit found, and adds clang's static analyser to the CI.
At a glance
0.7.3 0.7.4
field 1, field "Name" with no layer the card field the background field, as in HyperCard
the number of fields card + background, added up the background only
the number of buttons card + background, added up the card only
find, same word on both layers of a card card first background first
select the foundChunk, then the foundChunk still filled empty, as in HyperCard
go first background with no card open crash refused cleanly
static analysis (clang --analyze) not run zero warnings, enforced in CI
torture stack, HC vs HyperCard 2 lines disagreed, 2 still open all 108 agree, none open
the hcVersion, "About HC" 0.7.3 0.7.4
Installing: macOS will say the app is damaged. It is not.
This release is not notarised. macOS quarantines anything downloaded from the internet, and Gatekeeper refuses an app it cannot trace to a notarised Developer ID — with a message that says the wrong thing:
"HC is damaged and can't be opened. You should move it to the Trash."
The app is not damaged. It is unnotarised, which is a different problem, and the wording sends people to the Trash for no reason.
Copy HC.app to /Applications, then clear the quarantine flag once:
xattr -dr com.apple.quarantine /Applications/HC.app
It opens normally after that, and the flag does not come back. An app you build yourself from this repository is never quarantined — it is only the download that triggers this.
The binary is universal — x86_64 arm64. Deployment target is macOS 10.13.
- A field with no layer is a background field
field "Total" with no card or bkgnd in front of it is the most common phrase in stacks from 1987. HyperCard reads it as the background field. HC read it as the card field. A stack with a field of the same number or name on both layers therefore read and wrote the wrong text in HC, with no error at all.
Measured in HyperCard with a full bench, played twice:
a field with no layer is on the background: reading it, writing it, hiding it and the name of field 1 all go to the background;
a button with no layer is on the card, which HC already did;
the number of fields counts the background fields only, and the number of buttons the card buttons only. HC added both layers together, so it answered 7 and 3 where HyperCard answered 3 and 2;
a name that is not on the background raises an error in HyperCard.
HC now follows the same rule, with one deliberate improvement. Where HyperCard would stop with an error, HC falls back on the card field of that name. The answer is the same wherever HyperCard succeeds, and stacks written in HC, where field "X" often means a card field, keep working. A layer you write out (card field, bg field) never falls back.
If you wrote stacks in HC: a loop over the number of fields that meant the card fields now counts the background ones. Write the number of card fields, as HyperCard requires.
2. find looks at the background first
When the same word is in a card field and a background field of the same card, HyperCard finds the background one first, then the card one. HC did it the other way round. This was also the cause of the only two lines of the torture stack that had disagreed with HyperCard since September 27: the search took a different route from there on.
It is the same priority as section 1. The two differences had a single cause.
3. select clears the found text
In HyperCard, right after find "x" then select the foundChunk, the foundChunk is empty, and so are the foundText, the foundField and the foundLine. Any select of text in a field does the same. HC kept them until the next search, and its source code said so without ever having measured it. select empty clears nothing, in HyperCard or in HC.
HC now empties what these four functions return. The search position itself is kept, so the next find carries on from where it was: what a select does to it has not been measured.
The two last open questions of the torture stack were settled on the way. After a find that fails, the next one starts again from the current card, and the foundChunk names the background field. HC already did both.
4. What an outside audit found
An outside audit, made with clang's static analyser, reported four points. Each was checked before anything was changed:
a real crash: go first background typed with no card open — before the first stack is opened, or after the current one is closed — read through a null pointer. It is reproduced under AddressSanitizer, fixed, and held by a test;
one point cannot happen, and the code now states its assumption once;
one is right: the function that exits on an allocation failure is now marked as never returning;
one is a false alarm, and the fix the audit recommended would have broken the reading of older stack files and of paint. Measured: two tests fail with it. The invariant is written down instead.
The analyser now reports zero warnings on the kernel. make analyse requires that, and it runs as its own CI job. It was checked that it does fail on the old code.
5. What is not measured is written down as not measured
Applied for consistency, not measured: a field designated by id (field id 1) or by an ordinal (first field) follows the same layer rule as field 1.
A click on text in a field does not clear the found text. Only a script select does. What a click does in HyperCard is not measured.
select before … and select after … are treated like any other select, without a measurement.
Deduced from the torture stack, not measured: in HyperCard, writing into another field seems to lose the current text selection. HC keeps it.
The Cocoa layer still has no automated tests. CI only proves it compiles.
Test status
C kernel: 265 checks, all green, including under AddressSanitizer, UndefinedBehaviorSanitizer and LeakSanitizer. Four new ones: sanscarte, couchefond, findcouches, findselect.
Zero warnings, gcc at three optimisation levels and clang with Xcode's families. Zero static-analyser warnings.
The torture stack: 108 / 0 / 0 in HyperCard and in HC.
Known, and not fixed here
After a fault raised by a command (hide, set, a message nobody handles), the handler carries on and the dialog comes at the end. HyperCard probably stops on the spot — to play in HyperCard.
A fault inside a user function stops the function but not its caller.
visual effect followed by go, without lock screen, still plays after the script rather than inside go.
Keystrokes typed during a script are put back at the head of the queue, so they now come before a click that preceded them.
Still standing from 0.7: named windows, patterns, resource-fork icons, HyperCard 1.x, private-access stacks, the proportional period fonts. CFBundleVersion is still hard-coded to 1. Not notarised; see "Installing".
Changes in this release
#87 — the outside audit: the no-card crash, make analyse and its CI job; and an inventory of what an App Store sandbox would break, kept for later (5bb18a6, 8c852ed)
#88 — a field with no layer is a background field; the field and button counts follow (c78af21)
#89 — find looks at the background first (e60ac10)
#90 — select clears the found text; the torture stack's last questions settled (1e42c32, 23ed3b3)
#91 — the torture stack agrees with HyperCard, 108 / 0 / 0 (8638959)
this release — version 0.7.4 in all three places, and this note
Full changelog: HC-0.7.3...HC-0.7.4
HC-0.7.3
HC-0.7.3 — Cmd-period stops a script
One subject: you can now stop a running script. Press Cmd-. and HC stops the script, along with every handler that called it.
Until now nothing could stop a script. A repeat forever ran until the
ten-million-iteration cap, or until you quit the app — losing whatever had not
been saved.
The project also gets a README, in French and English, and a licence: MIT.
At a glance
Installing: macOS will say the app is damaged. It is not.
This release is not notarised. macOS quarantines anything downloaded from the internet, and Gatekeeper refuses an app it cannot trace to a notarised Developer ID — with a message that says the wrong thing:
"HC is damaged and can't be opened. You should move it to the Trash."
The app is not damaged. It is unnotarised, which is a different problem, and the wording sends people to the Trash for no reason.
Copy HC.app to /Applications, then clear the quarantine flag once:
xattr -dr com.apple.quarantine /Applications/HC.appIt opens normally after that, and the flag does not come back. An app you build yourself from this repository is never quarantined — it is only the download that triggers this.
The binary is universal — x86_64 arm64. Deployment target is macOS 10.13.
1. Cmd-period
"We need a Cmd-. — it's essential."
What it does. Cmd-. stops the running handler and every handler that
called it, the whole chain, without an error dialog. A real error that
happened before the stop is still reported. Loops and wait stop at once.
Typed in the message box, repeat forever stops too.
A stopped function leaves no damage. A HyperTalk function stopped mid-way
returns empty. Without care, put total() into field "Sum" would have
finished its line with that empty value and wiped the field. The line that
called the function does not run, and the field stays as it was.
Why there was none. There were two missing pieces:
- The kernel had no way to stop a script at all.
- The key never arrived. During a script, HC keeps the window alive, but nothing takes keystrokes out of the queue: they wait for the script to end. Cmd-. waited for the end of the very script it was meant to stop.
Now:
- Kernel: each handler in the chain checks for the request before its next
line, and every loop and
waitchecks too. The request clears once nothing is running. Pressed when no script runs, Cmd-. does nothing, and it cannot stop the next script. - App: while a script runs, HC looks for Cmd-. in the event queue up to sixty times a second. The other keystrokes are put back, in order, and handled after the script as before. Cmd-. is recognised by its character, so it works on an AZERTY keyboard, where the period needs Shift.
Tried in HC by its author: "it works very well."
2. A README and a licence
The repository had neither. README.md (French) and README.en.md (English)
now say in a few lines what HC is: HyperCard's scripts run as they are, 99.7 %
of a corpus of real stacks, and HC adds colour — paint, icons, and
backColor / foreColor / textColor on objects.
The licence is MIT: free to use, modify and redistribute, provided the authors are credited — Patricia Benedetto and Claude (Anthropic).
3. What is not measured is written down as not measured
- What HyperCard does on Cmd-. is not measured: a dialog, a beep, or
nothing? Do the callers stop too? HC assumes everything stops, silently. To
play in HyperCard:
on mouseUpput "before" into msgrepeat foreverend repeatput "after" into msgend mouseUp
- The key check in the app is compiled on macOS and was tried by hand; it has no automated test.
- A script that neither loops nor waits cannot be stopped: it finishes on its own.
Test status
- C kernel: 261 checks, all green, including under AddressSanitizer,
UndefinedBehaviorSanitizer and LeakSanitizer. One new one:
cmdpoint, fourteen cases — loops with and without a body, callers, a stopped function,wait, the message box, no dialog, back to rest. - Zero warnings, gcc at three optimisation levels and clang with Xcode's families.
- The Cocoa layer still has no automated tests — CI only proves it compiles.
Decided against: cantAbort
HyperCard has a stack property, cantAbort, that disables Cmd-. HC will not
have it. Cmd-. is the only way out of a runaway script without quitting and
losing unsaved work; a stack must not be able to take that away. This is a
decision, not a gap.
Known, and not fixed here
- Keystrokes typed during a script are put back at the head of the queue, so they now come before a click that preceded them.
- After a fault raised by a command (
hide,set, a message nobody handles), the handler carries on and the dialog comes at the end. HyperCard probably stops on the spot — to play in HyperCard. - A fault inside a user function stops the function but not its caller.
visual effectfollowed bygo, withoutlock screen, still plays after the script rather than insidego.- Still standing from 0.7: named windows, patterns, resource-fork icons, HyperCard 1.x, priv...
HC-0.7.2
HC-0.7.2 — errors you can find, and an audit that found real bugs
Two things in this release, and they meet in the middle.
When a script fails, HC now shows you exactly where. The error dialog names the right object and the right line — in every one of twenty measured cases, where five used to point somewhere else — and "Script" opens the editor with the faulty line framed in red, the way HyperCard framed it, with line numbers in the margin.
And two audits went through the whole project — one of our own, with a fuzzer and a static analyser, and one brought from outside. Between them they found a line that executed half of itself before reporting an error, a freeze, four crashes, and a handful of operations that damaged what they were working on when they failed. All fixed, and each one held by a test that was checked to fail without its fix.
Apple's Stack Templates calendar works too: "Show The Year…" redraws all twelve months.
At a glance
0.7.1 0.7.2
a syntax error on a line reported, after half the line had run (delete card "D deleted the card) reported, nothing runs
"Script" in the error dialog opens the editor with the line selected — the first key typed replaced the whole line opens it with the line framed, cursor at its start, nothing selected
line numbers in the script editor none in the margin, the faulty one in red
the right object and line, over 20 measured error cases 15 20
a handler without its end did nothing, silently an error, pointing at the handler's first line
the dialog propriété inconnue (v3, ligne 3 de button "B".mouseUp) the fault as the title; the object, the line and the line of code below
Stack Templates, "Show The Year…" only the title changed the twelve months redraw
its Next / Previous arrows several years per click one per click; held down, one per repeat
unlock screen with visual effect never played played inside the command, as in HyperCard
visual effect e, with the effect's name in e the effect "e", unknown the effect named in e
put value("the name of" & return & "me") froze the app returns
charToNum(), offset() with no argument crashed empty
global g then put g + 1 could print -5.31401e+303 1
the hcVersion, "About HC" 0.7.1 0.7.2
Installing: macOS will say the app is damaged. It is not.
This release is not notarised. macOS quarantines anything downloaded from the internet, and Gatekeeper refuses an app it cannot trace to a notarised Developer ID — with a message that says the wrong thing:
"HC is damaged and can't be opened. You should move it to the Trash."
The app is not damaged. It is unnotarised, which is a different problem, and the wording sends people to the Trash for no reason.
Copy HC.app to /Applications, then clear the quarantine flag once:
xattr -dr com.apple.quarantine /Applications/HC.app
It opens normally after that, and the flag does not come back. An app you build yourself from this repository is never quarantined — it is only the download that triggers this.
The binary is universal — x86_64 arm64. Deployment target is macOS 10.13.
- Finding the line that fails
The request was plain: the user must see exactly which part of the script is at fault; complex scripts get cryptic, and everything must be done to make debugging easy.
First, the kernel had to point at the right place — and it often didn't. The dialog received an object and a line, but from two different sources: the object from the first error line that happened to be collected, the line from the first runtime error. Measured over sixteen cases, then twenty (ouerreur), five were wrong:
A button fails on line 3. The card's script, read on the way, has a typo in a different handler that never runs. "Script" opened the card, at line 3, under the title of the card's typo — an innocent script, accused.
A line typed into the message box opened the card's script.
zorglub 3, hide button "Absent", set the zorglub of me to 3, delete field "Absent": line 0 — the editor opened at the top.
on mouseUp without its end: line 0, and the handler did nothing at all, skipped without a word.
The cause was one thing: diagnostics from parsing a script — produced when a script is first read, not when it fails — went into the dialog and took its first line. They now go to the log only. The object and the line are set together, by whatever actually stopped the script, and nothing is lost: running a faulty line raises its own error, at its own line.
Then the editor had to show it. It used to select the line. A selection vanishes at the first click — and the first key you typed replaced the whole line, so fixing an error meant deleting the line that carried it. The line is now framed, the way HyperCard framed it: a red rectangle under the text, which stays while you read the rest of the script and goes away at your first edit (the line numbers no longer mean the same thing after that). Lines are numbered in the margin; the faulty one is red. The "Vérifier" (check) button frames the first fault it finds, too.
The dialog puts the fault in the title, then the object, the line number and the line of code itself (HC's messages are in French):
propriété inconnue
button "B", ligne 3 :
put the zorglub of me
"Line 42" makes you count. The line itself you recognise.
The script editor changes are compiled on macOS but have not been run there. The bench is in docs/mesures/ligne_fautive.txt.
2. A line that ran half of itself
Found by the fuzzer, behind what looked like a false alarm. The parser kept the part of a line it understood and put the error next to it, so the executor ran the first half, then complained about the rest:
line what happened
delete card "D the current card was deleted, then the error
put 1 into g zz g was set, then the error
go next card "zz moved to the next card, no error at all
repeat with i = 1 to 10 step 3 ten iterations of step 1, no error at all
Reporting an error and acting is the worst of both: you read that the line was not understood, and the stack has already changed. A faulty line now does nothing, and the handler stops on it.
And a test that had been measuring two lines out of seven since it was written. It wrote "Une" — a C escape — into HyperTalk, which has none. Line 5 had always failed, the five after it had never run, and the reference output had recorded the failure. It only showed once the first half of the faulty line stopped printing.
3. The Stack Templates calendar
"convert todaysDate to dateItems runs but gives no result." The convert worked. The trouble was two lines further down, in Apple's "Show The Year…" button:
ask "Show what year?" with item 1 of todaysDate
if ((it is empty) or (it is not a date)) then exit mouseUp
else updateCalendar it, "barn door open"
The answer is a year on its own, 2027. HC only took a bare number for a date — a count of seconds since 1904 — above 100 000, so 2027 was not a date and the button left through exit mouseUp without a word. For Apple's button ever to have worked, HyperCard must take any integer as a date; HC now does too. (Deduced from Apple's script, not measured in HyperCard.)
Then only the title changed. updateCalendar sets the title, then loops over the twelve months starting with if the mouseClick then exit repeat — a way out for whoever clicks during the calculation. HC counted the click that had launched the button, so the loop left on its first turn. HyperCard has already consumed that click by the time the script runs; now HC has too. A click only counts for the mouseClick if it arrives while a script is running. Reproduced on Apple's real stack, then fixed: January 2027 starts on a Friday, February on a Monday.
And the arrows fired years like a machine gun. The calendar's Next and Previous buttons act on mouseDown, and repeat through mouseStillDown while the button stays down. Two things made one click worth several years:
The visual effect never played at all — "there's no scroll effect". updateCalendar ends with unlock screen with visual effect, on the effect it received as a parameter. HC unlocked the screen and threw away the rest of the line: the app never heard of an effect. And had it heard, it had nothing to animate from: the screen lock only held back redraws, it kept no picture of the frozen screen. The code that did keep one was there, and nothing called it. Now the kernel hands the effect over just before unlocking — reading theEffect as a variable, as Apple's script needs — and the lock photographs the screen, so the effect goes from the frozen screen to what the script painted underneath. It plays inside unlock screen, and the script waits for it, as in HyperCard.
That was also half the machine gun: with no effect, the script returned in a few milliseconds, the repeat timer started, and during the hundred milliseconds of an ordinary click it fired mouseStillDown several times. With the effect lasting longer than the click, a click is one year.
The repeat timer also fired during a running script, starting a new updateCalendar inside the one in progress. Replayed in the kernel with three ticks during the script: one press, 1995 became 1999. HyperCard never delivers a message in the middle of a handler; neither does HC now.
- What the audits found
Our own audit — clang's static analyser, and a fuzzer run under AddressSanitizer, UndefinedBehaviorSanitizer and LeakSanitizer on HC's three doors: 240 000 scripts, 20 000 damaged .stack files, and 8 000 damaged HyperCard stacks derived from six real ones.
put value("the name of" & return & "me") froze the app for good: a word scanner skipped spaces and tabs but stopped on any whitespace, read a zero-length word on a line break, and never moved again.
charToNum() and offset() with no argument crashed (strlen(NULL)).
A .stack file with two stack headers leaked the whole first stack; it is now refused, with...
HC-0.7.1
HC-0.7.1 — imported stacks draw the way Apple drew them
Two drawing defects, both found by looking at the same card side by side — HC on the left, HyperCard under Basilisk II on the right, Apple's "Stack Templates", the Year Calendar. Both were fixed by asking Apple's own geometry what the right number was, rather than by nudging a constant until the picture looked better.
If you opened a HyperCard stack in 0.7, replace it. The text was read correctly; it was drawn in boxes too small for it, and lines fell out silently.
At a glance
0.7 0.7.1
the weekday header of each month (M T W T F S S) invisible shown
a month's sixth week dropped shown
January in the year calendar ran to 19 runs to 31
vertical text margin of a field 8 px total 2 px (measured)
Courier / Monaco advance 0.6 em, 8 % too wide integer, as a bitmap font
the hcVersion see below 0.7.1
A note on the 0.7 tag. It points at a commit made before the version was raised, so an app built from that tag answers 0.6.9.4 to the hcVersion and shows it in "About HC". Check yours; if it says 0.6.9.4, that is why, and 0.7.1 settles it.
Installing: macOS will say the app is damaged. It is not.
This release is not notarised. macOS quarantines anything downloaded from the internet, and Gatekeeper refuses an app it cannot trace to a notarised Developer ID — with a message that says the wrong thing:
"HC is damaged and can't be opened. You should move it to the Trash."
The app is not damaged. It is unnotarised, which is a different problem, and the wording sends people to the Trash for no reason.
Copy HC.app to /Applications, then clear the quarantine flag once:
xattr -dr com.apple.quarantine /Applications/HC.app
It opens normally after that, and the flag does not come back. An app you build yourself from this repository is never quarantined — it is only the download that triggers this.
The binary is universal — x86_64 arm64. Deployment target is macOS 10.13.
- The measurement that moved the search from one end of the program to the other
Before touching any code, the real reader and the real importer were run on the file, and what the file holds was compared with what the model shows:
file : 44 contents for the "Year Calendar" background
model : 44 contents, including all TWELVE "M T W T F S S"
The days were loaded. So the defect was not in reading the stack — it was in drawing it. Without that comparison the fix would have gone hunting in the importer, which had done nothing wrong.
2. A field's vertical margin was eating whole lines
CGFloat m = o->wide_margins ? 8 : 4;
return NSInsetRect(r, m, m);
NSInsetRect removes m from the top and the bottom as much as from the sides: eight pixels of height. The twelve Weekdays fields are 104×12 — exactly one line of 12. After the margin, four pixels were left for a line that needs twelve, and the line was dropped entirely. Same cause, quieter, for each month's sixth week: 72 px tall, six lines of 12.
How much, then? The question went to Apple's 349 fields, not to a document. Apple sized fields in whole lines, so the right margin is the one for which (height − margin) lands exactly on the line height most often:
total vertical margin normal margins (261) wideMargins (88)
2 px 28.4 % ← peak 47.7 % ← peak
8 px (what we did) 0.4 % 4.5 %
Two pixels in total — one per side — and the peak of both independent populations. 8 px was not an approximation; it was the floor of the table, the worst value available. And wideMargins adds nothing vertically: both columns peak in the same place, which is what licensed separating the two axes instead of tuning by eye.
3. The month box was too small: our Courier is 8 % too wide
With the headers back, the months still lost their last weeks. It was not the margin, and Apple's geometry says so:
field width characters px per character available
4 Month 108 21 5.14 ← the tightest
Weekdays 104 19 5.47
Calendar 1 115 20 5.75
macOS's Courier 9 advances 0.6 em, or 5.40 px. Twenty-one characters need 113.4 px in a field that is 108 wide: it does not fit at any margin, not even zero. Shaving the horizontal margin would have squeezed the Calendar fields in (115 px) and left the Month fields broken (108 px) — half the symptom cured and the cause hidden underneath, to be paid for again at the next template.
The cause is the font. The Macintosh's Courier was a bitmap font, and a bitmap font advances by a whole number of pixels: 5 at 9 points. Apple sized these fields to the pixel for that advance; we draw with an outline font that advances 5.40. Eight per cent, which twenty-one characters turn into 5.4 pixels of overflow. The advance is now pulled back to the integer by kerning, for Courier and Monaco only — the fixed-pitch fonts of the period, where the error accumulates into columns and where the correction is a single number.
And a latent defect found on the way: style_attrs overwrote NSKernAttributeName for condense and extend. Laying the advance correction on top would have made one of the two disappear without a word. The three now add up.
4. What is not measured is written down as not measured
The horizontal margin does not move, and that is no longer a hole — it was examined, and it is not the cause. See docs/mesures/marges.txt.
The proportional period fonts — Geneva, Chicago, New York, Palatino — are untouched. The same discrepancy must exist, but correcting it needs a per-character advance table that nobody here has measured.
Only one case is measured for the fixed-pitch rule: Courier 9, from the geometry of Apple's fields. Extending it to other sizes and to Monaco rests on the principle that a bitmap font advances by an integer, not on a reading.
And a check that did not settle anything, written down precisely because it did not. To confirm the integer-advance rule across the corpus' 44 fixed-pitch fields: 34 fit with macOS's advance, 35 with the integer one, and the worst gaps were −1200 px. The instrument was counting fields of prose, which are perfectly entitled to wrap. It measured something other than what it announced, and it proves nothing. What carries the conclusion is the one layout that cannot wrap: the calendar's grid.
The instrument lied once more, which is becoming this project's signature lesson: the probe tested if (!hc_origine_lit(...)) when that function returns zero on success. Its first reading announced a refusal on a perfectly successful parse.
Test status
C kernel: 250 checks, all green, including under AddressSanitizer, UndefinedBehaviorSanitizer and LeakSanitizer.
Zero warnings, gcc at three optimisation levels and clang with Xcode's families.
math.h is declared rather than relied on through a transitive Cocoa include: one line uses floor, and the only compiler that would have settled it does not run in the container.
The Cocoa layer still has no automated tests — CI only proves it compiles. HCtext.m changed here, so the check that decided was a human one: January runs to 31, and all twelve months show their six weeks.
Known, and not fixed here
Everything listed under 0.7 still stands — named windows, patterns, resource-fork icons, HyperCard 1.x, private-access stacks, a fault inside a user function not stopping its caller, and the four benches still to play in HyperCard. CFBundleVersion is still hard-coded to 1. Not notarised; see "Installing".
Changes in this release
#77 — version 0.7 in all three places, the English release note, the stale re-record list in docs/livraison.md corrected, and the decision that the import stays one-way written down with its reasons (352c5f5, 48b1dbe, 6af7e89)
#78 — the two drawing defects above, measured on Apple's geometry (d27e9a4, 4c09031)
Full changelog: HC-0.7...HC-0.7.1
HC-0.7
HC-0.7 — HC opens real HyperCard stacks
One subject, and it is the one the whole project was for: HC now reads
Apple's own binary stack format, and runs the stacks. Not a converter run
once by hand — File ▸ Open recognises the format from the four letters of the
file's first block, reads the stack, and plays it.
Four stacks from 1990–1995 were read end to end, and 296 scripts, 4 073
lines went through the interpreter. What they refused was fixed: twenty-one
defects, most of them found by scripts nobody wrote to please us.
If you have HyperCard stacks, this is the release that matters. Everything
before it could only open stacks HC had written itself.
At a glance
| 0.6.9.5 | 0.7 | |
|---|---|---|
open an Apple .stack |
not possible | File ▸ Open, format recognised |
| the corpus' scripts accepted | — | 99.7 % (296 scripts, 4 073 lines) |
| card order | — | verified by both of HyperCard's checksums |
| field text, part properties, part ids | — | read |
| paint (WOBA compression) | — | read |
styled runs (STBL) |
— | read |
the target across a nested call |
reset to me |
survives |
set showPict of this card to false |
unknown property | works |
select the foundChunk |
"doesn't know how" | works |
find "x" in ch (computed designator) |
"Not found" | searches |
| a lit transparent button | blackens its area | inverts it |
| warning gate | gcc, -Wall -Wextra |
gcc ×3 levels + clang, Xcode's families |
Installing: macOS will say the app is damaged. It is not.
This release is not notarised. macOS quarantines anything downloaded from
the internet, and Gatekeeper refuses an app it cannot trace to a notarised
Developer ID — with a message that says the wrong thing:
"HC is damaged and can't be opened. You should move it to the Trash."
The app is not damaged. It is unnotarised, which is a different problem, and
the wording sends people to the Trash for no reason.
Copy HC.app to /Applications, then clear the quarantine flag once:
xattr -dr com.apple.quarantine /Applications/HC.appIt opens normally after that, and the flag does not come back. An app you build
yourself from this repository is never quarantined — it is only the download
that triggers this.
The binary is universal — x86_64 arm64. Deployment target is macOS 10.13.
1. Reading the original format
An extractor first, then a full importer of Apple's binary format (1987–1998):
the block chain, the card order, the MacRoman table, part properties, field
text, part ids, paint, and styled text runs.
Two things are worth naming because they are the parts that could have been
faked and were not:
The card order is verified, not trusted. HyperCard stores two checksums
over the card list, and both are recomputed here. When they do not agree with
the order read from the file, the order is reported as not read rather than
guessed — a stack whose cards come out in file order, silently, would be worse
than a refusal.
Opening a stack cannot overwrite it. The imported stack is installed
without a path, so "Save" has nowhere to write and asks. The original
.stack binary is never the target of a save, and cannot become one by
accident.
And one decision, so that it is not mistaken for a gap: the import is
one-way, by design. HC will not write Apple's format back. WOBA compresses a
one-bit bitmap and the format has no idea of a colour icon; our paint is
compressed RGBA. Writing a HC stack
into the 1987 format would mean either flattening every drawing to two values
in silence, or inventing blocks Apple never defined — a file nothing on earth
can read, carrying the four letters of a format that is known. Both are worse
than not writing. And a format is written so that someone can read it:
HyperCard no longer exists, and the only reader of these bytes besides HC is a
HyperCard under an emulator, which is our measuring bench rather than a
recipient. This changes no code — the no-path guard above was never a
precaution awaiting a writer; it is the finished shape.
Reading is a different question, and stays open: resource-fork icons and
patterns are still to be read. Giving up on writing the format gives up
nothing it contains.
2. The corpus
| stack | blocks | bkgnds | cards | anomalies | scripts | lines | accepted |
|---|---|---|---|---|---|---|---|
| 3D Parametric Equations | 45 | 3 | 11 | 0 | 66 | 804 | 100 % |
| Découvrir HyperCard (Apple) | 32 | 1 | 11 | 0 | 28 | 164 | 100 % |
| TEST3 (our torture stack) | 16 | 1 | 5 | 0 | 4 | 941 | 100 % |
| Stack Templates (Apple) | 65 | 17 | 17 | 1 | 198 | 2 164 | 99.5 % |
| total | 1 | 296 | 4 073 | 99.7 % |
"Stack Templates" is the first production stack of the corpus, and it is the
one that went from 94.4 % to 99.5 % — eleven refused handlers, four causes.
Eleven was never a count of four defects: three shapes fell on one cause, and
they had to be pulled out of the stack one at a time to find out.
The one remaining anomaly is the only one we can explain. The MAST block
of "Stack Templates" declares its size as 0x40000400 where it is 1 024 — the
high byte is corrupt, which the format's own documentation warns about. The
byte is masked off, and the mask is believed for one reason only: the chain of
65 blocks then lands exactly on the end of the file. A repair that displaced
anything would have no reason to land there. Measured before being believed: of
158 blocks across the four stacks, three stacks have not one non-zero high
byte, and the fourth has exactly one.
Five witnesses hold that repair from both ends, and two of them are worth
nothing apart: "four extra bytes without repair → read, chain does not reach
the end of the file" and "the same four with repair → refused, the repair does
not land right". Taken alone the first says we tolerate junk and the second
says we reject a file; it is their difference that shows the guard. One
measurement would not have reached it.
3. The twenty-one defects the stacks found
In the interpreter (ten)
| before | |
|---|---|
¬ continuation followed by a comment |
the continued line was lost |
else after a command with an optional argument |
else swallowed as the argument |
print card from x,y to x,y |
rectangle ignored, then about to lie |
last menuItem of menu "T" |
the ordinal did not count as a designator |
bg field "Year" + 1 |
the quoted name swallowed the operator |
select char 1 to 5 of line 3 of target |
a chunk inside a chunk did not resolve |
the target inside a nested handler or function call |
reset to me |
set showPict of this card to false |
unknown property |
bkgnd on the writing side |
"object not found: this bkgnd" |
the version |
answered ours, closing every stack with a version gate |
That last one is worth a line of its own. Apple's own teaching stack,
"Découvrir HyperCard", carries if the version < 2.2 then in its background
script, and HC answered 0.6.9.4 — so the stack politely refused to run. A
version gate was standard practice in 1993; every archived stack carrying one
would have stayed shut, with no recourse. the version now answers 2.4.1,
HyperCard's. Our own number has its own name, the hcVersion, and the witness
checks that the two are not the same thing.
In the application and the import (eight)
| before | |
|---|---|
| paste in the icon editor | went into the card — the target search crossed the panel |
| duplicating a colour icon | read freed memory |
the clickChunk, read before any other click property |
empty — layout had not happened yet |
| a lit transparent button | blackened its area instead of inverting it |
| the default line height | rounded, where HyperCard truncates (measured on 140 of Apple's parts) |
| field text on import | did not arrive |
| a radio button on import | drawn as a rectangle |
| part ids on import | collided — a part's namespace is its layer, not the stack |
In the diagnostics (three)
| before | |
|---|---|
select word 0 of … |
"doesn't know how" — the truth was "there is no word 0", and the selection was cleared while success was announced |
| Apple's uncommented copyright banner ahead of a handler | verdict ERROR, where HyperCard compiles a handler at call time and never parses the banner |
| the import dialog's "1 anomaly" | announced a loss that had not happened |
The last one is a distinction the code now keeps: an anomaly is something
that wants checking, a loss is something that could not be read. Thirteen
of the twenty-four anomaly sites are losses; the other eleven are suspicions.
Zero lost with one anomaly is a perfectly legitimate state, and it is the one
"Stack Templates" is in.
And one number in that dialog was mine, not the stacks': sixteen of seventeen
anomalies came from my own table, which reserved the shadow style to
buttons. It is not reserved to buttons — measured on 574 parts.
4. The torture stack, played on both sides
Nothing in the archives was demanding enough, so a torture stack is written
here, built by the code, and used twice: a harness runs it under the suite, and
the same program writes it to disk to be opened and clicked in the app.
Building it twice would have given two stacks that diverge at the first change.
It was then played inside HyperCard, on a stack assembled by hand under
Basilisk II, and compared line by line with ours. That is the first time both
columns existed at once, and it produced something better than fixes:
| HyperCard | HC | |
|---|---|---|
chars 1 to 5 of X |
Can't understand | works |
the number of chars of X |
*Can't understan... |
HC-0.6.9.5
gestion du clavier dans la pile demo.stack
HC-0.6.9.4
HC-0.6.9.4 — the numberFormat stops corrupting arithmetic
One subject, and it had been wrong since the beginning: the numberFormat
was rounding calculations, not just their display. Under a narrow format
like 0.0, every scientific calculation in HC was silently wrong.
If you plot anything, replace 0.6.9.3. A period plotter that sets set the numberFormat to 0.0 inside its loop drew a rosette in twelve-pixel stairsteps
where HyperCard draws a smooth curve — and the drawing was only the visible
part.
At a glance
Measured in HyperCard under Basilisk II, with the format set to 0.0:
| 0.6.9.3 | HyperCard, and now 0.6.9.4 | |
|---|---|---|
(0.34*1 = 0.34) |
false |
true |
(1/3*3 = 1) |
false |
true |
(1/3 = 0.3) |
true |
false |
(sqrt(2)*1 = 1.4) |
true |
false |
put sqrt(2) into x, (x = 1.4) |
true |
false |
… then (10*x = 14) |
true |
false |
put 1000*sin(pi/144) |
0.0 |
21.8 |
put word 2 of msg |
error — box was write-only | the word |
Installing: macOS will say the app is damaged. It is not.
This release is not notarised. macOS quarantines anything downloaded from
the internet, and Gatekeeper refuses an app it cannot trace to a notarised
Developer ID — with a message that says the wrong thing:
"HC is damaged and can't be opened. You should move it to the Trash."
The app is not damaged. It is unnotarised, which is a different problem, and
the wording sends people to the Trash for no reason.
Copy HC.app to /Applications, then clear the quarantine flag once:
xattr -dr com.apple.quarantine /Applications/HC.appIt opens normally after that, and the flag does not come back.
Notarising properly needs a paid Apple Developer account and a notarytool
step in the release process. Until that exists, this line is the whole
installation procedure — and worth knowing that it is only the download that
triggers this. An app you build yourself from this repository is never
quarantined.
The binary is universal — x86_64 arm64, verified with lipo. It runs on
Apple Silicon (M1 and later) and on Intel. Deployment target is macOS 10.13.
1. How the model was found: compare, do not display
Six weeks of careful reasoning had built a model of HyperCard that was wrong
end to end — "operators format their result". It survived because every
measurement used put, and put shows the display and the calculation
mixed together. There is no way to separate them that way.
The unlock was to stop displaying and start comparing. true is not a number,
so no format can touch it, and the arithmetic becomes visible on its own:
set the numberFormat to "0.0"
put (1/3*3 = 1) -- HyperCard: true. HC 0.6.9.3: false
Four booleans settled in one evening what six weeks of deduction had got
backwards. Two of them demolished values that had been written into this
project's own test harnesses as if they had come from HyperCard.
The rule, measured: arithmetic keeps full precision. Storing into a
variable keeps full precision. The numberFormat applies only when a number
becomes text for output — display, concatenation. A quoted text literal is
never formatted.
2. What was actually broken
HctValeur carried only text. A calculation's result entered it already
formatted, and nothing downstream could recover the precision — the rest of
the calculation re-read that rounded text.
The cost in the plotter: cos(t) under format 0.0 could only ever be 0.0,
0.1, 0.2 — twenty-one distinct values for a whole circle. Ten
consecutive points of the plotting chain returned the same number:
HyperCard 416.0 416.0 414.0 413.0 410.0 407.0 403.0 398.0 392.0 386.0
0.6.9.3 416.0 416.0 416.0 416.0 416.0 416.0 416.0 416.0 416.0 416.0
0.6.9.4 416.0 416.0 414.0 413.0 410.0 407.0 403.0 398.0 392.0 386.0
Worse than the drawing: any narrow-format scientific calculation was
wrong, with no sign of it. 1000*sin(pi/144) returned 0.0, because
sin(pi/144) had been flattened to 0.0 before the multiplication. A
thousand times zero is zero — that is not a rounding error, it is a lost
value.
3. The fix, and the six frontiers it took
HctValeur.txt is exactly what it was before — formatted — so no existing
reader of it can change behaviour. The un-rounded double travels beside it in
.brut, and only these paths look at it:
| path | where |
|---|---|
| function return values | hct_val_fonction |
| operator results | hct_val_calcul |
| numeric literals in the script | feuille(), hct_eval.c |
| function arguments | nombre_de(), hct_eval.c |
| comparison | vals_egales / vals_compare |
| accumulation operands | nombre_de_val(), hct_exec.c |
| storing into a variable | ecrit_var_nombre, both hosts |
Each frontier nullified the previous fix. Correcting function returns
without correcting operators achieved nothing; operators without storage,
nothing; and as long as 0.34 written in a script was merely text, a
comparison fell back to comparing rounded text and four booleans stayed wrong
while everything else was already right.
A precision fix that stops at one frontier is undone by the next. That is the
transferable finding of this release.
4. NaN ordering is untouched
hct_compare treats a NaN as text, because that is what a period stack
expects of its bounds test — established in 0.6.9.3, and a real stack proved
it. vals_compare therefore requires that both operands carry a number and
that neither is a NaN. Outside that, the old path exactly.
Reading the raw double on only one side would also have reintroduced the
asymmetry being hunted: x = 1.4 would change meaning with the order of its
operands.
5. The message box can be read
msg, message, the message box were a write-only container. put word 3 of msg found nothing.
There is now a persistent buffer in the kernel (hc_message_lu /
hc_message_ecrit), a lit_message host callback, and HCview.m keeps the
buffer current as you type.
One consequence is worth knowing, and it is HyperCard's behaviour too: from
the message box, no line can read anything but itself, because the line you
typed is the box's content when the script reads it.
put word 1 of msg -> put
put word 2 of msg -> word
put word 3 of msg -> 3
Each answer is word N of your own command. To read the box for real you need
two lines, so a script — and the into matters, or the put overwrites the
box before you can use what you read:
put "one two three"
put word 2 of msg into got -- "two"
the visible of msg is still "object not found": the window belongs to the
host, only its contents belong to the kernel.
6. A non-finite coordinate draws nothing, and says nothing
Measured: in point mode the plotter clicks before its bounds test, so
with a NAN(004) in hand — and HyperCard opens no alert. It draws nothing and
moves on.
0.6.9.3 raised HC_ERR, which reached the dialog. On a lemniscate, a hundred
and forty-two times. Deduplicating identical lines made that bearable; it did
not make it faithful.
The refusal to draw was right; the level of the report was wrong. It is now
HC_INFO: the dialog stays silent as in HyperCard, and whoever watches the
monitor still sees the discarded points. Measured silence applies to the user,
not to someone hunting a defect.
7. A runaway loop is now visible
The executor caps every loop at ten million iterations, but all three exits
were plain breaks: the loop stopped and the handler carried on as though
it had finished normally. A wrong result, silently, after minutes of freeze.
v3_respire's own comment had promised the opposite since day one.
Caught along the way, by AddressSanitizer: hct_ctx_faute keeps the
pointer, it does not copy. Every caller happened to pass a literal, which
made the contract true by accident. The first one to compose its message
dynamically fell in. The contract is now written down.
8. Two foreign images in the iconset
icon_16x16.png and icon_32x32.png did not contain the bird — two
multicoloured squares, measured at 0 % neutral pixels where the other eight are
at 100 %. Invisible, since the shipped .icns has no such slots, but a
regeneration by iconutil would have let them in.
9. A chantier that cancelled itself
HC has two ways of signalling an error: emit(HC_ERR) + return 1, which does
not stop the handler (83 sites), and hct_ctx_faute, which does (13
sites). This asymmetry was written up as a defect to fix, on the strength of
one sentence — "in HyperCard an error STOPS the handler" — that had been
reasoned, never measured.
Measured, in three lines:
put "1"
set the zorglub of this card to 1
put msg & "2"
HyperCard's box ends up containing 12. It opens the dialog and carries
on. The asymmetry runs the other way from what had been claimed, HC's
majority behaviour is the faithful one, and a fifty-site refactor was cancelled
before a line of it was written.
10. Two false references removed from the tests
Both written by this project, both contradicted by measurement, and both
recorded rather than quietly corrected:
1/3*3 -> 0.9, annotated "HyperCard" innumfmt.c. That was HC's own
value, read one evening with the two windows side by side.- "under HyperCard it shows 1.188 and not 1.194" in
emballee.cand
sane.c. The1.188came from the original bug report about HC.
Both have since been re-measured in Basilisk II: HyperCard gives 1.0 and
1.194. A third reading, 10*x -> 14.0, was the last co...