/******************************************* * Convenience wrappers around Text methods *******************************************/ #include "wily.h" #include "text.h" #include "view.h" #include /**************************************************** Random garbage ****************************************************/ void text_allread (Text*t) { undo_reset(t); undo_start(t); viewlist_refresh(t->v); } /**************************************************** Simple data access ****************************************************/ Data* text_data(Text*t) { return t? t->data:0; } View* text_view(Text*t) { return t->v; } ulong text_length(Text*t) { return t->length; } Bool text_needsbackup(Text*t) { return t->needsbackup; } /**************************************************** Cooked data access ****************************************************/ /* Return body view associated with 't'. */ View* text_body(Text*t) { if(t->data) { if (TEXT_ISTAG(t)) { t = data_body(t->data); return t->v; } else { return t->v; } } else { return 0; } } /**************************************************** Simple data manipulations ****************************************************/ void text_setneedsbackup(Text*t, Bool b) { t->needsbackup = b; } Bool text_badrange(Text *t, Range r) { return r.p1 > t->length || r.p0 > r.p1; } void text_addview(Text*t, View*v) { v->next = t->v; t->v = v; } /* * Remove 'v' from list of views displaying 't'. If 'v' was the last * one, remove 't'. If 't' represents some Data, make sure we've backed * it up if necessary. Return 0 for success. */ int text_rmview(Text*t, View *v) { View**ptr; if (t->v ==v && v->next == 0) { /* this is the last view */ if (t->isbody && data_del(t->data)) return -1; } /* update the list of views of the same body */ for (ptr = &(t->v); *ptr != v; ptr = &( (*ptr)->next)) ; *ptr = v->next; if(!t->v) { /* we've deleted the last view */ text_free(t); free(t); } return 0; } /**************************************************** Write to file ****************************************************/ /* Write the contents of 't' to 'fname'. Return 0 for success. */ int text_write(Text *t, char *fname) { int fd; int retval; /* open 0666 and rely on the umask */ if((fd = open(fname,O_RDWR|O_CREAT|O_TRUNC, 0666))<0){ diag(fname, "couldn't open %s for write",fname); return 1; } retval = text_write_range(t, range(0, t->length), fd); if(retval) diag(fname, "couldn't write %s",fname); close(fd); return retval; } /*Return a file descriptor open for reading from a temporary * file which has a copy of the current selection. * * The input for the child comes from the current selection. * Put the selection into a text file and attach that text * file to the command's fdin. */ int text_fd(Text *t, Range sel) { char *file = tmpnam(0); int fd; int input; if ((fd = open(file, O_WRONLY|O_CREAT, 0600)) < 0) { perror("open temp file"); (void) unlink(file); return(-1); } /* Now for the child's end. Do it quick so we can unlink. */ if ((input = open(file, O_RDONLY)) < 0) { perror("open temp file"); (void) unlink(file); return(-1); } if (unlink(file) < 0) perror("unlink temp file"); /* no need to *do* anything about it */ /* Our buffer is the most recent selection, or if the * most recent selection is in a tag, the selection in the body * of that win. */ if (text_write_range(t, sel, fd)) { perror("write temp file"); return(-1); } if (close(fd) < 0) perror("close temp file"); return input; } /**************************************************** Auto indent ****************************************************/ enum {MAXAI=128}; /* * Return an Rstring to be inserted after position 'p', * given that autoindent is on. * The Rstring is to a *static* Rune buffer. */ /** NB *not* reentrant */ Rstring text_autoindent(Text *t, ulong p) { static Rune buf[MAXAI]; Rstring s; Range r; int i; buf[0] = '\n'; s.r0 = buf; r = range(text_startOfLine(t, p), p); if (RLEN(r) > MAXAI) r.p1 = r.p0 + MAXAI; text_copy(t, r, buf +1); i = 1; while (i <= RLEN(r) && isspace(buf[i])) i++; s.r1 = buf + i; return s; } /**************************************************** Convenience functions ****************************************************/ /* * Return the character to start displaying from, * such that the character at 'p' will be offset by * 'height', when displayed * in a window of width 'w', with Font 'f' */ int back_height(Text *t, ulong p, Font *f, int width, int height) { int c, hpos; if (p > 0) --p; for( hpos = 0; p>0 && height>0; p--) { Tgetcset(t,p); c = Tgetc(t); switch(c) { case '\t': hpos += tabsize; break; case '\n': hpos = 0; height -= f->height; break; default: hpos += charwidth(f,c); break; } if (hpos >= width) { hpos =0; height -= f->height; } } return p? p+2 : p; } Range text_all(Text*t){ Range r; r.p0 = 0; r.p1 = t->length; return r; } void text_fillbutton(Text*t, Fcode f) { View*v; for(v = t->v; v; v= v->next) view_fillbutton(v,f); } /* Try to copy 'n' Runes at 'p' from 't' into 'buf'. * Return the number of runes copied. */ ulong text_ncopy(Text *t, Rune*buf, ulong p, ulong n) { Range r; if (p >= t->length) return 0; r = range(p, MIN(p+n, t->length)); text_copy(t, r, buf); return RLEN(r); } /* Replace 'r' in 't' with 'utf', return the range of newly placed runes */ Range text_replaceutf(Text*t, Range r, char*utf) { Rstring s; s = utf2rstring(utf); r = text_replace(t, r, s); if(s.r0) free(s.r0); return r; } /* Return a newly allocated string containing the utf * representation of the given range inside 't' */ char * text_duputf(Text *t, Range r) { ulong len = RLEN(r); ulong n; char *buf; if(!len) return strdup(""); buf = salloc(len * UTFmax); n = text_copyutf(t, r, buf); buf[n] = '\0'; return buf; } /* Copy Range 'r' from 't', convert it to utf, store it * in 'buf', (which must have enough space allocated). * Returns the number of utf characters copied. */ int text_copyutf(Text*t, Range r, char *buf) { Rune *rbuf; int rlen; int n; rlen = RLEN(r); rbuf = salloc( rlen * sizeof(Rune)); text_copy(t, r, rbuf); n = texttoutf(buf, rbuf, rbuf + rlen); free(rbuf); return n; } /**************************************************** Formatting text representing a directory ****************************************************/ static void text_getdir(Text *t, char**names) { Frame *f; char *s; f = &t->v->f; s = columnate(Dx(f->r), f->maxtab, f->font, names); undo_reset(t); text_replaceutf(t, range(0, t->length), s); undo_start(t); free(s); } /* * If 't' represents a directory, and doesn't have any modified text, * reformat the directory and return true. Otherwise return false. */ Bool text_refreshdir(Text*t) { char**names; if ( t->isbody && (names = data_names(t->data)) && undo_atmark(t) && t->v) { text_getdir(t, names); return true; } else { return false; } } /* Reformat 'd' (which must hold a directory) to fit nicely * in the smallest view displaying it. */ void text_formatdir(Text *t, char**names) { if(!(t->v && names)) return; text_getdir(t,names); viewlist_refresh(t->v); }