int zs_accept( int fd ) { zsocket_t* zs = zs_get_by_fd( fd ); if ( !zs || FL_ISSET( zs->general.status, ZS_STAT_CLOSED ) ) { errno = EBADF; return -1; } if ( zs->type != ZSOCK_LISTEN ) { errno = EINVAL; return -1; } if ( !FL_ISSET( zs->listen.status, ZS_STAT_READABLE ) ) { errno = EAGAIN; return -1; } if ( FD_ISSET( fd, &active_read_fd_set ) && FL_ISSET( zs->general.status, ZS_STAT_READABLE ) ) n_selectable--; FL_CLR( zs->listen.status, ZS_STAT_READABLE ); zs_update_fd_state( fd, NULL ); if ( zs->listen.accepted_errno ) { errno = zs->listen.accepted_errno; zs->listen.accepted_errno = 0; } else assert( zs->listen.accepted_fd >= 0 ); fd = zs->listen.accepted_fd; zs->listen.accepted_fd = -1; return fd; }
int zs_select() { fd_set read_fd_set = active_read_fd_set; fd_set write_fd_set = active_write_fd_set; // int rw_still_avail = 0; int fd = fd_hash_head( &active_hash ); while ( fd != -1 ) { zsocket_t* zs; if ( !( zs = zs_get_by_fd( fd ) ) ) continue; if ( FD_ISSET( fd, &read_fd_set ) && FD_ISSET( fd, &active_read_fd_set ) && FL_ISSET( zs->general.status, ZS_STAT_READABLE ) ) { if ( zs->type == ZSOCK_CONNECT ) zs->general.event_hdlr( fd, ZS_EVT_READ_READY ); else zs->general.event_hdlr( fd, ZS_EVT_ACCEPT_READY ); } if ( FD_ISSET( fd, &write_fd_set ) && FD_ISSET( fd, &active_write_fd_set ) && FL_ISSET( zs->general.status, ZS_STAT_WRITABLE ) ) { assert( zs->type == ZSOCK_CONNECT ); zs->general.event_hdlr( fd, ZS_EVT_WRITE_READY ); } /*if ( ( FD_ISSET( fd, &active_read_fd_set ) && FL_ISSET( zs->general.status, ZS_STAT_READABLE ) ) || ( FD_ISSET( fd, &active_write_fd_set ) && FL_ISSET( zs->general.status, ZS_STAT_WRITABLE ) ) ) rw_still_avail++;*/ fd = fd_hash_next( &active_hash, fd ); } return zs_need_select(); }
/* * ex_edit -- :e[dit][!] [+cmd] [file] * :ex[!] [+cmd] [file] * :vi[sual][!] [+cmd] [file] * * Edit a file; if none specified, re-edit the current file. The third * form of the command can only be executed while in vi mode. See the * hack in ex.c:ex_cmd(). * * !!! * Historic vi didn't permit the '+' command form without specifying * a file name as well. This seems unreasonable, so we support it * regardless. * * PUBLIC: int ex_edit __P((SCR *, EXCMD *)); */ int ex_edit(SCR *sp, EXCMD *cmdp) { FREF *frp; int attach, setalt; const char *np; size_t nlen; switch (cmdp->argc) { case 0: /* * If the name has been changed, we edit that file, not the * original name. If the user was editing a temporary file * (or wasn't editing any file), create another one. The * reason for not reusing temporary files is that there is * special exit processing of them, and reuse is tricky. */ frp = sp->frp; if (sp->ep == NULL || F_ISSET(frp, FR_TMPFILE)) { if ((frp = file_add(sp, NULL)) == NULL) return (1); attach = 0; } else attach = 1; setalt = 0; break; case 1: INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, np, nlen); if ((frp = file_add(sp, np)) == NULL) return (1); attach = 0; setalt = 1; set_alt_name(sp, np); break; default: abort(); } if (F_ISSET(cmdp, E_NEWSCREEN) || cmdp->cmd == &cmds[C_VSPLIT]) return (ex_N_edit(sp, cmdp, frp, attach)); /* * Check for modifications. * * !!! * Contrary to POSIX 1003.2-1992, autowrite did not affect :edit. */ if (file_m2(sp, FL_ISSET(cmdp->iflags, E_C_FORCE))) return (1); /* Switch files. */ if (file_init(sp, frp, NULL, (setalt ? FS_SETALT : 0) | (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0))) return (1); F_SET(sp, SC_FSWITCH); return (0); }
int daemon_detach( int flags ) { printf( " * starting daemon..." ); fflush( stdout ); void (*prev_sighandler)( int ); if ( ( prev_sighandler = signal( SIGHUP, empty_sighandler ) ) != SIG_DFL ) signal( SIGHUP, prev_sighandler ); if ( ( prev_sighandler = signal( SIGTERM, empty_sighandler ) ) != SIG_DFL ) signal( SIGTERM, prev_sighandler ); if ( ( prev_sighandler = signal( SIGINT, empty_sighandler ) ) != SIG_DFL ) signal( SIGINT, prev_sighandler ); if ( ( prev_sighandler = signal( SIGQUIT, empty_sighandler ) ) != SIG_DFL ) signal( SIGQUIT, prev_sighandler ); /* Our process ID and Session ID */ pid_t pid, sid; /* Fork off the parent process */ pid = fork(); if ( pid < 0 ) return 0; /* If we got a good PID, then we can exit the parent process. */ if ( pid > 0 ) exit( EXIT_SUCCESS ); /* Change the file mode mask */ umask( 0 ); /* Create a new SID for the child process */ sid = setsid( ); if ( sid < 0 ) return 0; /* Change the current working directory */ if ( !FL_ISSET( flags, D_NOCD ) && chdir( "/" ) < 0 ) return 0; if ( !FL_ISSET( flags, D_NOLOCKFILE ) ) { if ( !createlockfile( ) ) { printf( "failed: could not set lockfile.\n" ); return 0; } } is_daemon = true; printf( "started.\n" ); if ( !FL_ISSET( flags, D_KEEPSTDIO ) ) { daemon_redirect_stdio( ); } return 1; }
static void zs_accepter( int fd, void* udata ) { zsocket_t* zs = zs_get_by_fd( fd ); zsocket_t* zs_parent = zs_get_by_fd( zs->connect.parent_fd ); /* printf( "zs_accepter: %d: START\n", zs->general.sockfd ); ssize_t n; if ( zs->connect.ssl ) { n = SSL_accept( zs->connect.ssl ); if ( n < 0 ) { int err = SSL_get_error( zs->connect.ssl, n ); printf("zs_accepter: %d: SSL_accept error %d ( %d, %d )\n", zs->general.sockfd, err, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE ); switch ( err ) { case SSL_ERROR_WANT_READ: FL_SET( zs->connect.status, ZS_STAT_WANT_READ_FROM_ACCEPT ); return; case SSL_ERROR_WANT_WRITE: FL_SET( zs->connect.status, ZS_STAT_WANT_WRITE_FROM_ACCEPT ); return; } ERR_print_errors_fp( stderr ); zs_close( fd ); zs_parent->listen.accepted_fd = -1; zs_parent->listen.accepted_errno = EPROTO; } printf( "zs_accepter: %d: SSL_accept accepted!\n", zs->general.sockfd ); }*/ if ( FD_ISSET( zs_parent->general.sockfd, &active_read_fd_set ) && !FL_ISSET( zs_parent->general.status, ZS_STAT_READABLE ) ) n_selectable++; FL_SET( zs_parent->general.status, ZS_STAT_READABLE ); FL_CLR( zs_parent->general.status, ZS_STAT_ACCEPTING ); if ( zs_parent->listen.accepted_fd != -1 ) { if ( FD_ISSET( fd, &active_write_fd_set ) && !FL_ISSET( zs->general.status, ZS_STAT_WRITABLE ) ) n_selectable++; FL_SET( zs->connect.status, ZS_STAT_CONNECTED | ZS_STAT_WRITABLE ); if ( zs_is_ssl( fd ) ) FL_SET( zs->connect.status, ZS_STAT_ACCEPTING ); } // Must call this inside zs_accepter because the // parent zsocket's state update may not be called by // the caller on return from calling zs_accepter, // it may only call it for the child zs_update_fd_state( zs_parent->general.sockfd, NULL ); }
static void zs_connecter( int fd, void* udata ) { zsocket_t* zs = zs_get_by_fd( fd ); ssize_t n; if ( zs->connect.ssl ) { n = SSL_connect( zs->connect.ssl ); if ( n < 0 ) { int err = SSL_get_error( zs->connect.ssl, n ); switch ( err ) { case SSL_ERROR_WANT_READ: FL_SET( zs->connect.status, ZS_STAT_WANT_READ_FROM_CONNECT ); return; case SSL_ERROR_WANT_WRITE: FL_SET( zs->connect.status, ZS_STAT_WANT_WRITE_FROM_CONNECT ); return; } ERR_print_errors_fp( stderr ); zs->connect.read.rw_errno = EPROTO; zs->connect.write.rw_errno = EPROTO; } } if ( FD_ISSET( fd, &active_write_fd_set ) && !FL_ISSET( zs->general.status, ZS_STAT_WRITABLE ) ) n_selectable++; FL_SET( zs->connect.status, ZS_STAT_CONNECTED | ZS_STAT_WRITABLE ); FL_CLR( zs->general.status, ZS_STAT_CONNECTING ); }
/* * ex_delete: [line [,line]] d[elete] [buffer] [count] [flags] * * Delete lines from the file. * * PUBLIC: int ex_delete __P((SCR *, EXCMD *)); */ int ex_delete(SCR *sp, EXCMD *cmdp) { recno_t lno; NEEDFILE(sp, cmdp); /* * !!! * Historically, lines deleted in ex were not placed in the numeric * buffers. We follow historic practice so that we don't overwrite * vi buffers accidentally. */ if (cut(sp, FL_ISSET(cmdp->iflags, E_C_BUFFER) ? &cmdp->buffer : NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE)) return (1); /* Delete the lines. */ if (del(sp, &cmdp->addr1, &cmdp->addr2, 1)) return (1); /* Set the cursor to the line after the last line deleted. */ sp->lno = cmdp->addr1.lno; /* Or the last line in the file if deleted to the end of the file. */ if (db_last(sp, &lno)) return (1); if (sp->lno > lno) sp->lno = lno; return (0); }
/* * ex_tag_next -- * Switch context to the next TAG. * * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *)); */ int ex_tag_next(SCR *sp, EXCMD *cmdp) { EX_PRIVATE *exp; TAG *tp; TAGQ *tqp; char *np; size_t nlen; exp = EXP(sp); if ((tqp = TAILQ_FIRST(exp->tq)) == NULL) { tag_msg(sp, TAG_EMPTY, NULL); return (1); } if ((tp = TAILQ_NEXT(tqp->current, q)) == NULL) { msgq(sp, M_ERR, "282|Already at the last tag of this group"); return (1); } if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE))) return (1); tqp->current = tp; if (F_ISSET(tqp, TAG_CSCOPE)) (void)cscope_search(sp, tqp, tp); else (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); if (tqp->current->msg) { INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, np, nlen); msgq(sp, M_INFO, "%s", np); } return (0); }
/* * ex_tag_prev -- * Switch context to the next TAG. * * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *)); */ int ex_tag_prev(SCR *sp, EXCMD *cmdp) { EX_PRIVATE *exp; TAG *tp; TAGQ *tqp; const char *np; size_t nlen; exp = EXP(sp); if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) { tag_msg(sp, TAG_EMPTY, NULL); return (0); } if ((tp = tqp->current->q.cqe_prev) == (void *)&tqp->tagq) { msgq(sp, M_ERR, "255|Already at the first tag of this group"); return (1); } if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE))) return (1); tqp->current = tp; if (F_ISSET(tqp, TAG_CSCOPE)) (void)cscope_search(sp, tqp, tp); else (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); if (tqp->current->msg) { INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, np, nlen); msgq(sp, M_INFO, "%s", np); } return (0); }
/* * ex_map -- :map[!] [input] [replacement] * Map a key/string or display mapped keys. * * Historical note: * Historic vi maps were fairly bizarre, and likely to differ in * very subtle and strange ways from this implementation. Two * things worth noting are that vi would often hang or drop core * if the map was strange enough (ex: map X "xy$@x^V), or, simply * not work. One trick worth remembering is that if you put a * mark at the start of the map, e.g. map X mx"xy ...), or if you * put the map in a .exrc file, things would often work much better. * No clue why. * * PUBLIC: int ex_map __P((SCR *, EXCMD *)); */ int ex_map(SCR *sp, EXCMD *cmdp) { seq_t stype; CHAR_T *input, *p; stype = FL_ISSET(cmdp->iflags, E_C_FORCE) ? SEQ_INPUT : SEQ_COMMAND; switch (cmdp->argc) { case 0: if (seq_dump(sp, stype, 1) == 0) msgq(sp, M_INFO, stype == SEQ_INPUT ? "132|No input map entries" : "133|No command map entries"); return (0); case 2: input = cmdp->argv[0]->bp; break; default: abort(); } /* * If the mapped string is #[0-9]* (and wasn't quoted) then store the * function key mapping. If the screen specific routine has been set, * call it as well. Note, the SEQ_FUNCMAP type is persistent across * screen types, maybe the next screen type will get it right. */ if (input[0] == '#' && ISDIGIT((UCHAR_T)input[1])) { for (p = input + 2; ISDIGIT((UCHAR_T)*p); ++p); if (p[0] != '\0') goto nofunc; if (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len, cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_FUNCMAP | SEQ_USERDEF)) return (1); return (sp->gp->scr_fmap == NULL ? 0 : sp->gp->scr_fmap(sp, stype, input, cmdp->argv[0]->len, cmdp->argv[1]->bp, cmdp->argv[1]->len)); } /* Some single keys may not be remapped in command mode. */ nofunc: if (stype == SEQ_COMMAND && input[1] == '\0') switch (KEY_VAL(sp, input[0])) { case K_COLON: case K_ESCAPE: case K_NL: msgq(sp, M_ERR, "134|The %s character may not be remapped", KEY_NAME(sp, input[0])); return (1); default: break; } return (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len, cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_USERDEF)); }
int daemon_init( int flags ) { if ( is_daemon ) {// already daemonized printf( "failed: process already daemonized.\n" ); return 0; } pid_t pid; if ( ( !FL_ISSET( flags, D_NOLOCKFILE ) && !FL_ISSET( flags, D_NOLOCKCHECK ) ) && ( pid = readlockfile() ) ) {// lockfile previously set if ( kill( pid, 0 ) == 0 ) { // process running printf( "failed: daemon already running.\n" ); return false; } else { printf( "warning: previous daemon did not clean up lockfile on exit.\n" ); } } return 1; }
/* * ex_unmap -- (:unmap[!] key) * Unmap a key. * * PUBLIC: int ex_unmap __P((SCR *, EXCMD *)); */ int ex_unmap(SCR *sp, EXCMD *cmdp) { if (seq_delete(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len, FL_ISSET(cmdp->iflags, E_C_FORCE) ? SEQ_INPUT : SEQ_COMMAND)) { msgq_wstr(sp, M_INFO, cmdp->argv[0]->bp, "135|\"%s\" isn't currently mapped"); return (1); } return (0); }
void zs_set_write( int fd ) { zsocket_t* zs = zs_get_by_fd( fd ); assert( zs ); if ( !FD_ISSET( fd, &active_write_fd_set ) && FL_ISSET( zs->general.status, ZS_STAT_WRITABLE ) ) n_selectable++; FD_SET( fd, &active_write_fd_set ); fd_hash_add( &active_hash, fd ); zs_update_fd_state( fd, NULL ); }
static void zs_internal_read( int fd, void* udata ) { zsocket_t* zs = zs_get_by_fd( fd ); assert( zs->type == ZSOCK_CONNECT ); if ( FL_ISSET( zs->connect.status, ZS_STAT_CONNECTED ) ) { if ( !FL_ARESOMESET( zs->connect.status, ZS_STAT_WANT_READ_FROM_READ | ZS_STAT_WANT_READ_FROM_WRITE ) ) { FL_SET( zs->connect.status, ZS_STAT_READING ); FL_SET( zs->connect.status, ZS_STAT_WANT_READ_FROM_READ ); } if ( FL_ISSET( zs->connect.status, ZS_STAT_WANT_READ_FROM_READ ) ) { // clear write_from_write, if read_from_write is not set clear fd select flag FL_CLR( zs->connect.status, ZS_STAT_WANT_READ_FROM_READ ); zs_reader( fd, udata ); } if ( FL_ISSET( zs->connect.status, ZS_STAT_WANT_READ_FROM_WRITE ) ) { // clear write_from_read, if read_from_read is not set and read is not set clear fd select flag FL_CLR( zs->connect.status, ZS_STAT_WANT_READ_FROM_WRITE ); zs_writer( fd, udata ); // zs_writer sometimes will call zs_close if ( !zs_get_by_fd( fd ) ) return; } } else if ( FL_ISSET( zs->connect.status, ZS_STAT_CONNECTING ) ) { FL_CLR( zs->connect.status, ZS_STAT_WANT_READ_FROM_CONNECT ); zs_connecter( fd, udata ); } else { FL_CLR( zs->connect.status, ZS_STAT_WANT_READ_FROM_ACCEPT ); zs_accepter( fd, udata ); // zs_accpeter sometimes will call zclose // if this happens we must return before // before zs_update_fd_state tries to use it if ( !zs_get_by_fd( fd ) ) return; } zs_update_fd_state( fd, udata ); }
void zs_clr_write( int fd ) { zsocket_t* zs = zs_get_by_fd( fd ); assert( zs ); if ( FD_ISSET( fd, &active_write_fd_set ) && FL_ISSET( zs->general.status, ZS_STAT_WRITABLE ) ) n_selectable--; FD_CLR( fd, &active_write_fd_set ); if ( !FD_ISSET( fd, &active_read_fd_set ) && !FD_ISSET( fd, &active_write_fd_set ) ) fd_hash_remove( &active_hash, fd ); if ( zs_get_by_fd( fd ) ) zs_update_fd_state( fd, NULL ); }
ssize_t zs_write( int fd, const void* buf, size_t nbyte ) { zsocket_t* zs = zs_get_by_fd( fd ); if ( !zs || zs->type != ZSOCK_CONNECT || FL_ISSET( zs->general.status, ZS_STAT_CLOSED ) ) { errno = EBADF; fprintf( stderr, "%d: %d, bad\n", fd, zs->type ); return -1; } if ( !FL_ISSET( zs->connect.status, ZS_STAT_WRITABLE ) ) { errno = EAGAIN; return -1; } assert( !zs->connect.write.buffer ); if ( FD_ISSET( fd, &active_write_fd_set ) && FL_ISSET( zs->general.status, ZS_STAT_WRITABLE ) ) n_selectable--; FL_CLR( zs->connect.status, ZS_STAT_WRITABLE ); if ( zs->connect.write.rw_errno ) { errno = zs->connect.write.rw_errno || zs->connect.read.rw_errno; zs->connect.write.rw_errno = 0; nbyte = -1; } else { zs->connect.write.buffer = malloc( nbyte ); memcpy( zs->connect.write.buffer, buf, nbyte ); zs->connect.write.size = nbyte; zs->connect.write.used = nbyte; zs->connect.write.pos = 0; FL_SET( zs->connect.status, ZS_STAT_WRITING | ZS_STAT_WANT_WRITE_FROM_WRITE ); } zs_update_fd_state( fd, NULL ); return nbyte; }
int zs_close( int fd ) { zsocket_t* p = zsockets[ fd ]; if ( !p ) { errno = EBADF; return -1; } if ( p->type == ZSOCK_LISTEN ) { p->listen.n_open--; if ( p->listen.n_open > 0 ) return 0; } else { if ( FL_ISSET( p->general.status, ZS_STAT_WRITING ) ) { zs_clr_read( fd ); zs_clr_write( fd ); FL_SET( p->general.status, ZS_STAT_CLOSED ); return 0; } } zfd_clr( fd, ZFD_R ); zfd_clr( fd, ZFD_W ); zs_clr_read( fd ); zs_clr_write( fd ); zsockets[ fd ] = NULL; if ( p->type == ZSOCK_LISTEN ) { close( p->listen.sockfd ); if ( p->listen.ssl_ctx ) SSL_CTX_free( p->listen.ssl_ctx ); } else if ( p->type == ZSOCK_CONNECT ) { // Closing if ( p->connect.ssl ) SSL_shutdown( p->connect.ssl ); close( p->connect.sockfd ); // Freeing free( p->connect.read.buffer ); if ( p->connect.ssl ) SSL_free( p->connect.ssl ); if ( p->connect.write.buffer ) free( p->connect.write.buffer ); } free( p ); return 0; }
/* * ex_tag_push -- ^] * :tag[!] [string] * * Enter a new TAGQ context based on a ctag string. * * PUBLIC: int ex_tag_push __P((SCR *, EXCMD *)); */ int ex_tag_push(SCR *sp, EXCMD *cmdp) { EX_PRIVATE *exp; TAGQ *tqp; unsigned long tl; exp = EXP(sp); switch (cmdp->argc) { case 1: if (exp->tag_last != NULL) free(exp->tag_last); if ((exp->tag_last = v_wstrdup(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len)) == NULL) { msgq(sp, M_SYSERR, NULL); return (1); } /* Taglength may limit the number of characters. */ if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && STRLEN(exp->tag_last) > tl) exp->tag_last[tl] = '\0'; break; case 0: if (exp->tag_last == NULL) { msgq(sp, M_ERR, "158|No previous tag entered"); return (1); } break; default: abort(); } /* Get the tag information. */ #ifdef GTAGS if (O_ISSET(sp, O_GTAGSMODE)) { if ((tqp = gtag_slist(sp, exp->tag_last, F_ISSET(cmdp, E_REFERENCE))) == NULL) return (1); } else #endif if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL) return (1); if (tagq_push(sp, tqp, F_ISSET(cmdp, E_NEWSCREEN), FL_ISSET(cmdp->iflags, E_C_FORCE))) return 1; return 0; }
/* * ex_yank -- :[line [,line]] ya[nk] [buffer] [count] * Yank the lines into a buffer. * * PUBLIC: int ex_yank __P((SCR *, EXCMD *)); */ int ex_yank(SCR *sp, EXCMD *cmdp) { NEEDFILE(sp, cmdp); /* * !!! * Historically, yanking lines in ex didn't count toward the * number-of-lines-yanked report. */ return (cut(sp, FL_ISSET(cmdp->iflags, E_C_BUFFER) ? &cmdp->buffer : NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE)); }
/* * ex_quit -- :quit[!] * Quit. * * PUBLIC: int ex_quit __P((SCR *, EXCMD *)); */ int ex_quit(SCR *sp, EXCMD *cmdp) { int force; force = FL_ISSET(cmdp->iflags, E_C_FORCE); /* Check for file modifications, or more files to edit. */ if (file_m2(sp, force) || ex_ncheck(sp, force)) return (1); F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT); return (0); }
/* * ex_stop -- :stop[!] * :suspend[!] * Suspend execution. * * PUBLIC: int ex_stop __P((SCR *, EXCMD *)); */ int ex_stop(SCR *sp, EXCMD *cmdp) { int allowed; /* For some strange reason, the force flag turns off autowrite. */ if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && file_aw(sp, FS_ALL)) return (1); if (sp->gp->scr_suspend(sp, &allowed)) return (1); if (!allowed) ex_emsg(sp, NULL, EXM_NOSUSPEND); return (0); }
/* * ex_put -- [line] pu[t] [buffer] * Append a cut buffer into the file. * * PUBLIC: int ex_put __P((SCR *, EXCMD *)); */ int ex_put(SCR *sp, EXCMD *cmdp) { MARK m; NEEDFILE(sp, cmdp); m.lno = sp->lno; m.cno = sp->cno; if (put(sp, NULL, FL_ISSET(cmdp->iflags, E_C_BUFFER) ? &cmdp->buffer : NULL, &cmdp->addr1, &m, 1)) return (1); sp->lno = m.lno; sp->cno = m.cno; return (0); }
/* * ex_wq -- :wq[!] [>>] [file] * Write to a file and quit. * * PUBLIC: int ex_wq __P((SCR *, EXCMD *)); */ int ex_wq(SCR *sp, EXCMD *cmdp) { int force; if (exwr(sp, cmdp, WQ)) return (1); if (file_m3(sp, 0)) return (1); force = FL_ISSET(cmdp->iflags, E_C_FORCE); if (ex_ncheck(sp, force)) return (1); F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT); return (0); }
/* * ex_xit -- :x[it]! [file] * Write out any modifications and quit. * * PUBLIC: int ex_xit __P((SCR *, EXCMD *)); */ int ex_xit(SCR *sp, EXCMD *cmdp) { int force; NEEDFILE(sp, cmdp); if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT)) return (1); if (file_m3(sp, 0)) return (1); force = FL_ISSET(cmdp->iflags, E_C_FORCE); if (ex_ncheck(sp, force)) return (1); F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT); return (0); }
/* * ex_resize -- :resize [+-]rows * Change the screen size. * * PUBLIC: int ex_resize __P((SCR *, EXCMD *)); */ int ex_resize(SCR *sp, EXCMD *cmdp) { adj_t adj; switch (FL_ISSET(cmdp->iflags, E_C_COUNT | E_C_COUNT_NEG | E_C_COUNT_POS)) { case E_C_COUNT: adj = A_SET; break; case E_C_COUNT | E_C_COUNT_NEG: adj = A_DECREASE; break; case E_C_COUNT | E_C_COUNT_POS: adj = A_INCREASE; break; default: ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); return (1); } return (vs_resize(sp, cmdp->count, adj)); }
/* * v_esearch -- <dialog box> * Search command from the screen. * * PUBLIC: int v_esearch __P((SCR *, VICMD *)); */ int v_esearch(SCR *sp, VICMD *vp) { int flags; LF_INIT(SEARCH_NOOPT); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_EXT)) LF_SET(SEARCH_EXTEND); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_IC)) LF_SET(SEARCH_IC); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_ICL)) LF_SET(SEARCH_ICL); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_INCR)) LF_SET(SEARCH_INCR); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_LIT)) LF_SET(SEARCH_LITERAL); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_WR)) LF_SET(SEARCH_WRAP); return (v_search(sp, vp, vp->ev.e_csp, vp->ev.e_len, flags, FL_ISSET(vp->ev.e_flags, VI_SEARCH_REV) ? BACKWARD : FORWARD)); }
/* * ex_txt -- * Get lines from the terminal for ex. * * PUBLIC: int ex_txt __P((SCR *, TEXTH *, ARG_CHAR_T, u_int32_t)); */ int ex_txt(SCR *sp, TEXTH *tiqh, ARG_CHAR_T prompt, u_int32_t flags) { EVENT ev; GS *gp; TEXT ait, *ntp, *tp; carat_t carat_st; size_t cnt; int rval; rval = 0; /* * Get a TEXT structure with some initial buffer space, reusing the * last one if it's big enough. (All TEXT bookkeeping fields default * to 0 -- text_init() handles this.) */ if (tiqh->cqh_first != (void *)tiqh) { tp = tiqh->cqh_first; if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) { text_lfree(tiqh); goto newtp; } tp->len = 0; } else { newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) goto err; CIRCLEQ_INSERT_HEAD(tiqh, tp, q); } /* Set the starting line number. */ tp->lno = sp->lno + 1; /* * If it's a terminal, set up autoindent, put out the prompt, and * set it up so we know we were suspended. Otherwise, turn off * the autoindent flag, as that requires less special casing below. * * XXX * Historic practice is that ^Z suspended command mode (but, because * it ran in cooked mode, it was unaffected by the autowrite option.) * On restart, any "current" input was discarded, whether in insert * mode or not, and ex was in command mode. This code matches historic * practice, but not 'cause it's easier. */ gp = sp->gp; if (F_ISSET(gp, G_SCRIPTED)) LF_CLR(TXT_AUTOINDENT); else { if (LF_ISSET(TXT_AUTOINDENT)) { LF_SET(TXT_EOFCHAR); if (v_txt_auto(sp, sp->lno, NULL, 0, tp)) goto err; } txt_prompt(sp, tp, prompt, flags); } for (carat_st = C_NOTSET;;) { if (v_event_get(sp, &ev, 0, 0)) goto err; /* Deal with all non-character events. */ switch (ev.e_event) { case E_CHARACTER: break; case E_ERR: goto err; case E_REPAINT: case E_WRESIZE: continue; case E_EOF: rval = 1; /* FALLTHROUGH */ case E_INTERRUPT: /* * Handle EOF/SIGINT events by discarding partially * entered text and returning. EOF returns failure, * E_INTERRUPT returns success. */ goto notlast; default: v_event_err(sp, &ev); goto notlast; } /* * Deal with character events. * * Check to see if the character fits into the input buffer. * (Use tp->len, ignore overwrite and non-printable chars.) */ BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1); switch (ev.e_value) { case K_CR: /* * !!! * Historically, <carriage-return>'s in the command * weren't special, so the ex parser would return an * unknown command error message. However, if they * terminated the command if they were in a map. I'm * pretty sure this still isn't right, but it handles * what I've seen so far. */ if (!FL_ISSET(ev.e_flags, CH_MAPPED)) goto ins_ch; /* FALLTHROUGH */ case K_NL: /* * '\' can escape <carriage-return>/<newline>. We * don't discard the backslash because we need it * to get the <newline> through the ex parser. */ if (LF_ISSET(TXT_BACKSLASH) && tp->len != 0 && tp->lb[tp->len - 1] == '\\') goto ins_ch; /* * CR returns from the ex command line. * * XXX * Terminate with a nul, needed by filter. */ if (LF_ISSET(TXT_CR)) { tp->lb[tp->len] = '\0'; goto done; } /* * '.' may terminate text input mode; free the current * TEXT. */ if (LF_ISSET(TXT_DOTTERM) && tp->len == tp->ai + 1 && tp->lb[tp->len - 1] == '.') { notlast: CIRCLEQ_REMOVE(tiqh, tp, q); text_free(tp); goto done; } /* Set up bookkeeping for the new line. */ if ((ntp = text_init(sp, NULL, 0, 32)) == NULL) goto err; ntp->lno = tp->lno + 1; /* * Reset the autoindent line value. 0^D keeps the ai * line from changing, ^D changes the level, even if * there were no characters in the old line. Note, if * using the current tp structure, use the cursor as * the length, the autoindent characters may have been * erased. */ if (LF_ISSET(TXT_AUTOINDENT)) { if (carat_st == C_NOCHANGE) { if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp)) goto err; free(ait.lb); } else if (v_txt_auto(sp, OOBLNO, tp, tp->len, ntp)) goto err; carat_st = C_NOTSET; } txt_prompt(sp, ntp, prompt, flags); /* * Swap old and new TEXT's, and insert the new TEXT * into the queue. */ tp = ntp; CIRCLEQ_INSERT_TAIL(tiqh, tp, q); break; case K_CARAT: /* Delete autoindent chars. */ if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) carat_st = C_CARATSET; goto ins_ch; case K_ZERO: /* Delete autoindent chars. */ if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) carat_st = C_ZEROSET; goto ins_ch; case K_CNTRLD: /* Delete autoindent char. */ /* * !!! * Historically, the ^D command took (but then ignored) * a count. For simplicity, we don't return it unless * it's the first character entered. The check for len * equal to 0 is okay, TXT_AUTOINDENT won't be set. */ if (LF_ISSET(TXT_CNTRLD)) { for (cnt = 0; cnt < tp->len; ++cnt) if (!isblank(tp->lb[cnt])) break; if (cnt == tp->len) { tp->len = 1; tp->lb[0] = ev.e_c; tp->lb[1] = '\0'; /* * Put out a line separator, in case * the command fails. */ (void)putchar('\n'); goto done; } } /* * POSIX 1003.1b-1993, paragraph 7.1.1.9, states that * the EOF characters are discarded if there are other * characters to process in the line, i.e. if the EOF * is not the first character in the line. For this * reason, historic ex discarded the EOF characters, * even if occurring in the middle of the input line. * We match that historic practice. * * !!! * The test for discarding in the middle of the line is * done in the switch, because the CARAT forms are N+1, * not N. * * !!! * There's considerable magic to make the terminal code * return the EOF character at all. See that code for * details. */ if (!LF_ISSET(TXT_AUTOINDENT) || tp->len == 0) continue; switch (carat_st) { case C_CARATSET: /* ^^D */ if (tp->len > tp->ai + 1) continue; /* Save the ai string for later. */ ait.lb = NULL; ait.lb_len = 0; BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai); MEMCPYW(ait.lb, tp->lb, tp->ai); ait.ai = ait.len = tp->ai; carat_st = C_NOCHANGE; goto leftmargin; case C_ZEROSET: /* 0^D */ if (tp->len > tp->ai + 1) continue; carat_st = C_NOTSET; leftmargin: (void)gp->scr_ex_adjust(sp, EX_TERM_CE); tp->ai = tp->len = 0; break; case C_NOTSET: /* ^D */ if (tp->len > tp->ai) continue; if (txt_dent(sp, tp)) goto err; break; default: abort(); } /* Clear and redisplay the line. */ (void)gp->scr_ex_adjust(sp, EX_TERM_CE); txt_prompt(sp, tp, prompt, flags); break; default: /* * See the TXT_BEAUTIFY comment in vi/v_txt_ev.c. * * Silently eliminate any iscntrl() character that was * not already handled specially, except for <tab> and * <ff>. */ ins_ch: if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(ev.e_c) && ev.e_value != K_FORMFEED && ev.e_value != K_TAB) break; tp->lb[tp->len++] = ev.e_c; break; } } /* NOTREACHED */ done: return (rval); err: alloc_err: return (1); }
/* * ex_z -- :[line] z [^-.+=] [count] [flags] * Adjust window. * * PUBLIC: int ex_z __P((SCR *, EXCMD *)); */ int ex_z(SCR *sp, EXCMD *cmdp) { MARK abs; recno_t cnt, equals, lno; int eofcheck; NEEDFILE(sp, cmdp); /* * !!! * If no count specified, use either two times the size of the * scrolling region, or the size of the window option. POSIX * 1003.2 claims that the latter is correct, but historic ex/vi * documentation and practice appear to use the scrolling region. * I'm using the window size as it means that the entire screen * is used instead of losing a line to roundoff. Note, we drop * a line from the cnt if using the window size to leave room for * the next ex prompt. */ if (FL_ISSET(cmdp->iflags, E_C_COUNT)) cnt = cmdp->count; else #ifdef HISTORICAL_PRACTICE cnt = O_VAL(sp, O_SCROLL) * 2; #else cnt = O_VAL(sp, O_WINDOW) - 1; #endif equals = 0; eofcheck = 0; lno = cmdp->addr1.lno; switch (FL_ISSET(cmdp->iflags, E_C_CARAT | E_C_DASH | E_C_DOT | E_C_EQUAL | E_C_PLUS)) { case E_C_CARAT: /* Display cnt * 2 before the line. */ eofcheck = 1; if (lno > cnt * 2) cmdp->addr1.lno = (lno - cnt * 2) + 1; else cmdp->addr1.lno = 1; cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1; break; case E_C_DASH: /* Line goes at the bottom of the screen. */ cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1; cmdp->addr2.lno = lno; break; case E_C_DOT: /* Line goes in the middle of the screen. */ /* * !!! * Historically, the "middleness" of the line overrode the * count, so that "3z.19" or "3z.20" would display the first * 12 lines of the file, i.e. (N - 1) / 2 lines before and * after the specified line. */ eofcheck = 1; cnt = (cnt - 1) / 2; cmdp->addr1.lno = lno > cnt ? lno - cnt : 1; cmdp->addr2.lno = lno + cnt; /* * !!! * Historically, z. set the absolute cursor mark. */ abs.lno = sp->lno; abs.cno = sp->cno; (void)mark_set(sp, ABSMARK1, &abs, 1); break; case E_C_EQUAL: /* Center with hyphens. */ /* * !!! * Strangeness. The '=' flag is like the '.' flag (see the * above comment, it applies here as well) but with a special * little hack. Print out lines of hyphens before and after * the specified line. Additionally, the cursor remains set * on that line. */ eofcheck = 1; cnt = (cnt - 1) / 2; cmdp->addr1.lno = lno > cnt ? lno - cnt : 1; cmdp->addr2.lno = lno - 1; if (ex_pr(sp, cmdp)) return (1); (void)ex_puts(sp, "----------------------------------------\n"); cmdp->addr2.lno = cmdp->addr1.lno = equals = lno; if (ex_pr(sp, cmdp)) return (1); (void)ex_puts(sp, "----------------------------------------\n"); cmdp->addr1.lno = lno + 1; cmdp->addr2.lno = (lno + cnt) - 1; break; default: /* If no line specified, move to the next one. */ if (F_ISSET(cmdp, E_ADDR_DEF)) ++lno; /* FALLTHROUGH */ case E_C_PLUS: /* Line goes at the top of the screen. */ eofcheck = 1; cmdp->addr1.lno = lno; cmdp->addr2.lno = (lno + cnt) - 1; break; } if (eofcheck) { if (db_last(sp, &lno)) return (1); if (cmdp->addr2.lno > lno) cmdp->addr2.lno = lno; } if (ex_pr(sp, cmdp)) return (1); if (equals) sp->lno = equals; return (0); }
/* * ex_next -- :next [+cmd] [files] * Edit the next file, optionally setting the list of files. * * !!! * The :next command behaved differently from the :rewind command in * historic vi. See nvi/docs/autowrite for details, but the basic * idea was that it ignored the force flag if the autowrite flag was * set. This implementation handles them all identically. * * PUBLIC: int ex_next __P((SCR *, EXCMD *)); */ int ex_next(SCR *sp, EXCMD *cmdp) { ARGS **argv; FREF *frp; int noargs; char **ap; const CHAR_T *wp; size_t wlen; const char *np; size_t nlen; /* Check for file to move to. */ if (cmdp->argc == 0 && (sp->cargv == NULL || sp->cargv[1] == NULL)) { msgq(sp, M_ERR, "111|No more files to edit"); return (1); } if (F_ISSET(cmdp, E_NEWSCREEN)) { /* By default, edit the next file in the old argument list. */ if (cmdp->argc == 0) { CHAR2INT(sp, sp->cargv[1], strlen(sp->cargv[1]) + 1, wp, wlen); if (argv_exp0(sp, cmdp, wp, wlen - 1)) return (1); return (ex_edit(sp, cmdp)); } return (ex_N_next(sp, cmdp)); } /* Check modification. */ if (file_m1(sp, FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE)) return (1); /* Any arguments are a replacement file list. */ if (cmdp->argc) { /* Free the current list. */ if (!F_ISSET(sp, SC_ARGNOFREE) && sp->argv != NULL) { for (ap = sp->argv; *ap != NULL; ++ap) free(*ap); free(sp->argv); } F_CLR(sp, SC_ARGNOFREE | SC_ARGRECOVER); sp->cargv = NULL; /* Create a new list. */ CALLOC_RET(sp, sp->argv, char **, cmdp->argc + 1, sizeof(char *)); for (ap = sp->argv, argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv) { INT2CHAR(sp, argv[0]->bp, argv[0]->len, np, nlen); if ((*ap = v_strdup(sp, np, nlen)) == NULL) return (1); } *ap = NULL; /* Switch to the first file. */ sp->cargv = sp->argv; if ((frp = file_add(sp, *sp->cargv)) == NULL) return (1); noargs = 0; /* Display a file count with the welcome message. */ F_SET(sp, SC_STATUS_CNT); } else { if ((frp = file_add(sp, sp->cargv[1])) == NULL)
/* * ex_aci -- * Append, change, insert in ex. */ static int ex_aci(SCR *sp, EXCMD *cmdp, enum which cmd) { CHAR_T *p, *t; GS *gp; TEXT *tp; TEXTH tiq; db_recno_t cnt, lno; size_t len; u_int32_t flags; int need_newline; gp = sp->gp; NEEDFILE(sp, cmdp); /* * If doing a change, replace lines for as long as possible. Then, * append more lines or delete remaining lines. Changes to an empty * file are appends, inserts are the same as appends to the previous * line. * * !!! * Set the address to which we'll append. We set sp->lno to this * address as well so that autoindent works correctly when get text * from the user. */ lno = cmdp->addr1.lno; sp->lno = lno; if ((cmd == CHANGE || cmd == INSERT) && lno != 0) --lno; /* * !!! * If the file isn't empty, cut changes into the unnamed buffer. */ if (cmd == CHANGE && cmdp->addr1.lno != 0 && (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) || del(sp, &cmdp->addr1, &cmdp->addr2, 1))) return (1); /* * !!! * Anything that was left after the command separator becomes part * of the inserted text. Apparently, it was common usage to enter: * * :g/pattern/append|stuff1 * * and append the line of text "stuff1" to the lines containing the * pattern. It was also historically legal to enter: * * :append|stuff1 * stuff2 * . * * and the text on the ex command line would be appended as well as * the text inserted after it. There was an historic bug however, * that the user had to enter *two* terminating lines (the '.' lines) * to terminate text input mode, in this case. This whole thing * could be taken too far, however. Entering: * * :append|stuff1\ * stuff2 * stuff3 * . * * i.e. mixing and matching the forms confused the historic vi, and, * not only did it take two terminating lines to terminate text input * mode, but the trailing backslashes were retained on the input. We * match historic practice except that we discard the backslashes. * * Input lines specified on the ex command line lines are separated by * <newline>s. If there is a trailing delimiter an empty line was * inserted. There may also be a leading delimiter, which is ignored * unless it's also a trailing delimiter. It is possible to encounter * a termination line, i.e. a single '.', in a global command, but not * necessary if the text insert command was the last of the global * commands. */ if (cmdp->save_cmdlen != 0) { for (p = cmdp->save_cmd, len = cmdp->save_cmdlen; len > 0; p = t) { for (t = p; len > 0 && t[0] != '\n'; ++t, --len); if (t != p || len == 0) { if (F_ISSET(sp, SC_EX_GLOBAL) && t - p == 1 && p[0] == '.') { ++t; if (len > 0) --len; break; } if (db_append(sp, 1, lno++, p, t - p)) return (1); } if (len != 0) { ++t; if (--len == 0 && db_append(sp, 1, lno++, NULL, 0)) return (1); } } /* * If there's any remaining text, we're in a global, and * there's more command to parse. * * !!! * We depend on the fact that non-global commands will eat the * rest of the command line as text input, and before getting * any text input from the user. Otherwise, we'd have to save * off the command text before or during the call to the text * input function below. */ if (len != 0) cmdp->save_cmd = t; cmdp->save_cmdlen = len; } if (F_ISSET(sp, SC_EX_GLOBAL)) { if ((sp->lno = lno) == 0 && db_exist(sp, 1)) sp->lno = 1; return (0); } /* * If not in a global command, read from the terminal. * * If this code is called by vi, we want to reset the terminal and use * ex's line get routine. It actually works fine if we use vi's get * routine, but it doesn't look as nice. Maybe if we had a separate * window or something, but getting a line at a time looks awkward. * However, depending on the screen that we're using, that may not * be possible. */ if (F_ISSET(sp, SC_VI)) { if (gp->scr_screen(sp, SC_EX)) { ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON); return (1); } /* If we're still in the vi screen, move out explicitly. */ need_newline = !F_ISSET(sp, SC_SCR_EXWROTE); F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); if (need_newline) (void)ex_puts(sp, "\n"); /* * !!! * Users of historical versions of vi sometimes get confused * when they enter append mode, and can't seem to get out of * it. Give them an informational message. */ (void)ex_puts(sp, msg_cat(sp, "273|Entering ex input mode.", NULL)); (void)ex_puts(sp, "\n"); (void)ex_fflush(sp); } /* * Set input flags; the ! flag turns off autoindent for append, * change and insert. */ LF_INIT(TXT_DOTTERM | TXT_NUMBER); if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT)) LF_SET(TXT_AUTOINDENT); if (O_ISSET(sp, O_BEAUTIFY)) LF_SET(TXT_BEAUTIFY); /* * This code can't use the common screen TEXTH structure (sp->tiq), * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail * as we are only halfway through the text when the append code fires. * Use a local structure instead. (The ex code would have to use a * local structure except that we're guaranteed to finish remaining * characters in the common TEXTH structure when they were inserted * into the file, above.) */ memset(&tiq, 0, sizeof(TEXTH)); TAILQ_INIT(&tiq); if (ex_txt(sp, &tiq, 0, flags)) return (1); for (cnt = 0, tp = TAILQ_FIRST(&tiq); tp != NULL; ++cnt, tp = TAILQ_NEXT(tp, q)) if (db_append(sp, 1, lno++, tp->lb, tp->len)) return (1); /* * Set sp->lno to the final line number value (correcting for a * possible 0 value) as that's historically correct for the final * line value, whether or not the user entered any text. */ if ((sp->lno = lno) == 0 && db_exist(sp, 1)) sp->lno = 1; return (0); }