Stop the prompt writing off the front of its own buffer
The prompt is the working directory's path, worked out each time by walking the chain of parents up to the root. The names arrive deepest first, so they are written backwards from the end of a 127 byte buffer - and nothing bounded that walk. Nothing bounds the depth either. A path given to one operation is capped at 95 characters and a 22 character name, but "mkdir a" and "cd a" are each far inside that and can be repeated forever. Six directories of 22 characters is 132 characters of path, and at that point the walk wrote down past the front of CwdText and into what the assembler had laid out below it: the shell's own command names. ExitName sits five bytes under, so the word "exit" went first and the shell stopped recognising the command for leaving. Measured, not deduced: fine at five levels, gone at six. The walk now counts the room it has left, byte by byte, and stops. What is already written is the DEEP end of the path, which is the end worth showing, so it is cut at the front and three dots say so - out of three bytes held back from the count, so there is always somewhere to put them. Twenty levels deep the prompt shows the last five and every command still works. cosmosDeep records that, and records it by running help, cd and exit from down there rather than by looking at the prompt: a wrong prompt is cosmetic, and this was writing into other variables. It fails with the bound removed. The tree is built by SplitDisk because a path that long cannot be given to mkdir in one piece - which is the same fact that makes the depth unbounded. The three path limits are written down in the README now, including which one actually binds. The other two do not: the longest path on a full install is 21 characters.
This commit is contained in:
@@ -185,6 +185,29 @@ has moved about on looks exactly as it always did. Nothing stores the path: the
|
||||
directory is an entry index and two bytes, and the text on the prompt is worked out again
|
||||
each time by walking the chain of parents upward.
|
||||
|
||||
That walk goes from where you are up to the root, so the names arrive deepest first and
|
||||
are written into the buffer **backwards, from its end**. When they do not all fit, what is
|
||||
already down is the deep end of the path, which is the end worth keeping - so the prompt
|
||||
is cut at the front and says so:
|
||||
|
||||
```
|
||||
...opqrst03/abcdefghijklmnopqrst04/.../abcdefghijklmnopqrst08>
|
||||
```
|
||||
|
||||
**Three limits, and the smallest is not the one you would guess.** A path handed to any
|
||||
one operation is capped at 95 characters up to the last separator plus a name of 22; the
|
||||
host tool carries 512, which only means it can build a tree CosmOS cannot name in one
|
||||
piece. Neither of those binds anything: the longest path on a full install is 21
|
||||
characters. What binds is the prompt's 127 bytes - and **nothing caps how deep the
|
||||
directories go**, because `mkdir a` and `cd a` are each well inside every limit and can be
|
||||
typed all day.
|
||||
|
||||
Before the walk was bounded it wrote past the front of that buffer and into whatever the
|
||||
assembler had put below it, which was the shell's own command names. Six directories of 22
|
||||
characters was enough. The first five bytes to go were the word `exit`, so the shell
|
||||
stopped recognising the command for leaving - a fault with no plausible connection to the
|
||||
directory you happened to be standing in.
|
||||
|
||||
`dir` lists the directory you are in rather than the whole disk.
|
||||
|
||||
A program can move too, with `osChangeDir`, and **the shell puts the working directory
|
||||
|
||||
@@ -1235,6 +1235,23 @@ shellPath:
|
||||
SETD.1 CwdAt
|
||||
STD.0.1
|
||||
|
||||
; HOW MUCH THERE IS TO WRITE INTO, COUNTED DOWN. Nothing bounds how deep the directories
|
||||
; go: a path is capped at what one operation can name, but "mkdir a" and "cd a" are each
|
||||
; well inside that and can be typed all day. This walk writes BACKWARDS from the end, so
|
||||
; running out means walking off the FRONT of the buffer and into whatever the assembler
|
||||
; put below it - which was the shell's own command names. Six directories of twenty two
|
||||
; characters was enough, and the first five bytes to go were the word "exit", so the
|
||||
; shell stopped recognising the command for leaving.
|
||||
;
|
||||
; A hundred and twenty three of the hundred and twenty six, the other three being kept
|
||||
; back for the dots that say it was cut.
|
||||
INIA 0d123
|
||||
SETD.0 CwdRoom
|
||||
STA.0
|
||||
RSTA
|
||||
SETD.0 CwdCut
|
||||
STA.0
|
||||
|
||||
; Where the walk up starts.
|
||||
SETD.0 SbfsCwd
|
||||
SETD.1 CwdWalk
|
||||
@@ -1259,6 +1276,9 @@ shellPathStep:
|
||||
; The name goes in front of what is there, and a separator in front of that.
|
||||
SETD.0 SbfsName
|
||||
CALL shellPathPrepend
|
||||
SETD.0 CwdCut
|
||||
LDA.0
|
||||
BNA shellPathReady ; It would not all fit, so there is nothing above worth asking for.
|
||||
|
||||
SETD.0 SbfsUpParent
|
||||
SETD.1 CwdWalk
|
||||
@@ -1307,6 +1327,13 @@ pathBack:
|
||||
BRA pathSeparator
|
||||
DECA
|
||||
STA.1
|
||||
|
||||
SETD.1 CwdRoom
|
||||
LDA.1
|
||||
BRA pathCut ; The front of the buffer, and the next byte would be past it.
|
||||
DECA
|
||||
STA.1
|
||||
|
||||
DECD.0
|
||||
DECD.2
|
||||
LDA.0
|
||||
@@ -1314,6 +1341,12 @@ pathBack:
|
||||
BRI pathBack
|
||||
|
||||
pathSeparator:
|
||||
SETD.1 CwdRoom
|
||||
LDA.1
|
||||
BRA pathCut
|
||||
DECA
|
||||
STA.1
|
||||
|
||||
DECD.2
|
||||
INIA 0x2F
|
||||
STA.2
|
||||
@@ -1321,6 +1354,28 @@ pathSeparator:
|
||||
STD.2.1
|
||||
RET
|
||||
|
||||
; Stopping rather than writing past the front. The names go on backwards, so what is
|
||||
; already down is the DEEP end of the path - which is the end worth showing: the prompt
|
||||
; says where you are, and the last two directories say that better than the first two do.
|
||||
;
|
||||
; Three dots in front, out of the room that was never counted, so there is always
|
||||
; somewhere to put them. No separator: the name they sit against brought its own, or was
|
||||
; cut off half way, and "..." reads correctly against either.
|
||||
pathCut:
|
||||
INIA 0x2E
|
||||
DECD.2
|
||||
STA.2
|
||||
DECD.2
|
||||
STA.2
|
||||
DECD.2
|
||||
STA.2
|
||||
SETD.1 CwdAt
|
||||
STD.2.1
|
||||
INIA 0x01
|
||||
SETD.1 CwdCut
|
||||
STA.1
|
||||
RET
|
||||
|
||||
; ---- delete and rename ----
|
||||
;
|
||||
; The two things a disk needs that reading and writing do not provide, and the two that
|
||||
@@ -3534,6 +3589,15 @@ CwdWalk:
|
||||
PathLength:
|
||||
0x00
|
||||
|
||||
; What is left of CwdText to write into, and whether the path ran out of it. Counted down
|
||||
; rather than compared against an address, because the check is on every byte written and
|
||||
; a subtraction of two pointers is a great deal more than a byte that is already going to
|
||||
; be tested for zero.
|
||||
CwdRoom:
|
||||
0x00
|
||||
CwdCut:
|
||||
0x00
|
||||
|
||||
; What the working directory was when a program was started, so that it can be put back
|
||||
; when the program stops.
|
||||
SavedCwd:
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
CosmOS
|
||||
> /abcdefghijklmnopqrst01> /abcdefghijklmnopqrst01/abcdefghijklmnopqrst02> /abcdefghijklmnopqrst01/abcdefghijklmnopqrst02/abcdefghijklmnopqrst03> /abcdefghijklmnopqrst01/abcdefghijklmnopqrst02/abcdefghijklmnopqrst03/abcdefghijklmnopqrst04> /abcdefghijklmnopqrst01/abcdefghijklmnopqrst02/abcdefghijklmnopqrst03/abcdefghijklmnopqrst04/abcdefghijklmnopqrst05> ...opqrst01/abcdefghijklmnopqrst02/abcdefghijklmnopqrst03/abcdefghijklmnopqrst04/abcdefghijklmnopqrst05/abcdefghijklmnopqrst06> ...opqrst02/abcdefghijklmnopqrst03/abcdefghijklmnopqrst04/abcdefghijklmnopqrst05/abcdefghijklmnopqrst06/abcdefghijklmnopqrst07> ...opqrst03/abcdefghijklmnopqrst04/abcdefghijklmnopqrst05/abcdefghijklmnopqrst06/abcdefghijklmnopqrst07/abcdefghijklmnopqrst08> dir list what is on the disk
|
||||
load <file> read a program off the disk
|
||||
run [words] start what was loaded, and tell it those words
|
||||
<name> [words] look where you are and then in /Apps, and start that
|
||||
cd [path] go to a directory, or to the root with nothing after it
|
||||
mkdir <path> make a directory
|
||||
rmdir <path> remove an empty one
|
||||
delete <file> take it off the disk
|
||||
rename <file> <to> call it something else
|
||||
monitor look at memory, change it, and jump into it
|
||||
help this
|
||||
exit stop, or leave the monitor if you are in it
|
||||
...opqrst03/abcdefghijklmnopqrst04/abcdefghijklmnopqrst05/abcdefghijklmnopqrst06/abcdefghijklmnopqrst07/abcdefghijklmnopqrst08> > halted
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
@@ -0,0 +1,11 @@
|
||||
cd abcdefghijklmnopqrst01
|
||||
cd abcdefghijklmnopqrst02
|
||||
cd abcdefghijklmnopqrst03
|
||||
cd abcdefghijklmnopqrst04
|
||||
cd abcdefghijklmnopqrst05
|
||||
cd abcdefghijklmnopqrst06
|
||||
cd abcdefghijklmnopqrst07
|
||||
cd abcdefghijklmnopqrst08
|
||||
help
|
||||
cd
|
||||
exit
|
||||
@@ -326,3 +326,19 @@ cp "$ROOT/Programs/Examples/hello.asm" hello.asm
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" -I "$ROOT/Programs/CosmOS/Assembler" \
|
||||
"$ROOT/Programs/CosmOS/Assembler/tokenTest.asm" -o "$WORK/tokenTest.sbx" >/dev/null
|
||||
"$TOOL" put "$DISKS/asm.img" "$WORK/tokenTest.sbx" >/dev/null
|
||||
|
||||
# A tree deeper than the prompt can print, for the buffer that used to walk off the front
|
||||
# of itself. Eight directories of twenty two characters is 184 characters of path, and the
|
||||
# prompt is built backwards into 127 bytes - so before this was bounded, the walk wrote
|
||||
# down past the start of the buffer and over the shell's own command names. The word
|
||||
# "exit" was the first casualty, five bytes below.
|
||||
#
|
||||
# MADE HERE RATHER THAN BY THE TEST, because a path this long cannot be given to mkdir in
|
||||
# one piece: naming it is capped well below what walking into it a level at a time can
|
||||
# reach, which is exactly why the depth is unbounded in the first place.
|
||||
"$TOOL" format "$DISKS/deep.img" 512 8 >/dev/null
|
||||
DEEPPATH=""
|
||||
for i in 1 2 3 4 5 6 7 8; do
|
||||
DEEPPATH="$DEEPPATH/abcdefghijklmnopqrst0$i"
|
||||
"$TOOL" mkdir "$DISKS/deep.img" "$DEEPPATH" >/dev/null
|
||||
done
|
||||
|
||||
@@ -333,6 +333,18 @@ cosmosMore | CosmOS/Source/cosmos.asm | run | cosmosMor
|
||||
# part-block and 84,000-byte files are copied through one buffer; Compare checks the copies
|
||||
# and a same-sized file whose only difference is deep into the input.
|
||||
cosmosCopyCompare | CosmOS/Source/cosmos.asm | run | cosmosCopyCompare.in | - | disks/copycompare.img
|
||||
|
||||
# A working directory deeper than the prompt can print. The prompt is built BACKWARDS into
|
||||
# 127 bytes, and nothing bounds how deep the directories go - a path is capped at what one
|
||||
# operation can name, but "cd" a level at a time is well inside that and can be repeated.
|
||||
# So the walk wrote down past the front of its own buffer and into what the assembler put
|
||||
# below it, which was the shell's command names: at six directories of twenty two
|
||||
# characters the word "exit" was gone and the shell no longer knew how to stop.
|
||||
#
|
||||
# WHAT IS RECORDED IS THAT THE COMMANDS STILL WORK, and that is the whole point of running
|
||||
# help and cd and exit from down there rather than just looking at the prompt. A prompt
|
||||
# that is merely wrong is a cosmetic fault; this one was writing into other variables.
|
||||
cosmosDeep | CosmOS/Source/cosmos.asm | run | cosmosDeep.in | - | disks/deep.img
|
||||
# Reading a disk that has directories on it. The machine can walk a path at this point but
|
||||
# cannot make a directory, so the disk is built by the host tool and read here - which is
|
||||
# the two implementations checking each other rather than either checking itself.
|
||||
|
||||
Reference in New Issue
Block a user