/* Add or Update Contact details */ static void add_update_user(Db_Entry NewContact) { Db_Entry *entry; if (App.editing) { Db_Entry *contact; contact = elm_list_item_data_get(App.selection); /* Replace strings on memory */ eina_stringshare_replace(&(contact->Name), NewContact.Name); eina_stringshare_replace(&(contact->Email), NewContact.Email); eina_stringshare_replace(&(contact->Phone), NewContact.Phone); eina_stringshare_replace(&(contact->Street), NewContact.Street); eina_stringshare_replace(&(contact->Neighborhood), NewContact.Neighborhood); contact->Gender = NewContact.Gender; /* Update Elementary List */ elm_list_item_label_set(App.selection, NewContact.Name); } else { /* Add New Entry on DB */ entry = db_append(App._db, NewContact); elm_list_item_append(App.contactlist, entry->Name, NULL, NULL, NULL, entry); elm_list_go(App.contactlist); } }
/* * api_aline -- * Append a line. * * PUBLIC: int api_aline __P((SCR *, db_recno_t, char *, size_t)); */ int api_aline(SCR *sp, db_recno_t lno, char *line, size_t len) { size_t wblen; const CHAR_T *wbp; CHAR2INT(sp, line, len, wbp, wblen); return (db_append(sp, 1, lno, wbp, wblen)); }
/* * api_extend -- * Extend file. * * PUBLIC: int api_extend __P((SCR *, db_recno_t)); */ int api_extend(SCR *sp, db_recno_t lno) { db_recno_t lastlno; if (db_last(sp, &lastlno)) return 1; while(lastlno < lno) if (db_append(sp, 1, lastlno++, NULL, 0)) return 1; return 0; }
static int io(SCR *sp, VICMD *vp, enum which cmd) { db_recno_t ai_line, lno; size_t len; u_int32_t flags; CHAR_T *p; flags = set_txt_std(sp, vp, TXT_ADDNEWLINE | TXT_APPENDEOL); sp->showmode = SM_INSERT; if (sp->lno == 1) { if (db_last(sp, &lno)) return (1); if (lno != 0) goto insert; p = NULL; len = 0; ai_line = OOBLNO; } else { static CHAR_T nul = 0; insert: p = &nul; sp->cno = 0; LOG_CORRECT; if (cmd == O_cmd) { if (db_insert(sp, sp->lno, p, 0)) return (1); if (db_get(sp, sp->lno, DBG_FATAL, &p, &len)) return (1); ai_line = sp->lno + 1; } else { if (db_append(sp, 1, sp->lno, p, 0)) return (1); if (db_get(sp, ++sp->lno, DBG_FATAL, &p, &len)) return (1); ai_line = sp->lno - 1; } } return (v_txt(sp, vp, NULL, p, len, 0, ai_line, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags)); }
/* * put -- * Put text buffer contents into the file. * * PUBLIC: int put __P((SCR *, CB *, CHAR_T *, MARK *, MARK *, int)); */ int put(SCR *sp, CB *cbp, ARG_CHAR_T *namep, MARK *cp, MARK *rp, int append) { ARG_CHAR_T name; TEXT *ltp, *tp; db_recno_t lno; size_t blen, clen, len; int rval; CHAR_T *bp, *t; CHAR_T *p; if (cbp == NULL) { if (namep == NULL) { cbp = sp->wp->dcbp; if (cbp == NULL) { msgq(sp, M_ERR, "053|The default buffer is empty"); return (1); } } else { name = *namep; CBNAME(sp, cbp, name); if (cbp == NULL) { msgq(sp, M_ERR, "054|Buffer %s is empty", KEY_NAME(sp, name)); return (1); } } } tp = cbp->textq.cqh_first; /* * It's possible to do a put into an empty file, meaning that the cut * buffer simply becomes the file. It's a special case so that we can * ignore it in general. * * !!! * Historically, pasting into a file with no lines in vi would preserve * the single blank line. This is surely a result of the fact that the * historic vi couldn't deal with a file that had no lines in it. This * implementation treats that as a bug, and does not retain the blank * line. * * Historical practice is that the cursor ends at the first character * in the file. */ if (cp->lno == 1) { if (db_last(sp, &lno)) return (1); if (lno == 0) { for (; tp != (void *)&cbp->textq; ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next) if (db_append(sp, 1, lno, tp->lb, tp->len)) return (1); rp->lno = 1; rp->cno = 0; return (0); } } /* If a line mode buffer, append each new line into the file. */ if (F_ISSET(cbp, CB_LMODE)) { lno = append ? cp->lno : cp->lno - 1; rp->lno = lno + 1; for (; tp != (void *)&cbp->textq; ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next) if (db_append(sp, 1, lno, tp->lb, tp->len)) return (1); rp->cno = 0; (void)nonblank(sp, rp->lno, &rp->cno); return (0); } /* * If buffer was cut in character mode, replace the current line with * one built from the portion of the first line to the left of the * split plus the first line in the CB. Append each intermediate line * in the CB. Append a line built from the portion of the first line * to the right of the split plus the last line in the CB. * * Get the first line. */ lno = cp->lno; if (db_get(sp, lno, DBG_FATAL, &p, &len)) return (1); GET_SPACE_RETW(sp, bp, blen, tp->len + len + 1); t = bp; /* Original line, left of the split. */ if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) { MEMCPYW(bp, p, clen); p += clen; t += clen; } /* First line from the CB. */ if (tp->len != 0) { MEMCPYW(t, tp->lb, tp->len); t += tp->len; } /* Calculate length left in the original line. */ clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0)); /* * !!! * In the historical 4BSD version of vi, character mode puts within * a single line have two cursor behaviors: if the put is from the * unnamed buffer, the cursor moves to the character inserted which * appears last in the file. If the put is from a named buffer, * the cursor moves to the character inserted which appears first * in the file. In System III/V, it was changed at some point and * the cursor always moves to the first character. In both versions * of vi, character mode puts that cross line boundaries leave the * cursor on the first character. Nvi implements the System III/V * behavior, and expect POSIX.2 to do so as well. */ rp->lno = lno; rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 0); /* * If no more lines in the CB, append the rest of the original * line and quit. Otherwise, build the last line before doing * the intermediate lines, because the line changes will lose * the cached line. */ if (tp->q.cqe_next == (void *)&cbp->textq) { if (clen > 0) { MEMCPYW(t, p, clen); t += clen; } if (db_set(sp, lno, bp, t - bp)) goto err; if (sp->rptlchange != lno) { sp->rptlchange = lno; ++sp->rptlines[L_CHANGED]; } } else { /* * Have to build both the first and last lines of the * put before doing any sets or we'll lose the cached * line. Build both the first and last lines in the * same buffer, so we don't have to have another buffer * floating around. * * Last part of original line; check for space, reset * the pointer into the buffer. */ ltp = cbp->textq.cqh_last; len = t - bp; ADD_SPACE_RETW(sp, bp, blen, ltp->len + clen); t = bp + len; /* Add in last part of the CB. */ MEMCPYW(t, ltp->lb, ltp->len); if (clen) MEMCPYW(t + ltp->len, p, clen); clen += ltp->len; /* * Now: bp points to the first character of the first * line, t points to the last character of the last * line, t - bp is the length of the first line, and * clen is the length of the last. Just figured you'd * want to know. * * Output the line replacing the original line. */ if (db_set(sp, lno, bp, t - bp)) goto err; if (sp->rptlchange != lno) { sp->rptlchange = lno; ++sp->rptlines[L_CHANGED]; } /* Output any intermediate lines in the CB. */ for (tp = tp->q.cqe_next; tp->q.cqe_next != (void *)&cbp->textq; ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next) if (db_append(sp, 1, lno, tp->lb, tp->len)) goto err; if (db_append(sp, 1, lno, t, clen)) goto err; ++sp->rptlines[L_ADDED]; } rval = 0; if (0) err: rval = 1; FREE_SPACEW(sp, bp, blen); return (rval); }
/* * v_replace -- [count]r<char> * * !!! * The r command in historic vi was almost beautiful in its badness. For * example, "r<erase>" and "r<word erase>" beeped the terminal and deleted * a single character. "Nr<carriage return>", where N was greater than 1, * inserted a single carriage return. "r<escape>" did cancel the command, * but "r<literal><escape>" erased a single character. To enter a literal * <literal> character, it required three <literal> characters after the * command. This may not be right, but at least it's not insane. * * PUBLIC: int v_replace __P((SCR *, VICMD *)); */ int v_replace(SCR *sp, VICMD *vp) { EVENT ev; VI_PRIVATE *vip; TEXT *tp; size_t blen, len; u_long cnt; int quote, rval; CHAR_T *bp; CHAR_T *p; vip = VIP(sp); /* * If the line doesn't exist, or it's empty, replacement isn't * allowed. It's not hard to implement, but: * * 1: It's historic practice (vi beeped before the replacement * character was even entered). * 2: For consistency, this change would require that the more * general case, "Nr", when the user is < N characters from * the end of the line, also work, which would be a bit odd. * 3: Replacing with a <newline> has somewhat odd semantics. */ if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len)) return (1); if (len == 0) { msgq(sp, M_BERR, "186|No characters to replace"); return (1); } /* * Figure out how many characters to be replace. For no particular * reason (other than that the semantics of replacing the newline * are confusing) only permit the replacement of the characters in * the current line. I suppose we could append replacement characters * to the line, but I see no compelling reason to do so. Check this * before we get the character to match historic practice, where Nr * failed immediately if there were less than N characters from the * cursor to the end of the line. */ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; vp->m_stop.lno = vp->m_start.lno; vp->m_stop.cno = vp->m_start.cno + cnt - 1; if (vp->m_stop.cno > len - 1) { v_eol(sp, &vp->m_start); return (1); } /* * If it's not a repeat, reset the current mode and get a replacement * character. */ quote = 0; if (!F_ISSET(vp, VC_ISDOT)) { sp->showmode = SM_REPLACE; if (vs_refresh(sp, 0)) return (1); next: if (v_event_get(sp, &ev, 0, 0)) return (1); switch (ev.e_event) { case E_CHARACTER: /* * <literal_next> means escape the next character. * <escape> means they changed their minds. */ if (!quote) { if (ev.e_value == K_VLNEXT) { quote = 1; goto next; } if (ev.e_value == K_ESCAPE) return (0); } vip->rlast = ev.e_c; vip->rvalue = ev.e_value; break; case E_ERR: case E_EOF: F_SET(sp, SC_EXIT_FORCE); return (1); case E_INTERRUPT: /* <interrupt> means they changed their minds. */ return (0); case E_WRESIZE: /* <resize> interrupts the input mode. */ v_emsg(sp, NULL, VIM_WRESIZE); return (0); case E_REPAINT: if (vs_repaint(sp, &ev)) return (1); goto next; default: v_event_err(sp, &ev); return (0); } } /* Copy the line. */ GET_SPACE_RETW(sp, bp, blen, len); MEMMOVE(bp, p, len); p = bp; /* * Versions of nvi before 1.57 created N new lines when they replaced * N characters with <carriage-return> or <newline> characters. This * is different from the historic vi, which replaced N characters with * a single new line. Users complained, so we match historic practice. */ if ((!quote && vip->rvalue == K_CR) || vip->rvalue == K_NL) { /* Set return line. */ vp->m_stop.lno = vp->m_start.lno + 1; vp->m_stop.cno = 0; /* The first part of the current line. */ if (db_set(sp, vp->m_start.lno, p, vp->m_start.cno)) goto err_ret; /* * The rest of the current line. And, of course, now it gets * tricky. If there are characters left in the line and if * the autoindent edit option is set, white space after the * replaced character is discarded, autoindent is applied, and * the cursor moves to the last indent character. */ p += vp->m_start.cno + cnt; len -= vp->m_start.cno + cnt; if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) for (; len && isblank(*p); --len, ++p); if ((tp = text_init(sp, p, len, len)) == NULL) goto err_ret; if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) { if (v_txt_auto(sp, vp->m_start.lno, NULL, 0, tp)) goto err_ret; vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0; } else vp->m_stop.cno = 0; vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0; if (db_append(sp, 1, vp->m_start.lno, tp->lb, tp->len)) err_ret: rval = 1; else { text_free(tp); rval = 0; } } else { STRSET(bp + vp->m_start.cno, vip->rlast, cnt); rval = db_set(sp, vp->m_start.lno, bp, len); } FREE_SPACEW(sp, bp, blen); vp->m_final = vp->m_stop; return (rval); }
/* * ex_readfp -- * Read lines into the file. * * PUBLIC: int ex_readfp __P((SCR *, char *, FILE *, MARK *, db_recno_t *, int)); */ int ex_readfp(SCR *sp, char *name, FILE *fp, MARK *fm, db_recno_t *nlinesp, int silent) { EX_PRIVATE *exp; GS *gp; db_recno_t lcnt, lno; size_t len; u_long ccnt; /* XXX: can't print off_t portably. */ int nf, rval; char *p; size_t wlen; CHAR_T *wp; gp = sp->gp; exp = EXP(sp); /* * Add in the lines from the output. Insertion starts at the line * following the address. */ ccnt = 0; lcnt = 0; p = "147|Reading..."; for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno, ++lcnt) { if ((lcnt + 1) % INTERRUPT_CHECK == 0) { if (INTERRUPTED(sp)) break; if (!silent) { gp->scr_busy(sp, p, p == NULL ? BUSY_UPDATE : BUSY_ON); p = NULL; } } FILE2INT5(sp, exp->ibcw, exp->ibp, len, wp, wlen); if (db_append(sp, 1, lno, wp, wlen)) goto err; ccnt += len; } if (ferror(fp) || fclose(fp)) goto err; /* Return the number of lines read in. */ if (nlinesp != NULL) *nlinesp = lcnt; if (!silent) { p = msg_print(sp, name, &nf); msgq(sp, M_INFO, "148|%s: %lu lines, %lu characters", p, lcnt, ccnt); if (nf) FREE_SPACE(sp, p, 0); } rval = 0; if (0) { err: msgq_str(sp, M_SYSERR, name, "%s"); (void)fclose(fp); rval = 1; } if (!silent) gp->scr_busy(sp, NULL, BUSY_OFF); return (rval); }
int main(int argc, char** argv) { char* args; /* no option, print usage */ if (argc < 2) { usage(); return 0; } config_load(); /* connect to the db or error */ if (db_connect()) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); return 0; } /* compile the arguments */ args = get_args(argc,argv); /* if listing notes */ if (strcmp(argv[1],"list") == 0) { db_list(args); }else{ /* if null argument print usage */ if (args == NULL || argc <3) { usage(); /* if creating a new note */ }else if (strcmp(argv[1],"new") == 0 || strcmp(argv[1],"create") == 0) { db_new(args); /* if opening/editing an existing note */ }else if (strcmp(argv[1],"open") == 0 || strcmp(argv[1],"edit") == 0) { db_edit(args); /* append to a note if data is on stdin */ }else if (strcmp(argv[1],"append") == 0) { if (isatty(STDIN_FILENO)) { db_edit(args); }else{ db_append(args); } /* encrypt a new or existing note */ }else if (strcmp(argv[1],"encrypt") == 0) { db_encrypt(args); /* decrypt an existing note */ }else if (strcmp(argv[1],"decrypt") == 0) { db_decrypt(args); /* display an existing note */ }else if (strcmp(argv[1],"show") == 0) { db_show(args); /* if deleting note/s */ }else if (strcmp(argv[1],"del") == 0) { db_del(args); /* unknown option, print usage */ }else{ usage(); } } /* free args if we can */ if (args != NULL) free(args); /* close the database */ sqlite3_close(db); /* save config */ config_save(); return 0; }
/* * 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); }
/* * ex_move -- :[line [,line]] mo[ve] line * Move selected lines. * * PUBLIC: int ex_move(SCR *, EXCMD *); */ int ex_move(SCR *sp, EXCMD *cmdp) { LMARK *lmp; MARK fm1, fm2; recno_t cnt, diff, fl, tl, mfl, mtl; size_t blen, len; int mark_reset; char *bp, *p; NEEDFILE(sp, cmdp); /* * It's not possible to move things into the area that's being * moved. */ fm1 = cmdp->addr1; fm2 = cmdp->addr2; if (cmdp->lineno >= fm1.lno && cmdp->lineno <= fm2.lno) { msgq(sp, M_ERR, "Destination line is inside move range"); return (1); } /* * Log the positions of any marks in the to-be-deleted lines. This * has to work with the logging code. What happens is that we log * the old mark positions, make the changes, then log the new mark * positions. Then the marks end up in the right positions no matter * which way the log is traversed. * * XXX * Reset the MARK_USERSET flag so that the log can undo the mark. * This isn't very clean, and should probably be fixed. */ fl = fm1.lno; tl = cmdp->lineno; /* Log the old positions of the marks. */ mark_reset = 0; LIST_FOREACH(lmp, &sp->ep->marks, q) if (lmp->name != ABSMARK1 && lmp->lno >= fl && lmp->lno <= tl) { mark_reset = 1; F_CLR(lmp, MARK_USERSET); (void)log_mark(sp, lmp); } /* Get memory for the copy. */ GET_SPACE_RET(sp, bp, blen, 256); /* Move the lines. */ diff = (fm2.lno - fm1.lno) + 1; if (tl > fl) { /* Destination > source. */ mfl = tl - diff; mtl = tl; for (cnt = diff; cnt--;) { if (db_get(sp, fl, DBG_FATAL, &p, &len)) return (1); BINC_RET(sp, bp, blen, len); memcpy(bp, p, len); if (db_append(sp, 1, tl, bp, len)) return (1); if (mark_reset) LIST_FOREACH(lmp, &sp->ep->marks, q) if (lmp->name != ABSMARK1 && lmp->lno == fl) lmp->lno = tl + 1; if (db_delete(sp, fl)) return (1); } } else { /* Destination < source. */
/* * sscr_insert -- * Take a line from the shell and insert it into the file. */ static int sscr_insert(SCR *sp) { CHAR_T *endp, *p, *t; SCRIPT *sc; struct pollfd pfd[1]; recno_t lno; size_t blen, len, tlen; u_int value; int nr, rval; char *bp; /* Find out where the end of the file is. */ if (db_last(sp, &lno)) return (1); #define MINREAD 1024 GET_SPACE_RET(sp, bp, blen, MINREAD); endp = bp; /* Read the characters. */ rval = 1; sc = sp->script; more: switch (nr = read(sc->sh_master, endp, MINREAD)) { case 0: /* EOF; shell just exited. */ sscr_end(sp); rval = 0; goto ret; case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "shell"); goto ret; default: endp += nr; break; } /* Append the lines into the file. */ for (p = t = bp; p < endp; ++p) { value = KEY_VAL(sp, *p); if (value == K_CR || value == K_NL) { len = p - t; if (db_append(sp, 1, lno++, t, len)) goto ret; t = p + 1; } } if (p > t) { len = p - t; /* * If the last thing from the shell isn't another prompt, wait * up to 1/10 of a second for more stuff to show up, so that * we don't break the output into two separate lines. Don't * want to hang indefinitely because some program is hanging, * confused the shell, or whatever. */ if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) { pfd[0].fd = sc->sh_master; pfd[0].events = POLLIN; if (poll(pfd, 1, 100) > 0) { memmove(bp, t, len); endp = bp + len; goto more; } } if (sscr_setprompt(sp, t, len)) return (1); if (db_append(sp, 1, lno++, t, len)) goto ret; } /* The cursor moves to EOF. */ sp->lno = lno; sp->cno = len ? len - 1 : 0; rval = vs_refresh(sp, 1); ret: FREE_SPACE(sp, bp, blen); return (rval); }
/* * sscr_getprompt -- * Eat lines printed by the shell until a line with no trailing * carriage return comes; set the prompt from that line. */ static int sscr_getprompt(SCR *sp) { CHAR_T *endp, *p, *t, buf[1024]; SCRIPT *sc; struct pollfd pfd[1]; recno_t lline; size_t llen, len; u_int value; int nr; endp = buf; len = sizeof(buf); /* Wait up to a second for characters to read. */ sc = sp->script; pfd[0].fd = sc->sh_master; pfd[0].events = POLLIN; switch (poll(pfd, 1, 5 * 1000)) { case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "poll"); goto prompterr; case 0: /* Timeout */ msgq(sp, M_ERR, "Error: timed out"); goto prompterr; default: /* Characters to read. */ break; } /* Read the characters. */ more: len = sizeof(buf) - (endp - buf); switch (nr = read(sc->sh_master, endp, len)) { case 0: /* EOF. */ msgq(sp, M_ERR, "Error: shell: EOF"); goto prompterr; case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "shell"); goto prompterr; default: endp += nr; break; } /* If any complete lines, push them into the file. */ for (p = t = buf; p < endp; ++p) { value = KEY_VAL(sp, *p); if (value == K_CR || value == K_NL) { if (db_last(sp, &lline) || db_append(sp, 0, lline, t, p - t)) goto prompterr; t = p + 1; } } if (p > buf) { memmove(buf, t, endp - t); endp = buf + (endp - t); } if (endp == buf) goto more; /* Wait up 1/10 of a second to make sure that we got it all. */ switch (poll(pfd, 1, 100)) { case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "poll"); goto prompterr; case 0: /* Timeout */ break; default: /* Characters to read. */ goto more; } /* Timed out, so theoretically we have a prompt. */ llen = endp - buf; endp = buf; /* Append the line into the file. */ if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) { prompterr: sscr_end(sp); return (1); } return (sscr_setprompt(sp, buf, llen)); }
/* * sscr_insert -- * Take a line from the shell and insert it into the file. */ static int sscr_insert(SCR *sp) { struct timeval tv; char *endp, *p, *t; SCRIPT *sc; fd_set rdfd; db_recno_t lno; size_t len; ssize_t nr; char bp[1024]; const CHAR_T *ip; size_t ilen = 0; /* Find out where the end of the file is. */ if (db_last(sp, &lno)) return (1); endp = bp; /* Read the characters. */ sc = sp->script; more: switch (nr = read(sc->sh_master, endp, bp + sizeof(bp) - endp)) { case 0: /* EOF; shell just exited. */ sscr_end(sp); return (0); case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "shell"); return (1); default: endp += nr; break; } /* Append the lines into the file. */ for (p = t = bp; p < endp; ++p) { if (*p == '\r' || *p == '\n') { len = p - t; if (CHAR2INT(sp, t, len, ip, ilen) || db_append(sp, 1, lno++, ip, ilen)) return (1); t = p + 1; } } /* * If the last thing from the shell isn't another prompt, wait up to * 1/10 of a second for more stuff to show up, so that we don't break * the output into two separate lines. Don't want to hang indefinitely * because some program is hanging, confused the shell, or whatever. * Note that sc->sh_prompt can be NULL here. */ len = p - t; if (sc->sh_prompt == NULL || len != sc->sh_prompt_len || memcmp(t, sc->sh_prompt, len) != 0) { tv.tv_sec = 0; tv.tv_usec = 100000; FD_ZERO(&rdfd); FD_SET(sc->sh_master, &rdfd); if (select(sc->sh_master + 1, &rdfd, NULL, NULL, &tv) == 1) { if (len == sizeof(bp)) { if (CHAR2INT(sp, t, len, ip, ilen) || db_append(sp, 1, lno++, ip, ilen)) return (1); endp = bp; } else { memmove(bp, t, len); endp = bp + len; } goto more; } if (sscr_setprompt(sp, t, len)) return (1); } /* Append the remains into the file, and the cursor moves to EOF. */ if (len > 0) { if (CHAR2INT(sp, t, len, ip, ilen) || db_append(sp, 1, lno++, ip, ilen)) return (1); sp->cno = ilen - 1; } else sp->cno = 0; sp->lno = lno; return (vs_refresh(sp, 1)); }
/* * sscr_getprompt -- * Eat lines printed by the shell until a line with no trailing * carriage return comes; set the prompt from that line. */ static int sscr_getprompt(SCR *sp) { struct timeval tv; CHAR_T *endp, *p, *t, buf[1024]; SCRIPT *sc; fd_set fdset; db_recno_t lline; size_t llen, len; e_key_t value; int nr; FD_ZERO(&fdset); endp = buf; len = sizeof(buf); /* Wait up to a second for characters to read. */ tv.tv_sec = 5; tv.tv_usec = 0; sc = sp->script; FD_SET(sc->sh_master, &fdset); switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) { case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "select"); goto prompterr; case 0: /* Timeout */ msgq(sp, M_ERR, "Error: timed out"); goto prompterr; case 1: /* Characters to read. */ break; } /* Read the characters. */ more: len = sizeof(buf) - (endp - buf); switch (nr = read(sc->sh_master, endp, len)) { case 0: /* EOF. */ msgq(sp, M_ERR, "Error: shell: EOF"); goto prompterr; case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "shell"); goto prompterr; default: endp += nr; break; } /* If any complete lines, push them into the file. */ for (p = t = buf; p < endp; ++p) { value = KEY_VAL(sp, *p); if (value == K_CR || value == K_NL) { if (db_last(sp, &lline) || db_append(sp, 0, lline, t, p - t)) goto prompterr; t = p + 1; } } if (p > buf) { MEMMOVE(buf, t, endp - t); endp = buf + (endp - t); } if (endp == buf) goto more; /* Wait up 1/10 of a second to make sure that we got it all. */ tv.tv_sec = 0; tv.tv_usec = 100000; switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) { case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "select"); goto prompterr; case 0: /* Timeout */ break; case 1: /* Characters to read. */ goto more; } /* Timed out, so theoretically we have a prompt. */ llen = endp - buf; endp = buf; /* Append the line into the file. */ if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) { prompterr: sscr_end(sp); return (1); } return (sscr_setprompt(sp, buf, llen)); }