commit e5299bb

zzo38  ·  2022-11-16 06:15:51 +0000 UTC
parent 5ed07f5
Extended delete/append ports, to add additional functions such as seeking.
3 files changed,  +148, -53
M main.c
+58, -0
 1@@ -186,6 +186,9 @@ The vector is called after passing command-line arguments, but before any
 2 other events occur. If accepting input from stdin, then it will also be
 3 called after all input from stdin has been read.
 4 
 5+The -x switch also changes the meaning of the delete and append ports of
 6+the file device. See below about extended file device.
 7+
 8 (Note: Using this feature will make the program not portable to other
 9 implementations, and should be avoided if possible. However, it is
10 available if its functions (e.g. expanded memory) are needed in your
11@@ -193,6 +196,61 @@ program for any reason. Most programs probably will not need expanded
12 memory; some might be able to optionally use it.)
13 
14 
15+=== Extended file device ===
16+
17+If the -x switch is used, then the delete and append ports of the file
18+device have a different meaning than usual.
19+
20+N.B. Use of these extensions in an implementation which does not support
21+them may cause files to be inadvertently deleted or corrupted. (This
22+implementation will ignore the delete port entirely if the -x switch is
23+not specified.) Furthermore, they may be changed in future; please make
24+a discussion if you have any intention to use these extensions, or even
25+to use the delete/append ports without these extensions. There are some
26+problems with the design of the file device, so I would want to suggest
27+these changes in order to make an improvement. Unfortunately, it is not
28+as simple as other devices, but it may be necessary if file handling is
29+to be working properly.
30+
31+The delete port can be given any of the values:
32+
33+* 0x00 = Does nothing.
34+
35+* 0x01 = Delete the current file. (Not currently implemented.)
36+
37+* 0x10 = Close the file.
38+
39+* 0x11 = Open the file for reading. Success is 1 if opened.
40+
41+* 0x12 = Open the file for writing. Success is 1 if opened.
42+
43+* 0x80 to 0x8F = Seek. Bit0 and bit1 specify how much the length field
44+should be multiplied by, where 0 means zero, 1 means one, 2 means 64K,
45+and 3 is reserved. Bit2 and bit3 indicate where to seek and which way,
46+where 0 means forward from the current position, 1 means backward from
47+the current position, 2 means forward from the beginning, and 3 means
48+backward from the beginning.
49+
50+Telling it to open a file will close it first, even if it is already open
51+in the correct mode. Also, seeking will not work unless the file is already
52+open, so it must first be opened, either by one of the above commands for
53+opening a file or by attempting to read from or write to it.
54+
55+The append port has one of the following values:
56+
57+* 0 = Files opened for writing are truncated.
58+
59+* 1 = Files opened for writing will append at the end of the file.
60+
61+* 2 = Files are opened for both reading and writing. In this case,
62+switching from reading to writing or vice-versa will not close and reopen
63+the file like it does in other cases. If writing is disabled, then this is
64+treated the same as 0.
65+
66+Any value may be bitwise ORed by sixteen to refuse to open a file for
67+writing if the file already exists.
68+
69+
70 === Extension loading ===
71 
72 (This section describes a feature which is not implemented yet. Some
M main.c
+88, -53
  1@@ -150,6 +150,7 @@ static Uint8 use_screen=1;
  2 static Uint8 use_mouse=1;
  3 static Uint8 use_debug=0;
  4 static Uint8 use_utc=0;
  5+static Uint8 use_extension=0;
  6 static Uint8 hide_cursor=0;
  7 static Uint8 allow_write=0;
  8 static Uint8 zoom=1;
  9@@ -420,6 +421,71 @@ static Uint16 files_stat(char*name,Uint16 len,Uint8*out) {
 10   return 0;
 11 }
 12 
 13+static void files_seek(UxnFile*uf,Uint8 mode,Uint32 len) {
 14+  switch(mode&3) {
 15+    case 0: len=0; break;
 16+    case 1: /* do nothing */ break;
 17+    case 2: len<<=16; break;
 18+    case 3: return;
 19+  }
 20+  switch(uf->mode) {
 21+    case 1: case 2: case 4:
 22+      switch((mode>>2)&3) {
 23+        case 0: fseek(uf->file,len,SEEK_CUR); break;
 24+        case 1: fseek(uf->file,-len,SEEK_CUR); break;
 25+        case 2: fseek(uf->file,len,SEEK_SET); break;
 26+        case 3: fseek(uf->file,-len,SEEK_END); break;
 27+      }
 28+      break;
 29+  }
 30+}
 31+
 32+static int files_set(Device*dev,int rw) {
 33+  // rw: 0(close) 1(read) 2(write)
 34+  // return: 1 if file opened, 0 if not opened
 35+  Uint8 m=dev->d[7];
 36+  UxnFile*uf=dev->aux;
 37+  if(uf->file) fclose(uf->file);
 38+  if(uf->dir) closedir(uf->dir);
 39+  uf->file=0;
 40+  uf->dir=0;
 41+  uf->de=0;
 42+  uf->mode=0;
 43+  dev->d[2]=dev->d[3]=0;
 44+  if(!uf->name[0]) return 0;
 45+  if(m && !use_extension) m=1;
 46+  if((m&15)==2 && rw && allow_write) {
 47+    if(rw==1) m&=15;
 48+    if(uf->file=fopen(uf->name,m&16?"w+x":"r+")) {
 49+      uf->mode=4;
 50+      return 1;
 51+    } else if(uf->file=fopen(uf->name,m&16?"w+x":"w+")) {
 52+      uf->mode=4;
 53+      return 1;
 54+    } else {
 55+      warn("Cannot open file '%s' for reading and writing",uf->name);
 56+    }
 57+  } else if(rw==1) {
 58+    if(uf->dir=opendir(uf->name)) {
 59+      uf->mode=3;
 60+      return 1;
 61+    } else if(uf->file=fopen(uf->name,"r")) {
 62+      uf->mode=1;
 63+      return 1;
 64+    } else {
 65+      warn("Cannot open file '%s' for reading",uf->name);
 66+    }
 67+  } else if(rw==2 && allow_write) {
 68+    if(uf->file=fopen(uf->name,m==1?"a":m==17?"ax":m==16?"wx":"w")) {
 69+      uf->mode=2;
 70+      return 1;
 71+    } else {
 72+      warn("Cannot open file '%s' for writing",uf->name);
 73+    }
 74+  }
 75+  return 0;
 76+}
 77+
 78 static void files_out(Device*dev,Uint8 id) {
 79   UxnFile*uf=dev->aux;
 80   Uint16 len=GET16(dev->d+10);
 81@@ -433,15 +499,22 @@ static void files_out(Device*dev,Uint8 id) {
 82       y=files_stat(uf->name,len,mem+addr);
 83       if(y<=len) PUT16(dev->d+2,y);
 84       break;
 85+    case 6:
 86+      if(!use_extension) break;
 87+      dev->d[2]=dev->d[3]=0;
 88+      switch(dev->d[6]) {
 89+        case 0x10 ... 0x12: // Close/open
 90+          if(dev->d[6]==0x12 && !allow_write) goto disallow;
 91+          dev->d[3]=files_set(dev,dev->d[6]&0x0F);
 92+          break;
 93+        case 0x80 ... 0x8F: // Seek
 94+          files_seek(uf,dev->d[6]&0x0F,len);
 95+          break;
 96+      }
 97+      break;
 98     case 9:
 99       addr=GET16(dev->d+8);
100-      if(uf->file) fclose(uf->file);
101-      if(uf->dir) closedir(uf->dir);
102-      uf->file=0;
103-      uf->dir=0;
104-      uf->de=0;
105-      uf->mode=0;
106-      dev->d[2]=dev->d[3]=0;
107+      files_set(dev,0);
108       x=GET16(dev->d+8);
109       for(i=0;i<4095;i++) {
110         if(!(uf->name[i]=mem[x+i])) break;
111@@ -453,7 +526,7 @@ static void files_out(Device*dev,Uint8 id) {
112       addr=GET16(dev->d+12);
113       dev->d[2]=dev->d[3]=0;
114       read:
115-      if(uf->mode==1) {
116+      if(uf->mode==1 || uf->mode==4) {
117         if(!len) break;
118         x=fread(mem+addr,1,len,uf->file);
119         PUT16(dev->d+2,x);
120@@ -464,32 +537,19 @@ static void files_out(Device*dev,Uint8 id) {
121       } else if(uf->mode==3) {
122         //TODO
123       } else {
124-        if(uf->file) fclose(uf->file);
125-        if(uf->dir) closedir(uf->dir);
126-        uf->file=0;
127-        uf->dir=0;
128-        uf->de=0;
129-        if(!uf->name[0]) break;
130-        if(uf->dir=opendir(uf->name)) {
131-          uf->mode=3;
132-          goto read;
133-        } else if(uf->file=fopen(uf->name,"r")) {
134-          uf->mode=1;
135-          goto read;
136-        } else {
137-          warn("Cannot open file '%s' for reading",uf->name);
138-        }
139+        if(files_set(dev,1)) goto read;
140       }
141       break;
142     case 15:
143       addr=GET16(dev->d+14);
144       dev->d[2]=dev->d[3]=0;
145       if(!allow_write) {
146+        disallow:
147         warnx("Not allowed to write to file '%s'.",uf->name);
148         break;
149       }
150       write:
151-      if(uf->mode==2) {
152+      if(uf->mode==2 || uf->mode==4) {
153         if(!len) break;
154         x=fwrite(mem+addr,1,len,uf->file);
155         PUT16(dev->d+2,x);
156@@ -498,18 +558,7 @@ static void files_out(Device*dev,Uint8 id) {
157           clearerr(uf->file);
158         }
159       } else {
160-        if(uf->file) fclose(uf->file);
161-        if(uf->dir) closedir(uf->dir);
162-        uf->file=0;
163-        uf->dir=0;
164-        uf->de=0;
165-        if(!uf->name[0]) break;
166-        if(uf->file=fopen(uf->name,dev->d[7]?"a":"w")) {
167-          uf->mode=2;
168-          goto read;
169-        } else {
170-          warn("Cannot open file '%s' for %sing",uf->name,dev->d[7]?"append":"writ");
171-        }
172+        if(files_set(dev,2)) goto write;
173       }
174       break;
175   }
176@@ -522,7 +571,7 @@ static Uint8 files_in(Device*dev,Uint8 id) {
177     case 12: case 13:
178       dev->d[2]=dev->d[3]=0;
179       read:
180-      if(uf->mode==1) {
181+      if(uf->mode==1 || uf->mode==4) {
182         c=fgetc(uf->file);
183         if(c==EOF) {
184           if(ferror(uf->file)) {
185@@ -537,21 +586,7 @@ static Uint8 files_in(Device*dev,Uint8 id) {
186       } else if(uf->mode==3) {
187         return 0;
188       } else {
189-        if(uf->file) fclose(uf->file);
190-        if(uf->dir) closedir(uf->dir);
191-        uf->file=0;
192-        uf->dir=0;
193-        uf->de=0;
194-        if(!uf->name[0]) break;
195-        if(uf->name[0]=='.' && !uf->name[1] && (uf->dir=opendir(uf->name))) {
196-          uf->mode=3;
197-          goto read;
198-        } else if(uf->file=fopen(uf->name,"r")) {
199-          uf->mode=1;
200-          goto read;
201-        } else {
202-          warn("Cannot open file '%s' for reading",uf->name);
203-        }
204+        if(files_set(dev,1)) goto read;
205       }
206       return 0;
207     default: return dev->d[id];
208@@ -974,7 +1009,7 @@ int main(int argc,char**argv) {
209     case 'q': use_console=0; break;
210     case 't': timer_rate=strtol(optarg,0,10); break;
211     case 'w': default_width=strtol(optarg,0,10); break;
212-    case 'x': device[15].in=extension_in; device[15].out=extension_out; break;
213+    case 'x': use_extension=1; device[15].in=extension_in; device[15].out=extension_out; break;
214     case 'y': allow_write=0; device[10].out=device[11].out=files_out; device[10].in=device[11].in=files_in; break;
215     case 'z': zoom=strtol(optarg,0,10); break;
216     default: return 1;
+2, -0
1@@ -226,6 +226,8 @@ long read since if you write to only 0xAC then the address is undefined.
2 
3 (TODO)
4 
5+See also emulator.doc for nonstandard meanings of delete/append ports.
6+
7 
8 === Date/time ===
9