diff options
Diffstat (limited to 'src/msgfile.c')
-rw-r--r-- | src/msgfile.c | 800 |
1 files changed, 800 insertions, 0 deletions
diff --git a/src/msgfile.c b/src/msgfile.c new file mode 100644 index 0000000..a059fd5 --- /dev/null +++ b/src/msgfile.c @@ -0,0 +1,800 @@ +/****************************************************** + * + * zexy - implementation file + * + * copyleft (c) IOhannes m zmölnig + * + * 1999:forum::für::umläute:2004 + * + * institute of electronic music and acoustics (iem) + * + ****************************************************** + * + * license: GNU General Public License v.2 + * + ******************************************************/ + + +/* + this is heavily based on code from [textfile], + which is part of pd and written by Miller S. Pucket + pd (and thus [textfile]) come with their own license +*/ + +#include "zexy.h" +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#ifdef linux +#include <unistd.h> +#endif +#ifdef NT +#include <io.h> +#endif + +/* ****************************************************************************** */ +/* msgfile : save and load messages... */ + +#define PD_MODE 0 +#define CR_MODE 1 +#define CSV_MODE 2 +/* modi + PD : separate items by ' '; seperate lines by ";\n" + looks like a PD-file + CR : separate items by ' '; seperate lines by " \n" + how you would expect a file to look like + CSV: separate items by ','; seperate lines by " \n" + spreadsheet: each argument gets its own column +*/ + + +typedef struct _msglist { + int n; + t_atom *thislist; + + void *next; + void *previous; +} t_msglist; + +typedef struct _msgfile +{ + t_object x_obj; /* everything */ + t_outlet *x_secondout; /* "done" */ + + int mode; + + + t_msglist *current; /* pointer to our list */ + + t_symbol *x_dir; + t_canvas *x_canvas; + + char eol, separator; + +} t_msgfile; + +static t_class *msgfile_class; + +static int node_wherearewe(t_msgfile *x) +{ + int counter = 0; + t_msglist *cur = x->current; + + while (cur && cur->previous) cur=cur->previous; + + while (cur && cur->next && cur!=x->current) { + counter++; + cur = cur->next; + } + + return (cur->thislist)?counter:-1; +} + +static void write_currentnode(t_msgfile *x, int ac, t_atom *av) +{ + /* append list to the current node list */ + + t_msglist *cur=x->current; + + if (cur) { + t_atom *ap; + int newsize = cur->n + ac; + + ap = (t_atom *)getbytes(newsize * sizeof(t_atom)); + memcpy(ap, cur->thislist, cur->n * sizeof(t_atom)); + cur->thislist = ap; + memcpy(cur->thislist + cur->n, av, ac * sizeof(t_atom)); + + cur->n = newsize; + } +} + +static void clear_currentnode(t_msgfile *x) +{ + t_msglist *dummy = x->current; + t_msglist *nxt; + t_msglist *prv; + + if (!dummy) return; + + freebytes(dummy->thislist, sizeof(dummy->thislist)); + dummy->thislist = 0; + dummy->n = 0; + + prv = dummy->previous; + nxt = dummy->next; + + if (nxt) nxt->previous = prv; + if (prv) prv->next = nxt; + + if (!prv && !nxt) return; + + x->current = (nxt)?nxt:prv; + freebytes(dummy, sizeof(t_msglist)); +} +static void clear_emptynodes(t_msgfile *x) +{ + t_msglist *dummy = x->current; + + if (!x->current) return; + + while (!dummy->thislist && !dummy->next && dummy->previous) dummy=dummy->previous; + + while (x->current && x->current->previous) x->current = x->current->previous; + while (x->current && x->current->next) { + if (!x->current->thislist) clear_currentnode(x); + else x->current = x->current->next; + } + dummy = x->current; +} + +static void add_currentnode(t_msgfile *x) +{ + /* add (after the current node) a node at the current position (do not write the listbuf !!!) */ + t_msglist *newnode = (t_msglist *)getbytes(sizeof(t_msglist)); + t_msglist *prv, *nxt, *cur=x->current; + + newnode->n = 0; + newnode->thislist = 0; + + prv = cur; + nxt = (cur)?cur->next:0; + + newnode->next = nxt; + newnode->previous = prv; + + if (prv) prv->next = newnode; + if (nxt) nxt->previous = newnode; + + + if (x->current->thislist) x->current = newnode; +} +static void insert_currentnode(t_msgfile *x) +{ /* insert (add before the current node) a node (do not write a the listbuf !!!) */ + t_msglist *newnode; + t_msglist *prv, *nxt, *cur = x->current; + + if (!(cur && cur->thislist)) add_currentnode(x); + else { + newnode = (t_msglist *)getbytes(sizeof(t_msglist)); + + newnode->n = 0; + newnode->thislist = 0; + + nxt = cur; + prv = (cur)?cur->previous:0; + + newnode->next = nxt; + newnode->previous = prv; + + if (prv) prv->next = newnode; + if (nxt) nxt->previous = newnode; + + x->current = newnode; + } +} + +static void msgfile_rewind(t_msgfile *x) +{ + while (x->current && x->current->previous) x->current = x->current->previous; +} +static void msgfile_end(t_msgfile *x) +{ + if (!x->current) return; + while (x->current->next) x->current = x->current->next; + +} +static void msgfile_goto(t_msgfile *x, t_float f) +{ + int i = f; + + if (i<0) return; + if (!x->current) return; + while (x->current && x->current->previous) x->current = x->current->previous; + + while (i-- && x->current->next) { + x->current = x->current->next; + } +} +static void msgfile_skip(t_msgfile *x, t_float f) +{ + int i; + int counter = 0; + + t_msglist *dummy = x->current; + while (dummy && dummy->previous) dummy = dummy->previous; + + if (!f) return; + if (!x->current) return; + + while (dummy->next && dummy!=x->current) { + counter++; + dummy=dummy->next; + } + + i = counter + f; + if (i<0) i=0; + + msgfile_goto(x, i); + +} + +static void msgfile_clear(t_msgfile *x) +{ + while (x->current && x->current->previous) x->current = x->current->previous; + + while (x->current && (x->current->previous || x->current->next)) { + clear_currentnode(x); + } + if (x->current->thislist) { + freebytes(x->current->thislist, sizeof(x->current->thislist)); + x->current->n = 0; + } +} + +static void delete_region(t_msgfile *x, int start, int stop) +{ + int n; + int newwhere, oldwhere = node_wherearewe(x); + + /* get the number of lists in the buffer */ + t_msglist *dummy = x->current; + int counter = 0; + + while (dummy && dummy->previous) dummy=dummy->previous; + while (dummy && dummy->next) { + counter++; + dummy = dummy->next; + } + + if ((stop > counter) || (stop == -1)) stop = counter; + if ((stop+1) && (start > stop)) return; + if (stop == 0) return; + + newwhere = (oldwhere < start)?oldwhere:( (oldwhere < stop)?start:start+(oldwhere-stop)); + n = stop - start; + + msgfile_goto(x, start); + + while (n--) clear_currentnode(x); + + if (newwhere+1) msgfile_goto(x, newwhere); + else msgfile_end(x); +} + +static void msgfile_delete(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + if (ac==1) { + int pos = atom_getfloat(av); + int oldwhere = node_wherearewe(x); + + if (pos<0) return; + if (oldwhere > pos) oldwhere--; + + msgfile_goto(x, pos); + clear_currentnode(x); + msgfile_goto(x, oldwhere); + } else if (ac==2) { + int pos1 = atom_getfloat(av++); + int pos2 = atom_getfloat(av); + + if ((pos1 < pos2) || (pos2 == -1)) { + if (pos2+1) delete_region(x, pos1, pos2+1); + else delete_region(x, pos1, -1); + } else { + delete_region(x, pos1+1, -1); + delete_region(x, 0, pos2); + } + } else clear_currentnode(x); +} + +static void msgfile_add(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + msgfile_end(x); + add_currentnode(x); + write_currentnode(x, ac, av); +} +static void msgfile_add2(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + msgfile_end(x); + if (x->current->previous) x->current = x->current->previous; + write_currentnode(x, ac, av); + if (x->current->next) x->current = x->current->next; +} +static void msgfile_append(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + add_currentnode(x); + write_currentnode(x, ac, av); +} +static void msgfile_append2(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + if (x->current->thislist) write_currentnode(x, ac, av); + else msgfile_append(x, s, ac, av); +} +static void msgfile_insert(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + t_msglist *cur = x->current; + insert_currentnode(x); + write_currentnode(x, ac, av); + x->current = cur; +} +static void msgfile_insert2(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + t_msglist *cur = x->current; + if ((x->current) && (x->current->previous)) x->current = x->current->previous; + write_currentnode(x, ac, av); + x->current = cur; +} + +static void msgfile_set(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + msgfile_clear(x); + msgfile_add(x, s, ac, av); +} + +static void msgfile_replace(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + freebytes(x->current->thislist, sizeof(x->current->thislist)); + x->current->thislist = 0; + x->current->n = 0; + write_currentnode(x, ac, av); +} + +static void msgfile_flush(t_msgfile *x) +{ + t_msglist *cur = x->current; + + while (x->current && x->current->previous) x->current=x->current->previous; + while (x->current && x->current->thislist) { + outlet_list(x->x_obj.ob_outlet, gensym("list"), x->current->n, x->current->thislist); + x->current = x->current->next; + } + x->current = cur; +} +static void msgfile_this(t_msgfile *x) +{ + if ((x->current) && (x->current->thislist)) { + outlet_list(x->x_obj.ob_outlet, gensym("list"), x->current->n, x->current->thislist); + } else { + outlet_bang(x->x_secondout); + } +} +static void msgfile_next(t_msgfile *x) +{ + if ((x->current) && (x->current->next)) { + t_msglist *next = x->current->next; + if (next->thislist) + outlet_list(x->x_obj.ob_outlet, gensym("list"), next->n, next->thislist); + else outlet_bang(x->x_secondout); + } else outlet_bang(x->x_secondout); +} +static void msgfile_prev(t_msgfile *x) +{ + if ((x->current) && (x->current->previous)) { + t_msglist *prev = x->current->previous; + if (prev->thislist) + outlet_list(x->x_obj.ob_outlet, gensym("list"), prev->n, prev->thislist); + else outlet_bang(x->x_secondout); + } else outlet_bang(x->x_secondout); +} + +static void msgfile_bang(t_msgfile *x) +{ + msgfile_this(x); + msgfile_skip(x, 1); +} + +static int atomcmp(t_atom *this, t_atom *that) +{ + if (this->a_type != that->a_type) return 1; + + switch (this->a_type) { + case A_FLOAT: + return !(atom_getfloat(this) == atom_getfloat(that)); + break; + case A_SYMBOL: + return strcmp(atom_getsymbol(this)->s_name, atom_getsymbol(that)->s_name); + break; + case A_POINTER: + return !(this->a_w.w_gpointer == that->a_w.w_gpointer); + break; + default: + return 0; + } + + return 1; +} + +static void msgfile_find(t_msgfile *x, t_symbol *s, int ac, t_atom *av) +{ + int searching = 1; + t_msglist *found = x->current; + int actu = 0; + + while ((searching) && (x->current) && (x->current->thislist)) { + int n = x->current->n; + int equal = 1; + t_atom *that = av; + t_atom *this = x->current->thislist; + + if (ac < n) n = ac; + + while (n--) { + if ( (strcmp("*", atom_getsymbol(that)->s_name) && atomcmp(that, this)) ) { + equal = 0; + } + + that++; + this++; + } + + if (equal) { + found = x->current; + outlet_float(x->x_secondout, node_wherearewe(x)); + outlet_list(x->x_obj.ob_outlet, gensym("list"), x->current->n, x->current->thislist); + } + + searching = !(equal); + + if (x->current && x->current->thislist) { + if (x->current->next) x->current = x->current->next; + actu++; + } + } + + if (searching) outlet_bang(x->x_secondout); + x->current = found; +} + +static void msgfile_where(t_msgfile *x) +{ + if (x->current && x->current->thislist) outlet_float(x->x_secondout, node_wherearewe(x)); + else outlet_bang(x->x_secondout); +} +static void msgfile_print(t_msgfile *x) +{ + t_msglist *cur = x->current; + + post("--------- msgfile contents: -----------"); + + while (x->current && x->current->previous) x->current=x->current->previous; + while (x->current) { + t_msglist *dum=x->current; + int i; + startpost(""); + for (i = 0; i < dum->n; i++) { + t_atom *a = dum->thislist + i; + postatom(1, a); + } + endpost(); + x->current = x->current->next; + } + x->current = cur; +} + +static void msgfile_binbuf2listbuf(t_msgfile *x, t_binbuf *bbuf) +{ + int ac = binbuf_getnatom(bbuf); + t_atom *ap = binbuf_getvec(bbuf); + + while (ac--) { + if (ap->a_type == A_SEMI) { + add_currentnode(x); + } else { + write_currentnode(x, 1, ap); + } + ap++; + } + + clear_emptynodes(x); +} + +static void msgfile_read(t_msgfile *x, t_symbol *filename, t_symbol *format) +{ + int rmode = 0; + + int fd; + long readlength, length; + char filnam[MAXPDSTRING]; + char buf[MAXPDSTRING], *bufptr, *readbuf; + + int mode = x->mode; + char separator, eol; + + t_binbuf *bbuf = binbuf_new(); + + if ((fd = open_via_path(canvas_getdir(x->x_canvas)->s_name, + filename->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0) { + error("%s: can't open", filename->s_name); + return; + } + else + close (fd); + + if (!strcmp(format->s_name, "cr")) { + mode = CR_MODE; + } else if (!strcmp(format->s_name, "csv")) { + mode = CSV_MODE; + } else if (!strcmp(format->s_name, "pd")) { + mode = PD_MODE; + } else if (*format->s_name) + error("msgfile_read: unknown flag: %s", format->s_name); + + switch (mode) { + case CR_MODE: + separator = ' '; + eol = ' '; + break; + case CSV_MODE: + separator = ','; + eol = ' '; + break; + default: + separator = ' '; + eol = ';'; + break; + } + + /* open and get length */ + sys_bashfilename(filename->s_name, filnam); + +#ifdef NT + rmode |= O_BINARY; +#endif + + if ((fd = open(filnam, rmode)) < 0) { + error("msgfile_read: unable to open %s", filnam); + return; + } + if ((length = lseek(fd, 0, SEEK_END)) < 0 || lseek(fd, 0,SEEK_SET) < 0 + || !(readbuf = t_getbytes(length))) { + error("msgfile_read: unable to lseek %s", filnam); + close(fd); + return; + } + + /* read */ + if ((readlength = read(fd, readbuf, length)) < length) { + error("msgfile_read: unable to read %s", filnam); + close(fd); + t_freebytes(readbuf, length); + return; + } + + /* close */ + close(fd); + + /* undo separators and eols */ + bufptr=readbuf; + + while (readlength--) { + if (*bufptr == separator) { + *bufptr = ' '; + } + else if ((*bufptr == eol) && (bufptr[1] == '\n')) *bufptr = ';'; + bufptr++; + } + + /* convert to binbuf */ + binbuf_text(bbuf, readbuf, length); + msgfile_binbuf2listbuf(x, bbuf); + + + binbuf_free(bbuf); + t_freebytes(readbuf, length); +} + +static void msgfile_write(t_msgfile *x, t_symbol *filename, t_symbol *format) +{ + char buf[MAXPDSTRING]; + t_binbuf *bbuf = binbuf_new(); + t_msglist *cur = x->current; + + char *mytext = 0, *dumtext; + char filnam[MAXPDSTRING]; + int textlen = 0, i; + + char separator, eol; + int mode = x->mode; + + FILE *f=0; + + while (x->current && x->current->previous) x->current=x->current->previous; + + while(x->current) { + binbuf_add(bbuf, x->current->n, x->current->thislist); + binbuf_addsemi(bbuf); + x->current = x->current->next; + } + x->current = cur; + + canvas_makefilename(x->x_canvas, filename->s_name, + buf, MAXPDSTRING); + + if (!strcmp(format->s_name, "cr")) { + mode = CR_MODE; + } else if (!strcmp(format->s_name, "csv")) { + mode = CSV_MODE; + } else if (!strcmp(format->s_name, "pd")) { + mode = PD_MODE; + } else if (*format->s_name) + error("msgfile_write: unknown flag: %s", format->s_name); + + switch (mode) { + case CR_MODE: + separator = ' '; + eol = ' '; + break; + case CSV_MODE: + separator = ','; + eol = ' '; + break; + default: + separator = ' '; + eol = ';'; + break; + } + + binbuf_gettext(bbuf, &mytext, &textlen); + dumtext = mytext; + i = textlen; + + while(i--) { + if (*dumtext==' ') *dumtext=separator; + if ((*dumtext==';') && (dumtext[1]=='\n')) *dumtext = eol; + dumtext++; + } + + /* open */ + sys_bashfilename(filename->s_name, filnam); + if (!(f = fopen(filnam, "w"))) { + error("msgfile : failed to open %s", filnam); + } else { + /* write */ + if (fwrite(mytext, textlen*sizeof(char), 1, f) < 1) { + error("msgfile : failed to write %s", filnam); + } + } + /* close */ + if (f) fclose(f); + +#if 0 + if (binbuf_write(bbuf, buf, "", cr)) + error("%s: write failed", filename->s_name); +#endif + + binbuf_free(bbuf); +} + +static void msgfile_help(t_msgfile *x) +{ + post("\n%c msgfile\t:: handle and store files of lists", HEARTSYMBOL); + post("goto <n>\t: goto line <n>" + "\nrewind\t\t: goto the beginning of the file" + "\nend\t\t: goto the end of the file" + "\nskip <n>\t: move relatively to current position" + "\nbang\t\t: output current line and move forward" + "\nprev\t\t: output previous line" + "\nthis\t\t: output this line" + "\nnext\t\t: output next line" + "\nflush\t\t: output all lines" + "\nset <list>\t: clear the buffer and add <list>" + "\nadd <list>\t: add <list> at the end of the file" + "\nadd2 <list>\t: append <list> to the last line of the file" + "\nappend <list>\t: append <list> at the current position" + "\nappend2 <list>\t: append <list> to the current line" + "\ninsert <list>\t: insert <list> at the current position" + "\ninsert2 <list>\t: append <list> to position [current-1]" + "\nreplace <list>\t: replace current line by <list>" + "\ndelete [<pos> [<pos2>]]\t: delete lines or regions" + "\nclear\t\t: delete the whole buffer" + "\nwhere\t\t: output current position" + "\nfind <list>\t: search for <list>" + "\nread <file> [<format>]\t: read <file> as <format>" + "\nwrite <file> [<format>]\t: write <file> as <format>" + "\n\t\t: valid <formats> are\t: PD, CR, CSV" + "\n\nprint\t\t: show buffer (for debugging)" + "\nhelp\t\t: show this help"); + post("creation: \"msgfile [<format>]\": <format> defines fileaccess-mode(default is PD)"); +} +static void msgfile_free(t_msgfile *x) +{ + while (x->current && x->current->previous) x->current=x->current->previous; + + msgfile_clear(x); + freebytes(x->current, sizeof(t_msglist)); +} + +static void *msgfile_new(t_symbol *s, int argc, t_atom *argv) +{ + t_msgfile *x = (t_msgfile *)pd_new(msgfile_class); + + /* an empty node indicates the end of our listbuffer */ + x->current = (t_msglist *)getbytes(sizeof(t_msglist)); + x->current->n = 0; + x->current->thislist = 0; + x->current->previous = x->current->next = 0; + + if ((argc==1) && (argv->a_type == A_SYMBOL)) { + if (!strcmp(argv->a_w.w_symbol->s_name, "cr")) x->mode = CR_MODE; + else if (!strcmp(argv->a_w.w_symbol->s_name, "csv")) x->mode = CSV_MODE; + else if (!strcmp(argv->a_w.w_symbol->s_name, "pd")) x->mode = PD_MODE; + else { + error("msgfile: unknown argument %s", argv->a_w.w_symbol->s_name); + x->mode = PD_MODE; + } + } else x->mode = PD_MODE; + + outlet_new(&x->x_obj, &s_list); + x->x_secondout = outlet_new(&x->x_obj, &s_float); + x->x_canvas = canvas_getcurrent(); + + x->eol=' '; + x->separator=','; + return (x); +} + +void msgfile_setup(void) +{ + msgfile_class = class_new(gensym("msgfile"), (t_newmethod)msgfile_new, + (t_method)msgfile_free, sizeof(t_msgfile), 0, A_GIMME, 0); + class_addmethod(msgfile_class, (t_method)msgfile_goto, gensym("goto"), A_DEFFLOAT, 0); + class_addmethod(msgfile_class, (t_method)msgfile_rewind, gensym("rewind"), 0); + class_addmethod(msgfile_class, (t_method)msgfile_rewind, gensym("begin"), 0); + class_addmethod(msgfile_class, (t_method)msgfile_end, gensym("end"), 0); + + class_addmethod(msgfile_class, (t_method)msgfile_next, gensym("next"), A_DEFFLOAT, 0); + class_addmethod(msgfile_class, (t_method)msgfile_prev, gensym("prev"), A_DEFFLOAT, 0); + + class_addmethod(msgfile_class, (t_method)msgfile_skip, gensym("skip"), A_DEFFLOAT, 0); + + class_addmethod(msgfile_class, (t_method)msgfile_set, gensym("set"), A_GIMME, 0); + + class_addmethod(msgfile_class, (t_method)msgfile_clear, gensym("clear"), 0); + class_addmethod(msgfile_class, (t_method)msgfile_delete, gensym("delete"), A_GIMME, 0); + + class_addmethod(msgfile_class, (t_method)msgfile_add, gensym("add"), A_GIMME, 0); + class_addmethod(msgfile_class, (t_method)msgfile_add2, gensym("add2"), A_GIMME, 0); + class_addmethod(msgfile_class, (t_method)msgfile_append, gensym("append"), A_GIMME, 0); + class_addmethod(msgfile_class, (t_method)msgfile_append2, gensym("append2"), A_GIMME, 0); + class_addmethod(msgfile_class, (t_method)msgfile_insert, gensym("insert"), A_GIMME, 0); + class_addmethod(msgfile_class, (t_method)msgfile_insert2, gensym("insert2"), A_GIMME, 0); + + class_addmethod(msgfile_class, (t_method)msgfile_replace, gensym("replace"), A_GIMME, 0); + + class_addmethod(msgfile_class, (t_method)msgfile_find, gensym("find"), A_GIMME, 0); + + class_addmethod(msgfile_class, (t_method)msgfile_read, gensym("read"), A_SYMBOL, A_DEFSYM, 0); + class_addmethod(msgfile_class, (t_method)msgfile_write, gensym("write"), A_SYMBOL, A_DEFSYM, 0); + class_addmethod(msgfile_class, (t_method)msgfile_print, gensym("print"), 0); + class_addmethod(msgfile_class, (t_method)msgfile_flush, gensym("flush"), 0); + + class_addbang(msgfile_class, msgfile_bang); + class_addmethod(msgfile_class, (t_method)msgfile_this, gensym("this"), 0); + class_addmethod(msgfile_class, (t_method)msgfile_where, gensym("where"), 0); + + class_addmethod(msgfile_class, (t_method)msgfile_help, gensym("help"), 0); + class_sethelpsymbol(msgfile_class, gensym("zexy/msgfile")); + + zexy_register("msgfile"); +} |