static int opost(unsigned char c, struct tty_struct *tty) { if (FULL(&tty->write_q)) return -1; if (O_OPOST(tty)) { switch (c) { case '\n': if (O_ONLRET(tty)) tty->column = 0; if (O_ONLCR(tty)) { if (LEFT(&tty->write_q) < 2) return -1; put_tty_queue('\r', &tty->write_q); tty->column = 0; } tty->canon_column = tty->column; break; case '\r': if (O_ONOCR(tty) && tty->column == 0) return 0; if (O_OCRNL(tty)) { c = '\n'; if (O_ONLRET(tty)) tty->canon_column = tty->column = 0; break; } tty->canon_column = tty->column = 0; break; case '\t': if (O_TABDLY(tty) == XTABS) { if (LEFT(&tty->write_q) < 8) return -1; do put_tty_queue(' ', &tty->write_q); while (++tty->column % 8); return 0; } tty->column = (tty->column | 7) + 1; break; case '\b': if (tty->column > 0) tty->column--; break; default: if (O_OLCUC(tty)) c = toupper(c); if (!iscntrl(c)) tty->column++; break; } } put_tty_queue(c, &tty->write_q); return 0; }
static inline void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) { if (I_IGNPAR(tty)) return; if (I_PARMRK(tty)) { put_tty_queue('\377', tty); put_tty_queue('\0', tty); put_tty_queue(c, tty); } else if (I_INPCK(tty)) put_tty_queue('\0', tty); else put_tty_queue(c, tty); wake_up_interruptible(&tty->read_wait); }
static inline void n_tty_receive_break(struct tty_struct *tty) { if (I_IGNBRK(tty)) return; if (I_BRKINT(tty)) { isig(SIGINT, tty, 1); return; } if (I_PARMRK(tty)) { put_tty_queue('\377', tty); put_tty_queue('\0', tty); } put_tty_queue('\0', tty); wake_up_interruptible(&tty->read_wait); }
static inline void pty_copy(struct tty_struct * from, struct tty_struct * to) { int c; while (!from->stopped && !EMPTY(&from->write_q)) { if (FULL(&to->read_q)) { if (FULL(&to->secondary)) break; TTY_READ_FLUSH(to); continue; } c = get_tty_queue(&from->write_q); put_tty_queue(c, &to->read_q); if (current->signal & ~current->blocked) break; } TTY_READ_FLUSH(to); wake_up(&from->write_q.proc_list); }
static inline void pty_copy(struct tty_struct * from, struct tty_struct * to) { int c; while (!from->stopped && !EMPTY(&from->write_q)) { if (FULL(&to->read_q)) { if (FULL(&to->secondary)) break; TTY_READ_FLUSH(to); continue; } c = get_tty_queue(&from->write_q); put_tty_queue(c, &to->read_q); if (current->signal & ~current->blocked) break; } TTY_READ_FLUSH(to); wake_up_interruptible(&from->write_q.proc_list); if (from->write_data_cnt) { set_bit(from->line, &tty_check_write); mark_bh(TTY_BH); } }
int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned int arg) { struct tty_struct * tty; struct tty_struct * other_tty; int pgrp; int dev; if (MAJOR(file->f_rdev) != 4) { printk("tty_ioctl: tty pseudo-major != 4\n"); return -EINVAL; } dev = MINOR(file->f_rdev); tty = TTY_TABLE(dev); if (!tty) return -EINVAL; if (IS_A_PTY(dev)) other_tty = tty_table[PTY_OTHER(dev)]; else other_tty = NULL; switch (cmd) { case TCGETS: return get_termios(tty,(struct termios *) arg); case TCSETSF: flush_input(tty); /* fallthrough */ case TCSETSW: wait_until_sent(tty); /* fallthrough */ case TCSETS: return set_termios(tty,(struct termios *) arg, dev); case TCGETA: return get_termio(tty,(struct termio *) arg); case TCSETAF: flush_input(tty); /* fallthrough */ case TCSETAW: wait_until_sent(tty); /* fallthrough */ case TCSETA: return set_termio(tty,(struct termio *) arg, dev); case TCXONC: switch (arg) { case TCOOFF: tty->stopped = 1; TTY_WRITE_FLUSH(tty); return 0; case TCOON: tty->stopped = 0; TTY_WRITE_FLUSH(tty); return 0; case TCIOFF: if (STOP_CHAR(tty)) put_tty_queue(STOP_CHAR(tty), &tty->write_q); return 0; case TCION: if (START_CHAR(tty)) put_tty_queue(START_CHAR(tty), &tty->write_q); return 0; } return -EINVAL; /* not implemented */ case TCFLSH: if (arg==0) flush_input(tty); else if (arg==1) flush_output(tty); else if (arg==2) { flush_input(tty); flush_output(tty); } else return -EINVAL; return 0; case TIOCEXCL: return -EINVAL; /* not implemented */ case TIOCNXCL: return -EINVAL; /* not implemented */ case TIOCSCTTY: return -EINVAL; /* set controlling term NI */ case TIOCGPGRP: verify_area((void *) arg,4); put_fs_long(tty->pgrp,(unsigned long *) arg); return 0; case TIOCSPGRP: if ((current->tty < 0) || (current->tty != dev) || (tty->session != current->session)) return -ENOTTY; pgrp=get_fs_long((unsigned long *) arg); if (pgrp < 0) return -EINVAL; if (session_of_pgrp(pgrp) != current->session) return -EPERM; tty->pgrp = pgrp; return 0; case TIOCOUTQ: verify_area((void *) arg,4); put_fs_long(CHARS(&tty->write_q), (unsigned long *) arg); return 0; case TIOCINQ: verify_area((void *) arg,4); if (L_CANON(tty) && !tty->secondary.data) put_fs_long(0, (unsigned long *) arg); else put_fs_long(CHARS(&tty->secondary), (unsigned long *) arg); return 0; case TIOCSTI: return -EINVAL; /* not implemented */ case TIOCGWINSZ: return get_window_size(tty,(struct winsize *) arg); case TIOCSWINSZ: if (IS_A_PTY_MASTER(dev)) set_window_size(other_tty,(struct winsize *) arg); return set_window_size(tty,(struct winsize *) arg); case TIOCGSOFTCAR: return -EINVAL; /* not implemented */ case TIOCSSOFTCAR: return -EINVAL; /* not implemented */ case TIOCLINUX: switch (get_fs_byte((char *)arg)) { case 0: return do_screendump(arg); case 1: return do_get_ps_info(arg); default: return -EINVAL; } case TIOCCONS: if (!IS_A_PTY(dev)) return -EINVAL; if (redirect) return -EBUSY; if (!suser()) return -EPERM; if (IS_A_PTY_MASTER(dev)) redirect = other_tty; else redirect = tty; return 0; case FIONBIO: if (arg) file->f_flags |= O_NONBLOCK; else file->f_flags &= ~O_NONBLOCK; return 0; case TIOCNOTTY: if (MINOR(file->f_rdev) != current->tty) return -EINVAL; current->tty = -1; if (current->leader) { if (tty->pgrp > 0) kill_pg(tty->pgrp, SIGHUP, 0); tty->pgrp = -1; tty->session = 0; } return 0; case TIOCPKT: { int on; if (!IS_A_PTY_MASTER(dev)) return (-EINVAL); verify_area ((unsigned long *)arg, sizeof (int)); on=get_fs_long ((unsigned long *)arg); if (on ) tty->packet = 1; else tty->packet = 0; return (0); } default: if (tty->ioctl) return (tty->ioctl)(tty, file, cmd, arg); else return -EINVAL; } }
static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { unsigned long flags; int parmrk; if (tty->raw) { put_tty_queue(c, tty); return; } if (I_ISTRIP(tty)) c &= 0x7f; if (I_IUCLC(tty) && L_IEXTEN(tty)) c = tolower(c); if (L_EXTPROC(tty)) { put_tty_queue(c, tty); return; } if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) && c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) { start_tty(tty); process_echoes(tty); } if (tty->closing) { if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); process_echoes(tty); } else if (c == STOP_CHAR(tty)) stop_tty(tty); } return; } if (!test_bit(c, tty->process_char_map) || tty->lnext) { tty->lnext = 0; parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { if (L_ECHO(tty)) process_output('\a', tty); return; } if (L_ECHO(tty)) { finish_erasing(tty); if (tty->canon_head == tty->read_head) echo_set_canon_col(tty); echo_char(c, tty); process_echoes(tty); } if (parmrk) put_tty_queue(c, tty); put_tty_queue(c, tty); return; } if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); process_echoes(tty); return; } if (c == STOP_CHAR(tty)) { stop_tty(tty); return; } } if (L_ISIG(tty)) { int signal; signal = SIGINT; if (c == INTR_CHAR(tty)) goto send_signal; signal = SIGQUIT; if (c == QUIT_CHAR(tty)) goto send_signal; signal = SIGTSTP; if (c == SUSP_CHAR(tty)) { send_signal: if (!L_NOFLSH(tty)) { n_tty_flush_buffer(tty); tty_driver_flush_buffer(tty); } if (I_IXON(tty)) start_tty(tty); if (L_ECHO(tty)) { echo_char(c, tty); process_echoes(tty); } if (tty->pgrp) kill_pgrp(tty->pgrp, signal, 1); return; } } if (c == '\r') { if (I_IGNCR(tty)) return; if (I_ICRNL(tty)) c = '\n'; } else if (c == '\n' && I_INLCR(tty)) c = '\r'; if (tty->icanon) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); process_echoes(tty); return; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { tty->lnext = 1; if (L_ECHO(tty)) { finish_erasing(tty); if (L_ECHOCTL(tty)) { echo_char_raw('^', tty); echo_char_raw('\b', tty); process_echoes(tty); } } return; } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { unsigned long tail = tty->canon_head; finish_erasing(tty); echo_char(c, tty); echo_char_raw('\n', tty); while (tail != tty->read_head) { echo_char(tty->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } process_echoes(tty); return; } if (c == '\n') { if (tty->read_cnt >= N_TTY_BUF_SIZE) { if (L_ECHO(tty)) process_output('\a', tty); return; } if (L_ECHO(tty) || L_ECHONL(tty)) { echo_char_raw('\n', tty); process_echoes(tty); } goto handle_newline; } if (c == EOF_CHAR(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE) return; if (tty->canon_head != tty->read_head) set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; } if ((c == EOL_CHAR(tty)) || (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { if (L_ECHO(tty)) process_output('\a', tty); return; } if (L_ECHO(tty)) { if (tty->canon_head == tty->read_head) echo_set_canon_col(tty); echo_char(c, tty); process_echoes(tty); } if (parmrk) put_tty_queue(c, tty); handle_newline: spin_lock_irqsave(&tty->read_lock, flags); set_bit(tty->read_head, tty->read_flags); put_tty_queue_nolock(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; spin_unlock_irqrestore(&tty->read_lock, flags); kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); return; } } parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { if (L_ECHO(tty)) process_output('\a', tty); return; } if (L_ECHO(tty)) { finish_erasing(tty); if (c == '\n') echo_char_raw('\n', tty); else { if (tty->canon_head == tty->read_head) echo_set_canon_col(tty); echo_char(c, tty); } process_echoes(tty); } if (parmrk) put_tty_queue(c, tty); put_tty_queue(c, tty); }
static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { if (tty->raw) { put_tty_queue(c, tty); return; } if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) { start_tty(tty); return; } if (I_ISTRIP(tty)) c &= 0x7f; if (I_IUCLC(tty) && L_IEXTEN(tty)) c=tolower(c); if (tty->closing) { if (I_IXON(tty)) { if (c == START_CHAR(tty)) start_tty(tty); else if (c == STOP_CHAR(tty)) stop_tty(tty); } return; } /* * If the previous character was LNEXT, or we know that this * character is not one of the characters that we'll have to * handle specially, do shortcut processing to speed things * up. */ if (!test_bit(c, &tty->process_char_map) || tty->lnext) { finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); /* beep if no space */ return; } /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; echo_char(c, tty); } if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); put_tty_queue(c, tty); return; } if (c == '\r') { if (I_IGNCR(tty)) return; if (I_ICRNL(tty)) c = '\n'; } else if (c == '\n' && I_INLCR(tty)) c = '\r'; if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); return; } if (c == STOP_CHAR(tty)) { stop_tty(tty); return; } } if (L_ISIG(tty)) { int signal; signal = SIGINT; if (c == INTR_CHAR(tty)) goto send_signal; signal = SIGQUIT; if (c == QUIT_CHAR(tty)) goto send_signal; signal = SIGTSTP; if (c == SUSP_CHAR(tty)) { send_signal: isig(signal, tty, 0); return; } } if (L_ICANON(tty)) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); return; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { tty->lnext = 1; if (L_ECHO(tty)) { finish_erasing(tty); if (L_ECHOCTL(tty)) { put_char('^', tty); put_char('\b', tty); } } return; } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { unsigned long tail = tty->canon_head; finish_erasing(tty); echo_char(c, tty); opost('\n', tty); while (tail != tty->read_head) { echo_char(tty->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } return; } if (c == '\n') { if (L_ECHO(tty) || L_ECHONL(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); return; } opost('\n', tty); } goto handle_newline; } if (c == EOF_CHAR(tty)) { if (tty->canon_head != tty->read_head) set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; } if ((c == EOL_CHAR(tty)) || (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { /* * XXX are EOL_CHAR and EOL2_CHAR echoed?!? */ if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); return; } /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; echo_char(c, tty); } /* * XXX does PARMRK doubling happen for * EOL_CHAR and EOL2_CHAR? */ if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); handle_newline: set_bit(tty->read_head, &tty->read_flags); put_tty_queue(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; if (tty->fasync) kill_fasync(tty->fasync, SIGIO); if (tty->read_wait) wake_up_interruptible(&tty->read_wait); return; } } finish_erasing(tty); if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); /* beep if no space */ return; } if (c == '\n') opost('\n', tty); else { /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; echo_char(c, tty); } } if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); put_tty_queue(c, tty); }
static void copy_to_cooked(struct tty_struct * tty) { int c, special_flag; unsigned long flags; if (!tty) { printk("copy_to_cooked: called with NULL tty\n"); return; } if (!tty->write) { printk("copy_to_cooked: tty %d has null write routine\n", tty->line); } while (1) { /* * Check to see how much room we have left in the * secondary queue. Send a throttle command or abort * if necessary. */ c = LEFT(&tty->secondary); if (tty->throttle && (c < SQ_THRESHOLD_LW) && !set_bit(TTY_SQ_THROTTLED, &tty->flags)) tty->throttle(tty, TTY_THROTTLE_SQ_FULL); if (c == 0) break; save_flags(flags); cli(); if (!EMPTY(&tty->read_q)) { c = tty->read_q.buf[tty->read_q.tail]; special_flag = clear_bit(tty->read_q.tail, &tty->readq_flags); INC(tty->read_q.tail); restore_flags(flags); } else { restore_flags(flags); break; } if (special_flag) { tty->char_error = c; continue; } if (tty->char_error) { if (tty->char_error == TTY_BREAK) { tty->char_error = 0; if (I_IGNBRK(tty)) continue; /* A break is handled by the lower levels. */ if (I_BRKINT(tty)) continue; if (I_PARMRK(tty)) { put_tty_queue('\377', &tty->secondary); put_tty_queue('\0', &tty->secondary); } put_tty_queue('\0', &tty->secondary); continue; } if (tty->char_error == TTY_OVERRUN) { tty->char_error = 0; printk("tty%d: input overrun\n", tty->line); continue; } /* Must be a parity or frame error */ tty->char_error = 0; if (I_IGNPAR(tty)) { continue; } if (I_PARMRK(tty)) { put_tty_queue('\377', &tty->secondary); put_tty_queue('\0', &tty->secondary); put_tty_queue(c, &tty->secondary); } else put_tty_queue('\0', &tty->secondary); continue; } if (I_ISTRIP(tty)) c &= 0x7f; if (!tty->lnext) { if (c == '\r') { if (I_IGNCR(tty)) continue; if (I_ICRNL(tty)) c = '\n'; } else if (c == '\n' && I_INLCR(tty)) c = '\r'; } if (I_IUCLC(tty) && L_IEXTEN(tty)) c=tolower(c); if (c == __DISABLED_CHAR) tty->lnext = 1; if (L_ICANON(tty) && !tty->lnext) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); continue; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { tty->lnext = 1; if (L_ECHO(tty)) { if (tty->erasing) { opost('/', tty); tty->erasing = 0; } if (L_ECHOCTL(tty)) { opost('^', tty); opost('\b', tty); } } continue; } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { unsigned long tail = tty->canon_head; if (tty->erasing) { opost('/', tty); tty->erasing = 0; } echo_char(c, tty); opost('\n', tty); while (tail != tty->secondary.head) { echo_char(tty->secondary.buf[tail], tty); INC(tail); } continue; } } if (I_IXON(tty) && !tty->lnext) { if ((tty->stopped && I_IXANY(tty) && L_IEXTEN(tty)) || c == START_CHAR(tty)) { start_tty(tty); continue; } if (c == STOP_CHAR(tty)) { stop_tty(tty); continue; } } if (L_ISIG(tty) && !tty->lnext) { if (c == INTR_CHAR(tty)) { isig(SIGINT, tty); continue; } if (c == QUIT_CHAR(tty)) { isig(SIGQUIT, tty); continue; } if (c == SUSP_CHAR(tty)) { if (!is_orphaned_pgrp(tty->pgrp)) isig(SIGTSTP, tty); continue; } } if (tty->erasing) { opost('/', tty); tty->erasing = 0; } if (c == '\n' && !tty->lnext) { if (L_ECHO(tty) || (L_ICANON(tty) && L_ECHONL(tty))) opost('\n', tty); } else if (L_ECHO(tty)) { /* Don't echo the EOF char in canonical mode. Sun handles this differently by echoing the char and then backspacing, but that's a hack. */ if (c != EOF_CHAR(tty) || !L_ICANON(tty) || tty->lnext) { /* Record the column of first canon char. */ if (tty->canon_head == tty->secondary.head) tty->canon_column = tty->column; echo_char(c, tty); } } if (I_PARMRK(tty) && c == (unsigned char) '\377' && (c != EOF_CHAR(tty) || !L_ICANON(tty) || tty->lnext)) put_tty_queue(c, &tty->secondary); if (L_ICANON(tty) && !tty->lnext && (c == '\n' || c == EOF_CHAR(tty) || c == EOL_CHAR(tty) || (c == EOL2_CHAR(tty) && L_IEXTEN(tty)))) { if (c == EOF_CHAR(tty)) c = __DISABLED_CHAR; set_bit(tty->secondary.head, &tty->secondary_flags); put_tty_queue(c, &tty->secondary); tty->canon_head = tty->secondary.head; tty->canon_data++; } else put_tty_queue(c, &tty->secondary); tty->lnext = 0; } if (!EMPTY(&tty->write_q)) TTY_WRITE_FLUSH(tty); if (L_ICANON(tty) ? tty->canon_data : !EMPTY(&tty->secondary)) wake_up_interruptible(&tty->secondary.proc_list); if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW) && clear_bit(TTY_RQ_THROTTLED, &tty->flags)) tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL); }
static void eraser(unsigned char c, struct tty_struct *tty) { enum { ERASE, WERASE, KILL } kill_type; int seen_alnums; if (tty->secondary.head == tty->canon_head) { /* opost('\a', tty); */ /* what do you think? */ return; } if (c == ERASE_CHAR(tty)) kill_type = ERASE; else if (c == WERASE_CHAR(tty)) kill_type = WERASE; else { if (!L_ECHO(tty)) { tty->secondary.head = tty->canon_head; return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty)) { tty->secondary.head = tty->canon_head; if (tty->erasing) { opost('/', tty); tty->erasing = 0; } echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ if (L_ECHOK(tty)) opost('\n', tty); return; } kill_type = KILL; } seen_alnums = 0; while (tty->secondary.head != tty->canon_head) { c = LAST(&tty->secondary); if (kill_type == WERASE) { /* Equivalent to BSD's ALTWERASE. */ if (isalnum(c) || c == '_') seen_alnums++; else if (seen_alnums) break; } DEC(tty->secondary.head); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!tty->erasing) { opost('\\', tty); tty->erasing = 1; } echo_char(c, tty); } else if (!L_ECHOE(tty)) { echo_char(ERASE_CHAR(tty), tty); } else if (c == '\t') { unsigned int col = tty->canon_column; unsigned long tail = tty->canon_head; /* Find the column of the last char. */ while (tail != tty->secondary.head) { c = tty->secondary.buf[tail]; if (c == '\t') col = (col | 7) + 1; else if (iscntrl(c)) { if (L_ECHOCTL(tty)) col += 2; } else col++; INC(tail); } /* Now backup to that column. */ while (tty->column > col) { /* Can't use opost here. */ put_tty_queue('\b', &tty->write_q); tty->column--; } } else { if (iscntrl(c) && L_ECHOCTL(tty)) { opost('\b', tty); opost(' ', tty); opost('\b', tty); } if (!iscntrl(c) || L_ECHOCTL(tty)) { opost('\b', tty); opost(' ', tty); opost('\b', tty); } } } if (kill_type == ERASE) break; } if (tty->erasing && tty->secondary.head == tty->canon_head) { opost('/', tty); tty->erasing = 0; } }
int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { struct tty_struct * tty; struct tty_struct * other_tty; struct tty_struct * termios_tty; pid_t pgrp; int dev; int termios_dev; int retval; if (MAJOR(file->f_rdev) != TTY_MAJOR) { printk("tty_ioctl: tty pseudo-major != TTY_MAJOR\n"); return -EINVAL; } dev = MINOR(file->f_rdev); tty = TTY_TABLE(dev); if (!tty) return -EINVAL; if (IS_A_PTY(dev)) other_tty = tty_table[PTY_OTHER(dev)]; else other_tty = NULL; if (IS_A_PTY_MASTER(dev)) { termios_tty = other_tty; termios_dev = PTY_OTHER(dev); } else { termios_tty = tty; termios_dev = dev; } switch (cmd) { case TCGETS: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (struct termios)); if (retval) return retval; memcpy_tofs((struct termios *) arg, termios_tty->termios, sizeof (struct termios)); return 0; case TCSETSF: case TCSETSW: case TCSETS: retval = check_change(termios_tty, termios_dev); if (retval) return retval; if (cmd == TCSETSF || cmd == TCSETSW) { if (cmd == TCSETSF) flush_input(termios_tty); wait_until_sent(termios_tty, 0); } return set_termios(termios_tty, (struct termios *) arg, termios_dev); case TCGETA: return get_termio(termios_tty,(struct termio *) arg); case TCSETAF: case TCSETAW: case TCSETA: retval = check_change(termios_tty, termios_dev); if (retval) return retval; if (cmd == TCSETAF || cmd == TCSETAW) { if (cmd == TCSETAF) flush_input(termios_tty); wait_until_sent(termios_tty, 0); } return set_termio(termios_tty, (struct termio *) arg, termios_dev); case TCXONC: retval = check_change(tty, dev); if (retval) return retval; switch (arg) { case TCOOFF: stop_tty(tty); break; case TCOON: start_tty(tty); break; case TCIOFF: if (STOP_CHAR(tty) != __DISABLED_CHAR) put_tty_queue(STOP_CHAR(tty), &tty->write_q); break; case TCION: if (START_CHAR(tty) != __DISABLED_CHAR) put_tty_queue(START_CHAR(tty), &tty->write_q); break; default: return -EINVAL; } return 0; case TCFLSH: retval = check_change(tty, dev); if (retval) return retval; switch (arg) { case TCIFLUSH: flush_input(tty); break; case TCIOFLUSH: flush_input(tty); /* fall through */ case TCOFLUSH: flush_output(tty); break; default: return -EINVAL; } return 0; case TIOCEXCL: set_bit(TTY_EXCLUSIVE, &tty->flags); return 0; case TIOCNXCL: clear_bit(TTY_EXCLUSIVE, &tty->flags); return 0; case TIOCSCTTY: if (current->leader && (current->session == tty->session)) return 0; /* * The process must be a session leader and * not have a controlling tty already. */ if (!current->leader || (current->tty >= 0)) return -EPERM; if (tty->session > 0) { /* * This tty is already the controlling * tty for another session group! */ if ((arg == 1) && suser()) { /* * Steal it away */ struct task_struct *p; for_each_task(p) if (p->tty == dev) p->tty = -1; } else return -EPERM; } current->tty = dev; tty->session = current->session; tty->pgrp = current->pgrp; return 0; case TIOCGPGRP: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (pid_t)); if (retval) return retval; if (current->tty != termios_dev) return -ENOTTY; put_fs_long(termios_tty->pgrp, (pid_t *) arg); return 0; case TIOCSPGRP: retval = check_change(termios_tty, termios_dev); if (retval) return retval; if ((current->tty < 0) || (current->tty != termios_dev) || (termios_tty->session != current->session)) return -ENOTTY; pgrp = get_fs_long((pid_t *) arg); if (pgrp < 0) return -EINVAL; if (session_of_pgrp(pgrp) != current->session) return -EPERM; termios_tty->pgrp = pgrp; return 0; case TIOCOUTQ: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (unsigned long)); if (retval) return retval; put_fs_long(CHARS(&tty->write_q), (unsigned long *) arg); return 0; case TIOCINQ: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (unsigned long)); if (retval) return retval; if (L_ICANON(tty)) put_fs_long(inq_canon(tty), (unsigned long *) arg); else put_fs_long(CHARS(&tty->secondary), (unsigned long *) arg); return 0; case TIOCSTI: if ((current->tty != dev) && !suser()) return -EPERM; retval = verify_area(VERIFY_READ, (void *) arg, 1); if (retval) return retval; put_tty_queue(get_fs_byte((char *) arg), &tty->read_q); TTY_READ_FLUSH(tty); return 0; case TIOCGWINSZ: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (struct winsize)); if (retval) return retval; memcpy_tofs((struct winsize *) arg, &tty->winsize, sizeof (struct winsize)); return 0; case TIOCSWINSZ: if (IS_A_PTY_MASTER(dev)) set_window_size(other_tty,(struct winsize *) arg); return set_window_size(tty,(struct winsize *) arg); case TIOCLINUX: switch (get_fs_byte((char *)arg)) { case 0: return do_screendump(arg); case 1: return do_get_ps_info(arg); #ifdef CONFIG_SELECTION case 2: return set_selection(arg); case 3: return paste_selection(tty); case 4: unblank_screen(); return 0; #endif /* CONFIG_SELECTION */ default: return -EINVAL; } case TIOCCONS: if (IS_A_CONSOLE(dev)) { if (!suser()) return -EPERM; redirect = NULL; return 0; } if (redirect) return -EBUSY; if (!suser()) return -EPERM; if (IS_A_PTY_MASTER(dev)) redirect = other_tty; else if (IS_A_PTY_SLAVE(dev)) redirect = tty; else return -ENOTTY; return 0; case FIONBIO: arg = get_fs_long((unsigned long *) arg); if (arg) file->f_flags |= O_NONBLOCK; else file->f_flags &= ~O_NONBLOCK; return 0; case TIOCNOTTY: if (current->tty != dev) return -ENOTTY; if (current->leader) disassociate_ctty(0); current->tty = -1; return 0; case TIOCGETD: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (unsigned long)); if (retval) return retval; put_fs_long(tty->disc, (unsigned long *) arg); return 0; case TIOCSETD: retval = check_change(tty, dev); if (retval) return retval; arg = get_fs_long((unsigned long *) arg); return tty_set_ldisc(tty, arg); case TIOCGLCKTRMIOS: arg = get_fs_long((unsigned long *) arg); retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (struct termios)); if (retval) return retval; memcpy_tofs((struct termios *) arg, &termios_locked[termios_dev], sizeof (struct termios)); return 0; case TIOCSLCKTRMIOS: if (!suser()) return -EPERM; arg = get_fs_long((unsigned long *) arg); memcpy_fromfs(&termios_locked[termios_dev], (struct termios *) arg, sizeof (struct termios)); return 0; case TIOCPKT: if (!IS_A_PTY_MASTER(dev)) return -ENOTTY; retval = verify_area(VERIFY_READ, (void *) arg, sizeof (unsigned long)); if (retval) return retval; if (get_fs_long(arg)) { if (!tty->packet) { tty->packet = 1; tty->link->ctrl_status = 0; } } else tty->packet = 0; return 0; case TCSBRK: case TCSBRKP: retval = check_change(tty, dev); if (retval) return retval; wait_until_sent(tty, 0); if (!tty->ioctl) return 0; tty->ioctl(tty, file, cmd, arg); return 0; default: if (tty->ioctl) { retval = (tty->ioctl)(tty, file, cmd, arg); if (retval != -EINVAL) return retval; } if (ldiscs[tty->disc].ioctl) { retval = (ldiscs[tty->disc].ioctl) (tty, file, cmd, arg); return retval; } return -EINVAL; }