Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.

Commit ebacf30

Browse files
authored
Improve globbing by getting size at the time we find files (#249)
* Improve globbing by getting size at the time we find files This stops having to stat files post-find, instead we can save away the size as we find the files. (ie. We use filepath.Walk instead of os.ReadDir so we get access to the fs.FileInfo immediately.)
1 parent fb93017 commit ebacf30

File tree

3 files changed

+55
-54
lines changed

3 files changed

+55
-54
lines changed

cpm/cpm_bdos.go

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -635,8 +635,10 @@ func BdosSysCallFileClose(cpm *CPM) error {
635635

636636
// BdosSysCallFindFirst finds the first filename, on disk, that matches the glob in the FCB supplied in DE.
637637
func BdosSysCallFindFirst(cpm *CPM) error {
638+
638639
// The pointer to the FCB
639640
ptr := cpm.CPU.States.DE.U16()
641+
640642
// Get the bytes which make up the FCB entry.
641643
xxx := cpm.Memory.GetRange(ptr, fcb.SIZE)
642644

@@ -727,25 +729,12 @@ func BdosSysCallFindFirst(cpm *CPM) error {
727729
// Create a new FCB and store it in the DMA entry
728730
x := fcb.FromString(res[0].Name)
729731

730-
// Get the file-size in records, and add to the FCB
731-
tmp, err := os.OpenFile(res[0].Host, os.O_RDONLY, 0644)
732-
if err == nil {
733-
defer tmp.Close()
734-
735-
fi, err := tmp.Stat()
736-
if err == nil {
737-
738-
fileSize := fi.Size()
732+
// Get file size, in blocks.
733+
x.RC = uint8(res[0].Size / blkSize)
739734

740-
// Get file size, in blocks
741-
x.RC = uint8(fileSize / blkSize)
742-
743-
// If the size is bigger than a multiple we deal with that.
744-
if fileSize > int64(int64(x.RC)*int64(blkSize)) {
745-
x.RC++
746-
}
747-
748-
}
735+
// If the size is bigger than a multiple we deal with that.
736+
if res[0].Size > int64(int64(x.RC)*int64(blkSize)) {
737+
x.RC++
749738
}
750739

751740
// Log the first result we're returning.
@@ -783,25 +772,12 @@ func BdosSysCallFindNext(cpm *CPM) error {
783772
// Create a new FCB and store it in the DMA entry
784773
x := fcb.FromString(res.Name)
785774

786-
// Get the file-size in records, and add to the FCB
787-
tmp, err := os.OpenFile(res.Host, os.O_RDONLY, 0644)
788-
if err == nil {
789-
defer tmp.Close()
790-
791-
fi, err := tmp.Stat()
792-
if err == nil {
793-
794-
fileSize := fi.Size()
795-
796-
// Get file size, in blocks
797-
x.RC = uint8(fileSize / blkSize)
775+
// Get file size, in blocks.
776+
x.RC = uint8(res.Size / blkSize)
798777

799-
// If the size is bigger than a multiple we deal with that.
800-
if fileSize > int64(int64(x.RC)*int64(blkSize)) {
801-
x.RC++
802-
}
803-
804-
}
778+
// If the size is bigger than a multiple we deal with that.
779+
if res.Size > int64(int64(x.RC)*int64(blkSize)) {
780+
x.RC++
805781
}
806782

807783
// Log that we're returning the next result.

fcb/fcb.go

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
package fcb
33

44
import (
5+
"io/fs"
56
"log/slog"
6-
"os"
77
"path/filepath"
88
"strings"
99
"unicode"
@@ -71,6 +71,9 @@ type Find struct {
7171
// Name is the name as CP/M would see it.
7272
// This will be upper-cased and in 8.3 format.
7373
Name string
74+
75+
// Size contains the size of the file.
76+
Size int64
7477
}
7578

7679
// GetName returns the name component of an FCB entry.
@@ -377,36 +380,40 @@ func (f *FCB) DoesMatch(name string) bool {
377380
func (f *FCB) GetMatches(prefix string) ([]Find, error) {
378381
var ret []Find
379382

380-
// Find files in the directory
381-
files, err := os.ReadDir(prefix)
382-
if err != nil {
383-
return ret, err
384-
}
383+
err := filepath.Walk(prefix, func(path string, info fs.FileInfo, err error) error {
385384

386-
// For each file
387-
for _, file := range files {
385+
if err != nil {
386+
return err
387+
}
388388

389389
// Ignore directories, we only care about files.
390-
if file.IsDir() {
391-
continue
390+
if info.IsDir() {
391+
return nil
392392
}
393393

394-
name := strings.ToUpper(file.Name())
394+
// Upper-case, and remove prefix.
395+
name := filepath.Base(strings.ToUpper(path))
396+
395397
if f.DoesMatch(name) {
396398

397399
var ent Find
398400

399401
// Populate the host-path before we do anything else.
400-
ent.Host = filepath.Join(prefix, file.Name())
402+
ent.Host = filepath.Join(path)
401403

402-
// populate the name, but note it needs to be upper-cased
404+
// populate the name
403405
ent.Name = name
404406

407+
// populate the size too
408+
ent.Size = info.Size()
409+
405410
// append
406411
ret = append(ret, ent)
407412
}
408-
}
413+
414+
return nil
415+
})
409416

410417
// Return the entries we found, if any.
411-
return ret, nil
418+
return ret, err
412419
}

fcb/fcb_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fcb
22

33
import (
44
"fmt"
5+
"sort"
56
"testing"
67
)
78

@@ -207,13 +208,30 @@ func TestGetMatches(t *testing.T) {
207208
t.Fatalf("failed to get matches")
208209
}
209210

210-
if len(out) != 1 {
211-
t.Fatalf("unexpected number of matches")
211+
if len(out) < 10 {
212+
t.Fatalf("unexpected number of matches got %d", len(out))
212213
}
213-
if out[0].Host != "../main.go" {
214+
215+
// sort the files - so we can be predictable
216+
sort.Slice(out, func(i, j int) bool {
217+
return out[i].Name < out[j].Name
218+
})
219+
220+
// first file, alphabetically
221+
if out[0].Host != "../ccp/ccp.go" {
214222
t.Fatalf("unexpected name %s", out[0].Host)
215223
}
216224

225+
found := false
226+
for _, e := range out {
227+
if e.Host == "../static/static.go" {
228+
found = true
229+
}
230+
}
231+
if !found {
232+
t.Fatalf("failed to find static.go")
233+
}
234+
217235
_, err = f.GetMatches("!>>//path/not/found")
218236
if err == nil {
219237
t.Fatalf("expected error on bogus directory, got none")

0 commit comments

Comments
 (0)