/* * db_insert -- * Insert a line into the file. * * PUBLIC: int db_insert(SCR *, recno_t, CHAR_T *, size_t); */ int db_insert( SCR *sp, recno_t lno, CHAR_T *p, size_t len) { DBT data, key; EXF *ep; char *fp; size_t flen; int rval; #if defined(DEBUG) && 0 TRACE(sp, "insert before %lu: len %lu {%.*s}\n", (u_long)lno, (u_long)len, MIN(len, 20), p); #endif /* Check for no underlying file. */ if ((ep = sp->ep) == NULL) { ex_emsg(sp, NULL, EXM_NOFILEYET); return (1); } INT2FILE(sp, p, len, fp, flen); /* Update file. */ key.data = &lno; key.size = sizeof(lno); data.data = fp; data.size = flen; if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) { msgq(sp, M_SYSERR, "005|unable to insert at line %lu", (u_long)lno); return (1); } /* Flush the cache, update line count, before screen update. */ if (lno >= ep->c_lno) ep->c_lno = OOBLNO; if (ep->c_nlines != OOBLNO) ++ep->c_nlines; /* File now dirty. */ if (F_ISSET(ep, F_FIRSTMODIFY)) (void)rcv_init(sp); F_SET(ep, F_MODIFIED); /* Log change. */ log_line(sp, lno, LOG_LINE_INSERT); /* Update marks, @ and global commands. */ rval = 0; if (mark_insdel(sp, LINE_INSERT, lno)) rval = 1; if (ex_g_insdel(sp, LINE_INSERT, lno)) rval = 1; /* Update screen. */ return (scr_update(sp, lno, LINE_INSERT, 1) || rval); }
/* * db_set -- * Store a line in the file. * * PUBLIC: int db_set __P((SCR *, db_recno_t, CHAR_T *, size_t)); */ int db_set(SCR *sp, db_recno_t lno, CHAR_T *p, size_t len) { DBT data, key; EXF *ep; const char *fp; size_t flen; #if defined(DEBUG) && 0 vtrace(sp, "replace line %lu: len %lu {%.*s}\n", (u_long)lno, (u_long)len, MIN(len, 20), p); #endif /* Check for no underlying file. */ if ((ep = sp->ep) == NULL) { ex_emsg(sp, NULL, EXM_NOFILEYET); return (1); } if (ep->l_win && ep->l_win != sp->wp) { ex_emsg(sp, NULL, EXM_LOCKED); return 1; } /* Log before change. */ log_line(sp, lno, LOG_LINE_RESET_B); INT2FILE(sp, p, len, fp, flen); /* Update file. */ memset(&key, 0, sizeof(key)); key.data = &lno; key.size = sizeof(lno); memset(&data, 0, sizeof(data)); data.data = __UNCONST(fp); data.size = flen; if ((sp->db_error = ep->db->put(ep->db, NULL, &key, &data, 0)) != 0) { msgq(sp, M_DBERR, "006|unable to store line %lu", (u_long)lno); return (1); } /* Flush the cache, update line count, before screen update. */ update_cache(sp, LINE_RESET, lno); /* File now dirty. */ if (F_ISSET(ep, F_FIRSTMODIFY)) (void)rcv_init(sp); F_SET(ep, F_MODIFIED); /* Log after change. */ log_line(sp, lno, LOG_LINE_RESET_F); /* Update screen. */ return (scr_update(sp, lno, LINE_RESET, 1)); }
/* * db_set -- * Store a line in the file. * * PUBLIC: int db_set(SCR *, recno_t, CHAR_T *, size_t); */ int db_set( SCR *sp, recno_t lno, CHAR_T *p, size_t len) { DBT data, key; EXF *ep; char *fp; size_t flen; #if defined(DEBUG) && 0 TRACE(sp, "replace line %lu: len %lu {%.*s}\n", (u_long)lno, (u_long)len, MIN(len, 20), p); #endif /* Check for no underlying file. */ if ((ep = sp->ep) == NULL) { ex_emsg(sp, NULL, EXM_NOFILEYET); return (1); } /* Log before change. */ log_line(sp, lno, LOG_LINE_RESET_B); INT2FILE(sp, p, len, fp, flen); /* Update file. */ key.data = &lno; key.size = sizeof(lno); data.data = fp; data.size = flen; if (ep->db->put(ep->db, &key, &data, 0) == -1) { msgq(sp, M_SYSERR, "006|unable to store line %lu", (u_long)lno); return (1); } /* Flush the cache, before logging or screen update. */ if (lno == ep->c_lno) ep->c_lno = OOBLNO; /* File now dirty. */ if (F_ISSET(ep, F_FIRSTMODIFY)) (void)rcv_init(sp); F_SET(ep, F_MODIFIED); /* Log after change. */ log_line(sp, lno, LOG_LINE_RESET_F); /* Update screen. */ return (scr_update(sp, lno, LINE_RESET, 1)); }
/* * ex_writefp -- * Write a range of lines to a FILE *. * * PUBLIC: int ex_writefp __P((SCR *, * PUBLIC: char *, FILE *, MARK *, MARK *, u_long *, u_long *, int)); */ int ex_writefp(SCR *sp, char *name, FILE *fp, MARK *fm, MARK *tm, u_long *nlno, u_long *nch, int silent) { struct stat sb; GS *gp; u_long ccnt; /* XXX: can't print off_t portably. */ recno_t fline, tline, lcnt; size_t len; int rval; char *msg; CHAR_T *p; char *f; size_t flen; int isutf16; gp = sp->gp; fline = fm->lno; tline = tm->lno; if (nlno != NULL) { *nch = 0; *nlno = 0; } /* * The vi filter code has multiple processes running simultaneously, * and one of them calls ex_writefp(). The "unsafe" function calls * in this code are to db_get() and msgq(). Db_get() is safe, see * the comment in ex_filter.c:ex_filter() for details. We don't call * msgq if the multiple process bit in the EXF is set. * * !!! * Historic vi permitted files of 0 length to be written. However, * since the way vi got around dealing with "empty" files was to * always have a line in the file no matter what, it wrote them as * files of a single, empty line. We write empty files. * * "Alex, I'll take vi trivia for $1000." */ ccnt = 0; lcnt = 0; msg = "253|Writing..."; if (O_ISSET(sp, O_FILEENCODING)) { isutf16 = !strncasecmp(O_STR(sp, O_FILEENCODING), "utf-16", 6); isutf16 += !strncasecmp(O_STR(sp, O_FILEENCODING), "utf-16le", 8); } else isutf16 = 0; if (tline != 0) { if (isutf16 == 1 && fwrite("\xfe\xff", 1, 2, fp) != 2) goto err; if (isutf16 == 2 && fwrite("\xff\xfe", 1, 2, fp) != 2) goto err; for (; fline <= tline; ++fline, ++lcnt) { /* Caller has to provide any interrupt message. */ if ((lcnt + 1) % INTERRUPT_CHECK == 0) { if (INTERRUPTED(sp)) break; if (!silent) { gp->scr_busy(sp, msg, msg == NULL ? BUSY_UPDATE : BUSY_ON); msg = NULL; } } if (db_get(sp, fline, DBG_FATAL, &p, &len)) goto err; INT2FILE(sp, p, len, f, flen); if (fwrite(f, 1, flen, fp) != flen) goto err; ccnt += len; /* UTF-16 w/o BOM is big-endian */ switch (isutf16) { case 1: /* UTF-16BE */ if (fwrite("\0\x0a", 1, 2, fp) != 2) goto done; break; case 2: /* UTF-16LE */ if (fwrite("\x0a\0", 1, 2, fp) != 2) goto done; break; default: if (putc('\n', fp) != '\n') goto done; } ++ccnt; } } done: if (fflush(fp)) goto err; /* * XXX * I don't trust NFS -- check to make sure that we're talking to * a regular file and sync so that NFS is forced to flush. */ if (!fstat(fileno(fp), &sb) && S_ISREG(sb.st_mode) && fsync(fileno(fp))) goto err; if (fclose(fp)) { fp = NULL; goto err; } rval = 0; if (0) { err: if (!F_ISSET(sp->ep, F_MULTILOCK)) msgq_str(sp, M_SYSERR, name, "%s"); if (fp != NULL) fclose(fp); rval = 1; } if (!silent) gp->scr_busy(sp, NULL, BUSY_OFF); /* Report the possibly partial transfer. */ if (nlno != NULL) { *nch = ccnt; *nlno = lcnt; } return (rval); }
/* maybe this could be simpler * * DB3 behaves differently from DB1 * * if lno != 0 just go to lno and put the new line after it * if lno == 0 then if there are any record, put in front of the first * otherwise just append to the end thus creating the first * line */ static int append(SCR *sp, db_recno_t lno, const CHAR_T *p, size_t len, lnop_t op, int update) { DBT data, key; DBC *dbcp_put; EXF *ep; const char *fp; size_t flen; int rval; /* Check for no underlying file. */ if ((ep = sp->ep) == NULL) { ex_emsg(sp, NULL, EXM_NOFILEYET); return (1); } if (ep->l_win && ep->l_win != sp->wp) { ex_emsg(sp, NULL, EXM_LOCKED); return 1; } /* Log before change. */ log_line(sp, lno + 1, LOG_LINE_APPEND_B); /* Update file. */ memset(&key, 0, sizeof(key)); key.data = &lno; key.size = sizeof(lno); memset(&data, 0, sizeof(data)); if ((sp->db_error = ep->db->cursor(ep->db, NULL, &dbcp_put, 0)) != 0) return 1; INT2FILE(sp, p, len, fp, flen); if (lno != 0) { if ((sp->db_error = dbcp_put->c_get(dbcp_put, &key, &data, DB_SET)) != 0) goto err2; data.data = __UNCONST(fp); data.size = flen; if ((sp->db_error = dbcp_put->c_put(dbcp_put, &key, &data, DB_AFTER)) != 0) { err2: (void)dbcp_put->c_close(dbcp_put); msgq(sp, M_DBERR, (op == LINE_APPEND) ? "004|unable to append to line %lu" : "005|unable to insert at line %lu", (u_long)lno); return (1); } } else { if ((sp->db_error = dbcp_put->c_get(dbcp_put, &key, &data, DB_FIRST)) != 0) { if (sp->db_error != DB_NOTFOUND) goto err2; data.data = __UNCONST(fp); data.size = flen; if ((sp->db_error = ep->db->put(ep->db, NULL, &key, &data, DB_APPEND)) != 0) { goto err2; } } else { key.data = &lno; key.size = sizeof(lno); data.data = __UNCONST(fp); data.size = flen; if ((sp->db_error = dbcp_put->c_put(dbcp_put, &key, &data, DB_BEFORE)) != 0) { goto err2; } } } (void)dbcp_put->c_close(dbcp_put); /* Flush the cache, update line count, before screen update. */ update_cache(sp, LINE_INSERT, lno); /* File now dirty. */ if (F_ISSET(ep, F_FIRSTMODIFY)) (void)rcv_init(sp); F_SET(ep, F_MODIFIED); /* Log after change. */ log_line(sp, lno + 1, LOG_LINE_APPEND_F); /* Update marks, @ and global commands. */ rval = line_insdel(sp, LINE_INSERT, lno + 1); /* * Update screen. * * comment copied from db_append * XXX * Nasty hack. If multiple lines are input by the user, they aren't * committed until an <ESC> is entered. The problem is the screen was * updated/scrolled as each line was entered. So, when this routine * is called to copy the new lines from the cut buffer into the file, * it has to know not to update the screen again. */ return (scr_update(sp, lno + 1, LINE_INSERT, update) || rval); }
/* * db_append -- * Append a line into the file. * * PUBLIC: int db_append(SCR *, int, recno_t, CHAR_T *, size_t); */ int db_append( SCR *sp, int update, recno_t lno, CHAR_T *p, size_t len) { DBT data, key; EXF *ep; char *fp; size_t flen; int rval; #if defined(DEBUG) && 0 TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); #endif /* Check for no underlying file. */ if ((ep = sp->ep) == NULL) { ex_emsg(sp, NULL, EXM_NOFILEYET); return (1); } INT2FILE(sp, p, len, fp, flen); /* Update file. */ key.data = &lno; key.size = sizeof(lno); data.data = fp; data.size = flen; if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) { msgq(sp, M_SYSERR, "004|unable to append to line %lu", (u_long)lno); return (1); } /* Flush the cache, update line count, before screen update. */ if (lno < ep->c_lno) ep->c_lno = OOBLNO; if (ep->c_nlines != OOBLNO) ++ep->c_nlines; /* File now dirty. */ if (F_ISSET(ep, F_FIRSTMODIFY)) (void)rcv_init(sp); F_SET(ep, F_MODIFIED); /* Log change. */ log_line(sp, lno + 1, LOG_LINE_APPEND); /* Update marks, @ and global commands. */ rval = 0; if (mark_insdel(sp, LINE_INSERT, lno + 1)) rval = 1; if (ex_g_insdel(sp, LINE_INSERT, lno + 1)) rval = 1; /* * Update screen. * * XXX * Nasty hack. If multiple lines are input by the user, they aren't * committed until an <ESC> is entered. The problem is the screen was * updated/scrolled as each line was entered. So, when this routine * is called to copy the new lines from the cut buffer into the file, * it has to know not to update the screen again. */ return (scr_update(sp, lno, LINE_APPEND, update) || rval); }