Open and send file requested by client
[m6809-computer-tools:dload-server.git] / dload.c
1 /*  DLOAD                  16 Jan 88           Burt Mitchell               */
2 /*                                                                         */
3 /*  Host software for an PC compatible to send files to a                  */
4 /*  Color Computer using the DLOAD command.                                */
5 /*  The Coco must be running Extended Color Basic v1.1 or OLDER            */
6
7 /*  This program uses 1200 baud as it is the default for the Coco.  It is  */
8 /*  configured to use the PC's com1: port. To change this you will have to */
9 /*  edit the procedures transmit_char() and receive_char().                */
10
11 /*  This program has not been tested in the DLOADM mode as I have no way   */
12 /*  of getting a Coco machine language routine into the PC in the first    */
13 /*  place.                                                                 */
14
15 /*  To operate DLOAD:                                                      */
16 /*  on the PC enter     DLOAD filename.ext a                               */
17 /*  the PC will inform you that it is waiting.                             */
18 /*  on the Coco enter   DLOAD                                              */
19
20 /* Modified for use with unix by Bryan Clingman (bac@realtimeweb.com)      */
21 /* 3-Jun-1997                                                                                                                      */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27
28 #define true    1
29 #define false   0
30 #define ok              1
31 #define bad             0
32
33 /* literals for the DLOAD protocol */
34
35 #define file_req        0x8A
36 #define block_req       0x97
37 #define NAK                     0xDE
38 #define ACK                     0xC8
39 #define EOT                     0xBC
40 #define ABORT           0xBC
41 #define ASCII           0xFF
42 #define BINARY          0x00
43 #define BASIC           0x00
44 #define ML                      0x02
45 #define block_size      127
46
47 unsigned char   chksum,
48                                 file_type,
49                                 asc_flag,
50                                 proceed;
51 unsigned char           rx_char;
52 char                    *tx_name;
53 char                    *port;
54 FILE                    *infile;
55 FILE                    *fp;
56 int                     tx_delay;
57
58 void transmit_char(unsigned char tx_char)
59 {
60         fputc(tx_char, fp);
61 #ifdef DEBUG
62         fprintf(stderr, " [%02X]", tx_char);
63 #endif
64         usleep(tx_delay);
65 }
66
67 void receive_char()
68 {
69         rx_char = fgetc(fp);
70 #ifdef DEBUG
71         if (rx_char != 0xFF && rx_char != 0)
72                 fprintf(stderr, " %02X", rx_char);
73 #endif
74         if (rx_char == ABORT)
75         {
76 #ifdef DEBUG
77                 fprintf(stderr, "\n");
78 #endif
79                 fprintf(stderr, "transfer aborted by target.\n");
80                 exit(1);
81         }
82 }
83
84 void initiate_transfer()
85 {
86         chksum = (file_type ^ asc_flag);
87         transmit_char(ACK);
88         transmit_char(file_type);
89         transmit_char(asc_flag);
90         transmit_char(chksum);
91 }
92
93 char rcv_file_name(char *filename)
94 {
95         int     count;
96         char    sum;
97         char    status;
98
99         filename[8] = 0;
100         count = 0;
101         while (count <= 7)
102         {
103                 receive_char();
104                 filename[count] = rx_char;
105                 chksum = (chksum ^ rx_char);
106                 ++count;
107         }
108
109         receive_char();
110         sum = rx_char;
111
112 #ifdef DEBUG
113         fprintf(stderr, "\n");
114 #endif
115         fprintf(stderr, "target requested \"%s\"\n", filename);
116
117         if (sum != chksum)
118         {
119 #ifdef DEBUG
120                 fprintf(stderr, "\n");
121 #endif
122                 fprintf(stderr, "filename checksum bad.\n");
123                 status = bad;
124                 filename[0] = '\0';
125         }
126         else
127                 status = ok;
128         return status;
129 }
130
131 void send_file()
132 {
133         int                             count,
134                                         j;
135         char                    tx_flag,
136                                         xfer_done,
137                                         first_time,
138                                         send_new_block;
139         unsigned char   file_char[130],
140                                         xfer_length,
141                                         sum,
142                                         block_num,
143                                         old_block_num;
144         long int                total_xfer;
145
146         /**/
147
148         first_time = true;
149         total_xfer = 0;
150         xfer_length = 0;
151         xfer_done = false;
152         tx_flag = true;
153
154         while (tx_flag == true)
155         {
156                 send_new_block = true;
157
158                 while (rx_char != block_req)
159                         receive_char();
160
161                 transmit_char(block_req);
162
163                 chksum = 0;
164
165                 /* get the MSByte of the block number */
166
167                 receive_char();
168                 chksum = (chksum ^ rx_char);
169                 block_num = (rx_char << 7);
170
171                 /* get the LSByte of the block number */
172
173                 receive_char();
174                 chksum = (chksum ^ rx_char);
175                 block_num = (rx_char | block_num);
176
177 #ifdef DEBUG
178                 fprintf(stderr, "\nRequested block # %i\n", block_num);
179 #endif
180                 /* should we retransmit the previous block? */
181
182                 if (first_time == false)
183                         if (block_num == old_block_num)
184                                 send_new_block = false;
185
186
187                 /* Get the checksum for the block number being requested. */
188                 /* Does it match our calculation?                         */
189                 
190                 receive_char();
191                 sum = rx_char;
192
193                 if (sum == chksum)
194                 {
195                         transmit_char(ACK);
196                         first_time = false;
197                 
198                         if (xfer_done == true)
199                         {
200                                 /* Send a block with a length of 0 and 128 bytes of */
201                                 /* anything along with a checksum for the block.    */
202                                 /* This tells the Coco that the file is ended.      */
203                 
204                                 tx_flag = false;
205                                 xfer_length = 0;
206                 
207                                 j = 0;
208                                 while (j <= block_size)
209                                 {
210                                         file_char[j] = 0;
211                                         ++j;
212                                 }
213                         }
214                         else
215                         {
216                                 /* go ahead and read in another 128 bytes.           */
217                                 /* If there aren't 128 bytes available pad the data  */
218                                 /* to 128 bytes. The checksum must still be correct. */
219
220                                 if (send_new_block == true)
221                                 {
222                                         count = 0;
223                                         while (count <= block_size)
224                                         {
225                                                 file_char[count] = getc(infile);
226                                                 /* The Coco can't handle linefeeds */
227                                                 if (asc_flag == ASCII)
228                                                         if (file_char[count] == '\n')
229                                                                 file_char[count] = '\r';
230                                                 xfer_length = (count + 1);
231                                                 ++count;
232                                                 if (feof(infile))
233                                                 {
234                                                         xfer_done = true;
235                                                         while (count <= block_size)
236                                                         {
237                                                                 file_char[count] = 0;
238                                                                 ++count;
239                                                                 if (asc_flag != ASCII)
240                                                                         tx_flag = false;
241                                                         }
242                                                 }
243                                         }
244                                 }
245                                 else
246                                 {
247 #ifdef DEBUG
248                                         fprintf(stderr, "\n");
249 #endif
250                                         fprintf(stderr, "Re-transmitting block #%d\n", block_num);
251                                 }
252                         }
253
254                         /* transmit whatever is in the buffer */
255
256                         transmit_char(xfer_length);
257
258                         chksum = 0;
259                         chksum = (chksum ^ xfer_length);
260                         count = 0;
261                         while (count <= block_size)
262                         {
263                                 transmit_char(file_char[count]);
264                                 chksum = (chksum ^ file_char[count]);
265                                 ++count;
266                         }
267                         transmit_char(chksum); 
268                 
269                         total_xfer = (total_xfer + xfer_length);
270 #ifdef DEBUG
271                         fprintf(stderr, "\n");
272 #endif
273                         fprintf(stderr, "Total transmitted = %ld\r", total_xfer);
274 #ifdef DEBUG
275                         fprintf(stderr, "\n");
276 #endif
277                         old_block_num = block_num;
278                 }
279                 else
280                 {
281                         fprintf(stderr, "checksum mismatch\n");
282                         transmit_char(NAK);
283                 }
284         }
285         fprintf(stderr, "\n");
286 }
287
288 void xfer_abort()
289 {
290         transmit_char(NAK);
291 #ifdef DEBUG
292         fprintf(stderr, "\n");
293 #endif
294         fprintf(stderr, "transfer aborted by host\n");
295         exit(1);
296 }
297
298 void init_port()
299 {
300         fp = fopen(port,"a+");
301         if (!fp)
302                 fprintf(stderr, "Error opening port\n");
303         setvbuf(fp, NULL, _IONBF, 0);
304 }
305
306 int main(int argc, char *argv[])
307 {
308         char    filename[9];
309         char    status;
310
311         if (argc < 4)
312         {
313                 fprintf(stderr, "usage: DLOAD [filename.ext] [A or B] [port]\n");
314                 exit(1);
315         }
316
317         tx_name = argv[1];
318
319         if ((*argv[2] == 'B') || (*argv[2] == 'b'))
320         {
321                 file_type = ML;
322                 asc_flag = BINARY;
323                 infile = fopen(tx_name,"rb");
324         }
325         else
326                 if ((*argv[2] == 'A') || (*argv[2] == 'a'))
327                 {
328                         file_type = BASIC;
329                         asc_flag = ASCII;
330                         infile = fopen(tx_name,"r");
331                 }
332                 else
333                 {
334                         fprintf(stderr, "unknown file type\n");
335                         exit(1);
336                 }
337
338         if (infile == NULL)
339         {
340                 fprintf(stderr, "cant open the file to be transmitted\n");
341                 exit(1);
342         }
343
344         if (argc > 4)
345                 tx_delay = atoi(argv[4]);
346         else
347                 tx_delay = 0;
348
349         port = argv[3];
350         init_port();
351
352         /* wait for the coco to start the transfer */
353
354         while (rx_char != file_req)
355                 receive_char();
356
357         proceed = true;
358
359         /* proceed with the file transfer */
360         while (proceed == true)
361         {
362                 /* get the file name from the coco. We arent using it but */
363                 /* it's part of the protocol. */
364
365                 transmit_char(file_req);
366
367                 status = rcv_file_name(filename);
368                 /* if non-empty file name, switch file */
369                 if (status == ok && strcmp(filename, "        "))
370                 {
371                         int lastc;
372
373                         /* zero out trailing spaces */
374                         for (lastc = 7; lastc > 0; lastc--)
375                         {
376                                 if (filename[lastc] == ' ')
377                                         filename[lastc] = '\0';
378                                 else
379                                         break;
380                         }
381                         fprintf(stderr, "trimmed to \"%s\"\n", filename);
382                         fclose(infile);
383                         /* only binary supported for now */
384                         infile = fopen(filename,"rb");
385                         if (infile == NULL)
386                         {
387                                 fprintf(stderr, "cannot open %s\n", filename);
388                                 /* abort and wait for new request */
389                                 /* FIXME Dragon does not interpret NAK here */
390                                 transmit_char(NAK);
391                                 while (rx_char != file_req)
392                                         receive_char();
393                                 infile = fopen(tx_name, "rb");
394                                 continue;
395                         }
396                 }
397
398                 /* tell the coco about the file it will receive. */
399
400                 initiate_transfer();
401
402                 /* now send the file */
403
404                 send_file();
405
406                 if (status == ok)
407                 {
408                         proceed = false;
409                 }
410                 else
411                 {
412                         proceed = false;
413                         fprintf(stderr, "file not transmitted successfully\n");
414                 }
415         }
416         return 0;
417 }
418