Strncpy elimination.
[wine] / dlls / winedos / devices.c
1 /*
2  * DOS devices
3  *
4  * Copyright 1999 Ove Kåven
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include "wine/winbase16.h"
24 #include "dosexe.h"
25 #include "wine/debug.h"
26
27 #include "pshpack1.h"
28
29 typedef struct {
30   BYTE ljmp1;
31   RMCBPROC strategy;
32   BYTE ljmp2;
33   RMCBPROC interrupt;
34 } WINEDEV_THUNK;
35
36 typedef struct {
37   BYTE size; /* length of header + data */
38   BYTE unit; /* unit (block devices only) */
39   BYTE command;
40   WORD status;
41   BYTE reserved[8];
42 } REQUEST_HEADER;
43
44 typedef struct {
45   REQUEST_HEADER hdr;
46   BYTE media; /* media descriptor from BPB */
47   SEGPTR buffer;
48   WORD count; /* byte/sector count */
49   WORD sector; /* starting sector (block devices) */
50   DWORD volume; /* volume ID (block devices) */
51 } REQ_IO;
52
53 typedef struct {
54   REQUEST_HEADER hdr;
55   BYTE data;
56 } REQ_SAFEINPUT;
57
58 typedef struct
59 {
60     DWORD next_dev;
61     WORD  attr;
62     WORD  strategy;
63     WORD  interrupt;
64     char  name[8];
65 } DOS_DEVICE_HEADER;
66
67 /* Warning: need to return LOL ptr w/ offset 0 (&ptr_first_DPB) to programs ! */
68 typedef struct _DOS_LISTOFLISTS
69 {
70     WORD  CX_Int21_5e01;        /* -24d contents of CX from INT 21/AX=5E01h */
71     WORD  LRU_count_FCB_cache;  /* -22d */
72     WORD  LRU_count_FCB_open;   /* -20d */
73     DWORD OEM_func_handler;     /* -18d OEM function of INT 21/AH=F8h */
74     WORD  INT21_offset;         /* -14d offset in DOS CS of code to return from INT 21 call */
75     WORD  sharing_retry_count;  /* -12d */
76     WORD  sharing_retry_delay;  /* -10d */
77     DWORD ptr_disk_buf;         /* -8d ptr to current disk buf */
78     WORD  offs_unread_CON;      /* -4d pointer in DOS data segment of unread CON input */
79     WORD  seg_first_MCB;        /* -2d */
80     DWORD ptr_first_DPB;        /* 00 */
81     DWORD ptr_first_SysFileTable; /* 04 */
82     DWORD ptr_clock_dev_hdr;    /* 08 */
83     DWORD ptr_CON_dev_hdr;      /* 0C */
84     WORD  max_byte_per_sec;     /* 10 maximum bytes per sector of any block device */
85     DWORD ptr_disk_buf_info;    /* 12 */
86     DWORD ptr_array_CDS;        /* 16 current directory structure */
87     DWORD ptr_sys_FCB;          /* 1A */
88     WORD  nr_protect_FCB;       /* 1E */
89     BYTE  nr_block_dev;         /* 20 */
90     BYTE  nr_avail_drive_letters; /* 21 */
91     DOS_DEVICE_HEADER NUL_dev;  /* 22 */
92     BYTE  nr_drives_JOINed;     /* 34 */
93     WORD  ptr_spec_prg_names;   /* 35 */
94     DWORD ptr_SETVER_prg_list;  /* 37 */
95     WORD DOS_HIGH_A20_func_offs;/* 3B */
96     WORD PSP_last_exec;         /* 3D if DOS in HMA: PSP of program executed last; if DOS low: 0000h */
97     WORD BUFFERS_val;           /* 3F */
98     WORD BUFFERS_nr_lookahead;  /* 41 */
99     BYTE boot_drive;            /* 43 */
100     BYTE flag_DWORD_moves;      /* 44 01h for 386+, 00h otherwise */
101     WORD size_extended_mem;     /* 45 size of extended mem in KB */
102     SEGPTR wine_rm_lol;         /* -- wine: Real mode pointer to LOL */
103     SEGPTR wine_pm_lol;         /* -- wine: Protected mode pointer to LOL */
104 } DOS_LISTOFLISTS;
105
106 #include "poppack.h"
107
108 #define CON_BUFFER 128
109
110 enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES };
111
112 static void *strategy_data[NB_SYSTEM_STRATEGIES];
113
114 #define NONEXT ((DWORD)-1)
115
116 #define ATTR_STDIN     0x0001
117 #define ATTR_STDOUT    0x0002
118 #define ATTR_NUL       0x0004
119 #define ATTR_CLOCK     0x0008
120 #define ATTR_FASTCON   0x0010
121 #define ATTR_RAW       0x0020
122 #define ATTR_NOTEOF    0x0040
123 #define ATTR_DEVICE    0x0080
124 #define ATTR_REMOVABLE 0x0800
125 #define ATTR_NONIBM    0x2000 /* block devices */
126 #define ATTR_UNTILBUSY 0x2000 /* char devices */
127 #define ATTR_IOCTL     0x4000
128 #define ATTR_CHAR      0x8000
129
130 #define CMD_INIT       0
131 #define CMD_MEDIACHECK 1 /* block devices */
132 #define CMD_BUILDBPB   2 /* block devices */
133 #define CMD_INIOCTL    3
134 #define CMD_INPUT      4 /* read data */
135 #define CMD_SAFEINPUT  5 /* "non-destructive input no wait", char devices */
136 #define CMD_INSTATUS   6 /* char devices */
137 #define CMD_INFLUSH    7 /* char devices */
138 #define CMD_OUTPUT     8 /* write data */
139 #define CMD_SAFEOUTPUT 9 /* write data with verify */
140 #define CMD_OUTSTATUS 10 /* char devices */
141 #define CMD_OUTFLUSH  11 /* char devices */
142 #define CMD_OUTIOCTL  12
143 #define CMD_DEVOPEN   13
144 #define CMD_DEVCLOSE  14
145 #define CMD_REMOVABLE 15 /* block devices */
146 #define CMD_UNTILBUSY 16 /* output until busy */
147
148 #define STAT_MASK  0x00FF
149 #define STAT_DONE  0x0100
150 #define STAT_BUSY  0x0200
151 #define STAT_ERROR 0x8000
152
153 #define LJMP 0xea
154
155
156 /* prototypes */
157 static void WINAPI nul_strategy(CONTEXT86*ctx);
158 static void WINAPI nul_interrupt(CONTEXT86*ctx);
159 static void WINAPI con_strategy(CONTEXT86*ctx);
160 static void WINAPI con_interrupt(CONTEXT86*ctx);
161
162 /* devices */
163 typedef struct
164 {
165     char name[8];
166     WORD attr;
167     RMCBPROC strategy;
168     RMCBPROC interrupt;
169 } WINEDEV;
170
171 static WINEDEV devs[] =
172 {
173   { "NUL     ",
174     ATTR_CHAR|ATTR_NUL|ATTR_DEVICE,
175     nul_strategy, nul_interrupt },
176
177   { "CON     ",
178     ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE,
179     con_strategy, con_interrupt }
180 };
181
182 #define NR_DEVS (sizeof(devs)/sizeof(WINEDEV))
183
184 /* DOS data segment */
185 typedef struct
186 {
187     DOS_LISTOFLISTS    lol;
188     DOS_DEVICE_HEADER  dev[NR_DEVS-1];
189     WINEDEV_THUNK      thunk[NR_DEVS];
190     REQ_IO             req;
191     BYTE               buffer[CON_BUFFER];
192
193 } DOS_DATASEG;
194
195 #define DOS_DATASEG_OFF(xxx) FIELD_OFFSET(DOS_DATASEG, xxx)
196
197 DWORD DOS_LOLSeg;
198
199 static struct _DOS_LISTOFLISTS * DOSMEM_LOL()
200 {
201     return PTR_REAL_TO_LIN(HIWORD(DOS_LOLSeg),0);
202 }
203
204
205 /* the device implementations */
206 static void do_lret(CONTEXT86*ctx)
207 {
208   WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegSs, ctx->Esp);
209
210   ctx->Eip   = *(stack++);
211   ctx->SegCs = *(stack++);
212   ctx->Esp  += 2*sizeof(WORD);
213 }
214
215 static void do_strategy(CONTEXT86*ctx, int id, int extra)
216 {
217   REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx);
218   void **hdr_ptr = strategy_data[id];
219
220   if (!hdr_ptr) {
221     hdr_ptr = calloc(1,sizeof(void *)+extra);
222     strategy_data[id] = hdr_ptr;
223   }
224   *hdr_ptr = hdr;
225   do_lret(ctx);
226 }
227
228 static REQUEST_HEADER * get_hdr(int id, void**extra)
229 {
230   void **hdr_ptr = strategy_data[id];
231   if (extra)
232     *extra = hdr_ptr ? (void*)(hdr_ptr+1) : (void *)NULL;
233   return hdr_ptr ? *hdr_ptr : (void *)NULL;
234 }
235
236 static void WINAPI nul_strategy(CONTEXT86*ctx)
237 {
238   do_strategy(ctx, SYSTEM_STRATEGY_NUL, 0);
239 }
240
241 static void WINAPI nul_interrupt(CONTEXT86*ctx)
242 {
243   REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_NUL, NULL);
244   /* eat everything and recycle nothing */
245   switch (hdr->command) {
246   case CMD_INPUT:
247     ((REQ_IO*)hdr)->count = 0;
248     hdr->status = STAT_DONE;
249     break;
250   case CMD_SAFEINPUT:
251     hdr->status = STAT_DONE|STAT_BUSY;
252     break;
253   default:
254     hdr->status = STAT_DONE;
255   }
256   do_lret(ctx);
257 }
258
259 static void WINAPI con_strategy(CONTEXT86*ctx)
260 {
261   do_strategy(ctx, SYSTEM_STRATEGY_CON, sizeof(int));
262 }
263
264 static void WINAPI con_interrupt(CONTEXT86*ctx)
265 {
266   int *scan;
267   REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_CON,(void **)&scan);
268   BIOSDATA *bios = DOSVM_BiosData();
269   WORD CurOfs = bios->NextKbdCharPtr;
270   DOS_LISTOFLISTS *lol = DOSMEM_LOL();
271   DOS_DATASEG *dataseg = (DOS_DATASEG *)lol;
272   BYTE *linebuffer = dataseg->buffer;
273   BYTE *curbuffer = (lol->offs_unread_CON) ?
274     (((BYTE*)dataseg) + lol->offs_unread_CON) : (BYTE*)NULL;
275   DOS_DEVICE_HEADER *con = dataseg->dev;
276   DWORD w;
277
278   switch (hdr->command) {
279   case CMD_INPUT:
280     {
281       REQ_IO *io = (REQ_IO *)hdr;
282       WORD count = io->count, len = 0;
283       BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
284                                         SELECTOROF(io->buffer),
285                                         (DWORD)OFFSETOF(io->buffer));
286
287       hdr->status = STAT_BUSY;
288       /* first, check whether we already have data in line buffer */
289       if (curbuffer) {
290         /* yep, copy as much as we can */
291         BYTE data = 0;
292         while ((len<count) && (data != '\r')) {
293           data = *curbuffer++;
294           buffer[len++] = data;
295         }
296         if (data == '\r') {
297           /* line buffer emptied */
298           lol->offs_unread_CON = 0;
299           curbuffer = NULL;
300           /* if we're not in raw mode, call it a day */
301           if (!(con->attr & ATTR_RAW)) {
302             hdr->status = STAT_DONE;
303             io->count = len;
304             break;
305           }
306         } else {
307           /* still some data left */
308           lol->offs_unread_CON = curbuffer - (BYTE*)lol;
309           /* but buffer was filled, we're done */
310           hdr->status = STAT_DONE;
311           io->count = len;
312           break;
313         }
314       }
315
316       /* if we're in raw mode, we just need to fill the buffer */
317       if (con->attr & ATTR_RAW) {
318         while (len<count) {
319           WORD data;
320
321           /* do we have a waiting scancode? */
322           if (*scan) {
323             /* yes, store scancode in buffer */
324             buffer[len++] = *scan;
325             *scan = 0;
326             if (len==count) break;
327           }
328
329           /* check for new keyboard input */
330           while (CurOfs == bios->FirstKbdCharPtr) {
331             /* no input available yet, so wait... */
332             DOSVM_Wait( ctx );
333           }
334           /* read from keyboard queue (call int16?) */
335           data = ((WORD*)bios)[CurOfs];
336           CurOfs += 2;
337           if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
338           bios->NextKbdCharPtr = CurOfs;
339           /* if it's an extended key, save scancode */
340           if (LOBYTE(data) == 0) *scan = HIBYTE(data);
341           /* store ASCII char in buffer */
342           buffer[len++] = LOBYTE(data);
343         }
344       } else {
345         /* we're not in raw mode, so we need to do line input... */
346         while (TRUE) {
347           WORD data;
348           /* check for new keyboard input */
349           while (CurOfs == bios->FirstKbdCharPtr) {
350             /* no input available yet, so wait... */
351             DOSVM_Wait( ctx );
352           }
353           /* read from keyboard queue (call int16?) */
354           data = ((WORD*)bios)[CurOfs];
355           CurOfs += 2;
356           if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
357           bios->NextKbdCharPtr = CurOfs;
358
359           if (LOBYTE(data) == '\r') {
360             /* it's the return key, we're done */
361             linebuffer[len++] = LOBYTE(data);
362             break;
363           }
364           else if (LOBYTE(data) >= ' ') {
365             /* a character */
366             if ((len+1)<CON_BUFFER) {
367               linebuffer[len] = LOBYTE(data);
368               WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &linebuffer[len++], 1, &w, NULL);
369             }
370             /* else beep, but I don't like noise */
371           }
372           else switch (LOBYTE(data)) {
373           case '\b':
374             if (len>0) {
375               len--;
376               WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\b \b", 3, &w, NULL);
377             }
378             break;
379           }
380         }
381         if (len > count) {
382           /* save rest of line for later */
383           lol->offs_unread_CON = linebuffer - (BYTE*)lol + count;
384           len = count;
385         }
386         memcpy(buffer, linebuffer, len);
387       }
388       hdr->status = STAT_DONE;
389       io->count = len;
390     }
391     break;
392   case CMD_SAFEINPUT:
393     if (curbuffer) {
394       /* some line input waiting */
395       hdr->status = STAT_DONE;
396       ((REQ_SAFEINPUT*)hdr)->data = *curbuffer;
397     }
398     else if (con->attr & ATTR_RAW) {
399       if (CurOfs == bios->FirstKbdCharPtr) {
400         /* no input */
401         hdr->status = STAT_DONE|STAT_BUSY;
402       } else {
403         /* some keyboard input waiting */
404         hdr->status = STAT_DONE;
405         ((REQ_SAFEINPUT*)hdr)->data = ((BYTE*)bios)[CurOfs];
406       }
407     } else {
408       /* no line input */
409       hdr->status = STAT_DONE|STAT_BUSY;
410     }
411     break;
412   case CMD_INSTATUS:
413     if (curbuffer) {
414       /* we have data */
415       hdr->status = STAT_DONE;
416     }
417     else if (con->attr & ATTR_RAW) {
418       if (CurOfs == bios->FirstKbdCharPtr) {
419         /* no input */
420         hdr->status = STAT_DONE|STAT_BUSY;
421       } else {
422         /* some keyboard input waiting */
423         hdr->status = STAT_DONE;
424       }
425     } else {
426       /* no line input */
427       hdr->status = STAT_DONE|STAT_BUSY;
428     }
429
430     break;
431   case CMD_INFLUSH:
432     /* flush line and keyboard queue */
433     lol->offs_unread_CON = 0;
434     bios->NextKbdCharPtr = bios->FirstKbdCharPtr;
435     break;
436   case CMD_OUTPUT:
437   case CMD_SAFEOUTPUT:
438     {
439       REQ_IO *io = (REQ_IO *)hdr;
440       BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
441                                         SELECTOROF(io->buffer),
442                                         (DWORD)OFFSETOF(io->buffer));
443       DWORD result = 0;
444       WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, io->count, &result, NULL);
445       io->count = result;
446       hdr->status = STAT_DONE;
447     }
448     break;
449   default:
450     hdr->status = STAT_DONE;
451   }
452   do_lret(ctx);
453 }
454
455 static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL)
456 {
457 /*
458 Output of DOS 6.22:
459
460 0133:0020                    6A 13-33 01 CC 00 33 01 59 00         j.3...3.Y.
461 0133:0030  70 00 00 00 72 02 00 02-6D 00 33 01 00 00 2E 05   p...r...m.3.....
462 0133:0040  00 00 FC 04 00 00 03 08-92 21 11 E0 04 80 C6 0D   .........!......
463 0133:0050  CC 0D 4E 55 4C 20 20 20-20 20 00 00 00 00 00 00   ..NUL     ......
464 0133:0060  00 4B BA C1 06 14 00 00-00 03 01 00 04 70 CE FF   .K...........p..
465 0133:0070  FF 00 00 00 00 00 00 00-00 01 00 00 0D 05 00 00   ................
466 0133:0080  00 FF FF 00 00 00 00 FE-00 00 F8 03 FF 9F 70 02   ..............p.
467 0133:0090  D0 44 C8 FD D4 44 C8 FD-D4 44 C8 FD D0 44 C8 FD   .D...D...D...D..
468 0133:00A0  D0 44 C8 FD D0 44                                 .D...D
469 */
470   DOS_LOL->CX_Int21_5e01                = 0x0;
471   DOS_LOL->LRU_count_FCB_cache  = 0x0;
472   DOS_LOL->LRU_count_FCB_open           = 0x0;
473   DOS_LOL->OEM_func_handler             = -1; /* not available */
474   DOS_LOL->INT21_offset         = 0x0;
475   DOS_LOL->sharing_retry_count  = 3;
476   DOS_LOL->sharing_retry_delay  = 1;
477   DOS_LOL->ptr_disk_buf         = 0x0;
478   DOS_LOL->offs_unread_CON              = 0x0;
479   DOS_LOL->seg_first_MCB                = 0x0;
480   DOS_LOL->ptr_first_DPB                = 0x0;
481   DOS_LOL->ptr_first_SysFileTable       = 0x0;
482   DOS_LOL->ptr_clock_dev_hdr            = 0x0;
483   DOS_LOL->ptr_CON_dev_hdr              = 0x0;
484   DOS_LOL->max_byte_per_sec             = 512;
485   DOS_LOL->ptr_disk_buf_info            = 0x0;
486   DOS_LOL->ptr_array_CDS                = 0x0;
487   DOS_LOL->ptr_sys_FCB          = 0x0;
488   DOS_LOL->nr_protect_FCB               = 0x0;
489   DOS_LOL->nr_block_dev         = 0x0;
490   DOS_LOL->nr_avail_drive_letters       = 26; /* A - Z */
491   DOS_LOL->nr_drives_JOINed             = 0x0;
492   DOS_LOL->ptr_spec_prg_names           = 0x0;
493   DOS_LOL->ptr_SETVER_prg_list  = 0x0; /* no SETVER list */
494   DOS_LOL->DOS_HIGH_A20_func_offs       = 0x0;
495   DOS_LOL->PSP_last_exec                = 0x0;
496   DOS_LOL->BUFFERS_val          = 99; /* maximum: 99 */
497   DOS_LOL->BUFFERS_nr_lookahead = 8; /* maximum: 8 */
498   DOS_LOL->boot_drive                   = 3; /* C: */
499   DOS_LOL->flag_DWORD_moves             = 0x01; /* i386+ */
500   DOS_LOL->size_extended_mem            = 0xf000; /* very high value */
501 }
502
503 void DOSDEV_InstallDOSDevices(void)
504 {
505   DOS_DATASEG *dataseg;
506   WORD seg;
507   WORD selector;
508   unsigned int n;
509
510   /* allocate DOS data segment or something */
511   dataseg = DOSVM_AllocDataUMB( sizeof(DOS_DATASEG), &seg, &selector );
512
513   DOS_LOLSeg = MAKESEGPTR( seg, 0 );
514   DOSMEM_LOL()->wine_rm_lol = 
515       MAKESEGPTR( seg, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
516   DOSMEM_LOL()->wine_pm_lol = 
517       MAKESEGPTR( selector, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
518
519   /* initialize the magnificent List Of Lists */
520   InitListOfLists(&dataseg->lol);
521
522   /* Set up first device (NUL) */
523   dataseg->lol.NUL_dev.next_dev  = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
524   dataseg->lol.NUL_dev.attr      = devs[0].attr;
525   dataseg->lol.NUL_dev.strategy  = DOS_DATASEG_OFF(thunk[0].ljmp1);
526   dataseg->lol.NUL_dev.interrupt = DOS_DATASEG_OFF(thunk[0].ljmp2);
527   memcpy(dataseg->lol.NUL_dev.name, devs[0].name, 8);
528
529   /* Set up the remaining devices */
530   for (n = 1; n < NR_DEVS; n++)
531   {
532     dataseg->dev[n-1].next_dev  = (n+1) == NR_DEVS ? NONEXT :
533                                   MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[n]));
534     dataseg->dev[n-1].attr      = devs[n].attr;
535     dataseg->dev[n-1].strategy  = DOS_DATASEG_OFF(thunk[n].ljmp1);
536     dataseg->dev[n-1].interrupt = DOS_DATASEG_OFF(thunk[n].ljmp2);
537     memcpy(dataseg->dev[n-1].name, devs[n].name, 8);
538   }
539
540   /* Set up thunks */
541   for (n = 0; n < NR_DEVS; n++)
542   {
543     dataseg->thunk[n].ljmp1     = LJMP;
544     dataseg->thunk[n].strategy  = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].strategy);
545     dataseg->thunk[n].ljmp2     = LJMP;
546     dataseg->thunk[n].interrupt = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].interrupt);
547   }
548
549   /* CON is device 1 */
550   dataseg->lol.ptr_CON_dev_hdr = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
551 }
552
553 DWORD DOSDEV_Console(void)
554 {
555   return DOSMEM_LOL()->ptr_CON_dev_hdr;
556 }
557
558 DWORD DOSDEV_FindCharDevice(char*name)
559 {
560   SEGPTR cur_ptr = MAKESEGPTR(HIWORD(DOS_LOLSeg), FIELD_OFFSET(DOS_LISTOFLISTS,NUL_dev));
561   DOS_DEVICE_HEADER *cur = PTR_REAL_TO_LIN(SELECTOROF(cur_ptr),OFFSETOF(cur_ptr));
562   char dname[8];
563   int cnt;
564
565   /* get first 8 characters */
566   /* if less than 8 characters, pad with spaces */
567   for (cnt=0; name[cnt] && cnt<8; cnt++)
568     dname[cnt]=name[cnt];
569
570   while(cnt<8) dname[cnt++] = ' ';
571
572   /* search for char devices with the right name */
573   while (cur &&
574          ((!(cur->attr & ATTR_CHAR)) ||
575           memcmp(cur->name,dname,8))) {
576     cur_ptr = cur->next_dev;
577     if (cur_ptr == NONEXT) cur=NULL;
578     else cur = PTR_REAL_TO_LIN(SELECTOROF(cur_ptr),OFFSETOF(cur_ptr));
579   }
580   return cur_ptr;
581 }
582
583 static void DOSDEV_DoReq(void*req, DWORD dev)
584 {
585   REQUEST_HEADER *hdr = (REQUEST_HEADER *)req;
586   DOS_DEVICE_HEADER *dhdr;
587   CONTEXT86 ctx;
588   char *phdr;
589
590   dhdr = PTR_REAL_TO_LIN(SELECTOROF(dev),OFFSETOF(dev));
591   phdr = ((char*)DOSMEM_LOL()) + DOS_DATASEG_OFF(req);
592
593   /* copy request to request scratch area */
594   memcpy(phdr, req, hdr->size);
595
596   /* prepare to call device driver */
597   memset(&ctx, 0, sizeof(ctx));
598   ctx.EFlags |= V86_FLAG;
599
600   /* ES:BX points to request for strategy routine */
601   ctx.SegEs = HIWORD(DOS_LOLSeg);
602   ctx.Ebx   = DOS_DATASEG_OFF(req);
603
604   /* call strategy routine */
605   ctx.SegCs = SELECTOROF(dev);
606   ctx.Eip   = dhdr->strategy;
607   DPMI_CallRMProc(&ctx, 0, 0, 0);
608
609   /* call interrupt routine */
610   ctx.SegCs = SELECTOROF(dev);
611   ctx.Eip   = dhdr->interrupt;
612   DPMI_CallRMProc(&ctx, 0, 0, 0);
613
614   /* completed, copy request back */
615   memcpy(req, phdr, hdr->size);
616
617   if (hdr->status & STAT_ERROR) {
618     switch (hdr->status & STAT_MASK) {
619     case 0x0F: /* invalid disk change */
620       /* this error seems to fit the bill */
621       SetLastError(ERROR_NOT_SAME_DEVICE);
622       break;
623     default:
624       SetLastError((hdr->status & STAT_MASK) + 0x13);
625       break;
626     }
627   }
628 }
629
630 static int DOSDEV_IO(unsigned cmd, DWORD dev, DWORD buf, int buflen)
631 {
632   REQ_IO req;
633
634   req.hdr.size=sizeof(req);
635   req.hdr.unit=0; /* not dealing with block devices yet */
636   req.hdr.command=cmd;
637   req.hdr.status=STAT_BUSY;
638   req.media=0; /* not dealing with block devices yet */
639   req.buffer=buf;
640   req.count=buflen;
641   req.sector=0; /* block devices */
642   req.volume=0; /* block devices */
643
644   DOSDEV_DoReq(&req, dev);
645
646   return req.count;
647 }
648
649 int DOSDEV_Peek(DWORD dev, BYTE*data)
650 {
651   REQ_SAFEINPUT req;
652
653   req.hdr.size=sizeof(req);
654   req.hdr.unit=0; /* not dealing with block devices yet */
655   req.hdr.command=CMD_SAFEINPUT;
656   req.hdr.status=STAT_BUSY;
657   req.data=0;
658
659   DOSDEV_DoReq(&req, dev);
660
661   if (req.hdr.status & STAT_BUSY) return 0;
662
663   *data = req.data;
664   return 1;
665 }
666
667 int DOSDEV_Read(DWORD dev, DWORD buf, int buflen)
668 {
669   return DOSDEV_IO(CMD_INPUT, dev, buf, buflen);
670 }
671
672 int DOSDEV_Write(DWORD dev, DWORD buf, int buflen, int verify)
673 {
674   return DOSDEV_IO(verify?CMD_SAFEOUTPUT:CMD_OUTPUT, dev, buf, buflen);
675 }
676
677 int DOSDEV_IoctlRead(DWORD dev, DWORD buf, int buflen)
678 {
679   return DOSDEV_IO(CMD_INIOCTL, dev, buf, buflen);
680 }
681
682 int DOSDEV_IoctlWrite(DWORD dev, DWORD buf, int buflen)
683 {
684   return DOSDEV_IO(CMD_OUTIOCTL, dev, buf, buflen);
685 }
686
687 void DOSDEV_SetSharingRetry(WORD delay, WORD count)
688 {
689     DOSMEM_LOL()->sharing_retry_delay = delay;
690     if (count) DOSMEM_LOL()->sharing_retry_count = count;
691 }
692
693 SEGPTR DOSDEV_GetLOL(BOOL v86)
694 {
695     if (v86) return DOSMEM_LOL()->wine_rm_lol;
696     else return DOSMEM_LOL()->wine_pm_lol;
697 }