/****************************************************************/ /* */ /* chario.c */ /* DOS-C */ /* */ /* Character device functions and device driver interface */ /* */ /* Copyright (c) 1994 */ /* Pasquale J. Villani */ /* All Rights Reserved */ /* */ /* This file is part of DOS-C. */ /* */ /* DOS-C is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version */ /* 2, or (at your option) any later version. */ /* */ /* DOS-C is distributed in the hope that it will be useful, but */ /* WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ /* the GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public */ /* License along with DOS-C; see the file COPYING. If not, */ /* write to the Free Software Foundation, 675 Mass Ave, */ /* Cambridge, MA 02139, USA. */ /* */ /****************************************************************/ #include "portab.h" #ifdef VERSION_STRINGS static BYTE *charioRcsId = "$Id$"; #endif #include "globals.h" STATIC int CharRequest(struct dhdr FAR **pdev, unsigned command) { struct dhdr FAR *dev = *pdev; CharReqHdr.r_command = command; CharReqHdr.r_unit = 0; CharReqHdr.r_status = 0; CharReqHdr.r_length = sizeof(request); execrh(&CharReqHdr, dev); if (CharReqHdr.r_status & S_ERROR) { for (;;) { switch (char_error(&CharReqHdr, dev)) { case ABORT: case FAIL: return DE_INVLDACC; case CONTINUE: CharReqHdr.r_count = 0; return 0; case RETRY: return 1; } } } return SUCCESS; } long BinaryCharIO(struct dhdr FAR **pdev, size_t n, void FAR * bp, unsigned command) { int err; do { CharReqHdr.r_count = n; CharReqHdr.r_trans = bp; err = CharRequest(pdev, command); } while (err == 1); return err == SUCCESS ? (long)CharReqHdr.r_count : err; } STATIC int CharIO(struct dhdr FAR **pdev, unsigned char ch, unsigned command) { int err = (int)BinaryCharIO(pdev, 1, &ch, command); if (err == 0) return 256; if (err < 0) return err; return ch; } /* STATE FUNCTIONS */ STATIC void CharCmd(struct dhdr FAR **pdev, unsigned command) { while (CharRequest(pdev, command) == 1); } STATIC int Busy(struct dhdr FAR **pdev) { CharCmd(pdev, C_ISTAT); return CharReqHdr.r_status & S_BUSY; } void con_flush(struct dhdr FAR **pdev) { CharCmd(pdev, C_IFLUSH); } /* if the sft is invalid, then we just monitor syscon */ struct dhdr FAR *sft_to_dev(sft FAR *s) { if (FP_OFF(s) == (size_t) -1) return syscon; if (s->sft_flags & SFT_FDEVICE) return s->sft_dev; return NULL; } int StdinBusy(void) { sft FAR *s = get_sft(STDIN); struct dhdr FAR *dev = sft_to_dev(s); if (dev) return Busy(&dev); return s->sft_posit >= s->sft_size; } /* get character from the console - this is how DOS gets CTL_C/CTL_S/CTL_P when outputting */ int ndread(struct dhdr FAR **pdev) { CharCmd(pdev, C_NDREAD); if (CharReqHdr.r_status & S_BUSY) return -1; return CharReqHdr.r_ndbyte; } /* OUTPUT FUNCTIONS */ #ifdef __WATCOMC__ void fast_put_char(char c); #pragma aux fast_put_char = "int 29h" parm[al] modify exact [bx] #else /* writes a character in raw mode using int29 for speed */ STATIC void fast_put_char(unsigned char chr) { #if defined(__TURBOC__) _AL = chr; __int__(0x29); #elif defined(I86) asm { mov al, byte ptr chr; int 0x29; } #endif } #endif void update_scr_pos(unsigned char c, unsigned char count) { unsigned char scrpos = scr_pos; if (c == CR) scrpos = 0; else if (c == BS) { if (scrpos > 0) scrpos--; } else if (c != LF && c != BELL) { scrpos += count; } scr_pos = scrpos; } STATIC int raw_get_char(struct dhdr FAR **pdev, BOOL check_break); /* writes a character in cooked mode; maybe with printer echo; handles TAB expansion */ STATIC int cooked_write_char(struct dhdr FAR **pdev, unsigned char c, unsigned char *fast_counter) { unsigned char count = 1; if (c == HT) { count = 8 - (scr_pos & 7); c = ' '; } update_scr_pos(c, count); do { /* if not fast then < 0x80; always check otherwise check every 32 characters */ if (*fast_counter <= 0x80 && check_handle_break(pdev) == CTL_S) /* Test for hold char and ctl_c */ raw_get_char(pdev, TRUE); *fast_counter += 1; *fast_counter &= 0x9f; if (PrinterEcho) DosWrite(STDPRN, 1, &c); if (*fast_counter & 0x80) { fast_put_char(c); } else { int err = CharIO(pdev, c, C_OUTPUT); if (err < 0) return err; } } while (--count != 0); return SUCCESS; } long cooked_write(struct dhdr FAR **pdev, size_t n, char FAR *bp) { size_t xfer = 0; unsigned char fast_counter; /* bit 7 means fastcon; low 5 bits count number of characters */ fast_counter = ((*pdev)->dh_attr & ATTR_FASTCON) << 3; for (xfer = 0; xfer < n; xfer++) { int err; unsigned char c = *bp++; if (c == CTL_Z) break; err = cooked_write_char(pdev, c, &fast_counter); if (err < 0) return err; } return xfer; } /* writes character for disk file or device */ void write_char(int c, int sft_idx) { unsigned char ch = (unsigned char)c; DosRWSft(sft_idx, 1, &ch, XFR_FORCE_WRITE); } void write_char_stdout(int c) { unsigned char count = 1; unsigned flags = get_sft(STDOUT)->sft_flags; /* ah=2, ah=9 should expand tabs even for raw devices and disk files */ if ((flags & (SFT_FDEVICE|SFT_FBINARY)) != SFT_FDEVICE) { if (c == HT) { count = 8 - (scr_pos & 7); c = ' '; } /* for raw CONOUT devices already updated in dosfns.c */ if ((flags & (SFT_FDEVICE|SFT_FCONOUT)) != (SFT_FDEVICE|SFT_FCONOUT)) update_scr_pos(c, count); } do { write_char(c, get_sft_idx(STDOUT)); } while (--count != 0); } #define iscntrl(c) ((unsigned char)(c) < ' ') /* this is for handling things like ^C, mostly used in echoed input */ STATIC int echo_char(int c, int sft_idx) { int out = c; if (iscntrl(c) && c != HT && c != LF && c != CR) { write_char('^', sft_idx); out += '@'; } write_char(out, sft_idx); return c; } STATIC void destr_bs(int sft_idx) { write_char(BS, sft_idx); write_char(' ', sft_idx); write_char(BS, sft_idx); } /* READ FUNCTIONS */ long cooked_read(struct dhdr FAR **pdev, size_t n, char FAR *bp) { unsigned xfer = 0; int c; while(n--) { c = raw_get_char(pdev, TRUE); if (c < 0) return c; if (c == 256) break; *bp++ = c; xfer++; if ((unsigned char)c == CTL_Z) break; } return xfer; } STATIC unsigned char read_char_sft_dev(int sft_in, int sft_out, struct dhdr FAR **pdev, BOOL check_break) { unsigned char c; if (*pdev) { FOREVER { if (ctrl_break_pressed()) { c = CTL_C; break; } if (!Busy(pdev)) { c = CharIO(pdev, 0, C_INPUT); break; } if (check_break && *pdev != syscon) check_handle_break(&syscon); /* the idle int is only safe if we're using the character stack */ if (user_r->AH < 0xd) DosIdle_int(); } } else DosRWSft(sft_in, 1, &c, XFR_READ); /* check for break or stop on sft_in, echo to sft_out */ if (check_break && (c == CTL_C || c == CTL_S)) { if (c == CTL_S) c = read_char_sft_dev(sft_in, sft_out, pdev, FALSE); if (c == CTL_C) handle_break(pdev, sft_out); /* DOS oddity: if you press ^S somekey ^C then ^C does not break */ c = read_char(sft_in, sft_out, FALSE); } return c; } STATIC int raw_get_char(struct dhdr FAR **pdev, BOOL check_break) { return read_char_sft_dev(-1, -1, pdev, check_break); } unsigned char read_char(int sft_in, int sft_out, BOOL check_break) { struct dhdr FAR *dev = sft_to_dev(idx_to_sft(sft_in)); return read_char_sft_dev(sft_in, sft_out, &dev, check_break); } STATIC unsigned char read_char_check_break(int sft_in, int sft_out) { return read_char(sft_in, sft_out, TRUE); } unsigned char read_char_stdin(BOOL check_break) { return read_char(get_sft_idx(STDIN), get_sft_idx(STDOUT), check_break); } /* reads a line (buffered, called by int21/ah=0ah, 3fh) */ void read_line(int sft_in, int sft_out, keyboard FAR * kp) { unsigned c; unsigned cu_pos = scr_pos; unsigned count = 0, stored_pos = 0; unsigned size = kp->kb_size, stored_size = kp->kb_count; BOOL insert = FALSE, first = TRUE; if (size == 0) return; /* the stored line is invalid unless it ends with a CR */ if (kp->kb_buf[stored_size] != CR) stored_size = 0; do { unsigned new_pos = stored_size; c = read_char_check_break(sft_in, sft_out); if (c == 0) c = (unsigned)read_char_check_break(sft_in, sft_out) << 8; switch (c) { case LF: /* show LF if it's not the first character. Never store it */ if (!first) { write_char(CR, sft_out); write_char(LF, sft_out); } break; case CTL_F: break; case RIGHT: case F1: if (stored_pos < stored_size && count < size - 1) local_buffer[count++] = echo_char(kp->kb_buf[stored_pos++], sft_out); break; case F2: case F4: /* insert/delete up to character c */ { unsigned char c2 = read_char_check_break(sft_in, sft_out); new_pos = stored_pos; if (c2 == 0) { read_char_check_break(sft_in, sft_out); } else { char FAR *sp = fmemchr(&kp->kb_buf[stored_pos], c2, stored_size - stored_pos); if (sp != NULL) new_pos = (FP_OFF(sp) - FP_OFF(&kp->kb_buf[stored_pos])) + 1; } } /* fall through */ case F3: if (c != F4) /* not delete */ { while (stored_pos < new_pos && count < size - 1) local_buffer[count++] = echo_char(kp->kb_buf[stored_pos++], sft_out); } stored_pos = new_pos; break; case F5: fmemcpy(kp->kb_buf, local_buffer, count); stored_size = count; write_char('@', sft_out); goto start_new_line; case INS: insert = !insert; break; case DEL: stored_pos++; break; case LEFT: case CTL_BS: case BS: if (count > 0) { unsigned new_pos; char c2 = local_buffer[--count]; if (c2 == HT) { unsigned i; new_pos = cu_pos; for (i = 0; i < count; i++) { if (local_buffer[i] == HT) new_pos = (new_pos + 8) & ~7; else if (iscntrl(local_buffer[i])) new_pos += 2; else new_pos++; } do destr_bs(sft_out); while (scr_pos > new_pos); } else { if (iscntrl(c2)) destr_bs(sft_out); destr_bs(sft_out); } } if (stored_pos > 0) stored_pos--; break; case ESC: write_char('\\', sft_out); start_new_line: write_char(CR, sft_out); write_char(LF, sft_out); for (count = 0; count < cu_pos; count++) write_char(' ', sft_out); count = 0; stored_pos = 0; insert = FALSE; break; case F6: c = CTL_Z; /* fall through */ default: if (count < size - 1 || c == CR) local_buffer[count++] = echo_char(c, sft_out); else write_char(BELL, sft_out); if (stored_pos < stored_size && !insert) stored_pos++; break; } first = FALSE; } while (c != CR); fmemcpy(kp->kb_buf, local_buffer, count); /* if local_buffer overflows into the CON default buffer we must invalidate it */ if (count > LINEBUFSIZECON) kb_buf.kb_size = 0; /* kb_count does not include the final CR */ kp->kb_count = count - 1; } /* called by handle func READ (int21/ah=3f) */ size_t read_line_handle(int sft_idx, size_t n, char FAR * bp) { size_t chars_left; if (inputptr == NULL) { /* can we reuse kb_buf or was it overwritten? */ if (kb_buf.kb_size != LINEBUFSIZECON) { kb_buf.kb_count = 0; kb_buf.kb_size = LINEBUFSIZECON; } read_line(sft_idx, sft_idx, &kb_buf); kb_buf.kb_buf[kb_buf.kb_count + 1] = echo_char(LF, sft_idx); inputptr = kb_buf.kb_buf; if (*inputptr == CTL_Z) { inputptr = NULL; return 0; } } chars_left = &kb_buf.kb_buf[kb_buf.kb_count + 2] - inputptr; if (n > chars_left) n = chars_left; fmemcpy(bp, inputptr, n); inputptr += n; if (n == chars_left) inputptr = NULL; return n; }