1
/******************************************************
3
* zexy - implementation file
5
* copyleft (c) IOhannes m zm�lnig
7
* 1999:forum::f�r::uml�ute:2004
9
* institute of electronic music and acoustics (iem)
11
******************************************************
13
* license: GNU General Public License v.2
15
******************************************************/
19
this is heavily based on code from [textfile],
20
which is part of pd and written by Miller S. Pucket
21
pd (and thus [textfile]) come with their own license
38
/* ****************************************************************************** */
39
/* msgfile : save and load messages... */
45
PD : separate items by ' '; seperate lines by ";\n"
47
CR : separate items by ' '; seperate lines by " \n"
48
how you would expect a file to look like
49
CSV: separate items by ','; seperate lines by " \n"
50
spreadsheet: each argument gets its own column
54
typedef struct _msglist {
62
typedef struct _msgfile
64
t_object x_obj; /* everything */
65
t_outlet *x_secondout; /* "done" */
71
t_msglist *current; /* pointer to our list */
72
t_msglist *previous; /* just in case we lost "current" */
81
static t_class *msgfile_class;
87
/* ************************************************************************ */
88
/* forward declarations */
90
static void msgfile_end(t_msgfile *x);
91
static void msgfile_goto(t_msgfile *x, t_float f);
93
/* ************************************************************************ */
96
static int node_wherearewe(t_msgfile *x)
99
t_msglist *cur = x->start;
101
while (cur && cur->next && cur!=x->current) {
106
return (cur->thislist)?counter:-1;
109
static void write_currentnode(t_msgfile *x, int ac, t_atom *av)
111
/* append list to the current node list */
113
t_msglist *cur=x->current;
117
if(!cur || (ac && av && A_SYMBOL==av->a_type && gensym("")==atom_getsymbol(av))){
118
/* ignoring empty symbols! */
122
newsize = cur->n + ac;
124
ap = (t_atom *)getbytes(newsize * sizeof(t_atom));
125
memcpy(ap, cur->thislist, cur->n * sizeof(t_atom));
127
memcpy(cur->thislist + cur->n, av, ac * sizeof(t_atom));
132
static void delete_currentnode(t_msgfile *x)
135
t_msglist *dummy = x->current;
143
if(dummy==x->start) {
147
freebytes(dummy->thislist, sizeof(dummy->thislist));
153
freebytes(dummy, sizeof(t_msglist));
158
if (nxt) nxt->previous = prv;
159
if (prv) prv->next = nxt;
161
x->current = (nxt)?nxt:prv;
163
x->previous=x->current->previous;
170
static void delete_emptynodes(t_msgfile *x)
174
if (!x->current) return;
176
while (x->current && x->current->next) {
177
if (!x->current->thislist) delete_currentnode(x);
179
x->previous=x->current;
180
x->current = x->current->next;
185
static void add_currentnode(t_msgfile *x)
187
/* add (after the current node) a node at the current position (do not write the listbuf !!!) */
188
t_msglist *newnode = (t_msglist *)getbytes(sizeof(t_msglist));
189
t_msglist *prv, *nxt, *cur=x->current;
192
newnode->thislist = 0;
195
nxt = (cur)?cur->next:0;
198
newnode->previous = prv;
200
if (prv) prv->next = newnode;
201
if (nxt) nxt->previous = newnode;
203
x->current = newnode;
206
if(!x->start) /* it's the first line in the buffer */
209
static void insert_currentnode(t_msgfile *x)
210
{ /* insert (add before the current node) a node (do not write a the listbuf !!!) */
212
t_msglist *prv, *nxt, *cur = x->current;
214
if (!(cur && cur->thislist)) {
217
newnode = (t_msglist *)getbytes(sizeof(t_msglist));
220
newnode->thislist = 0;
223
prv = (cur)?cur->previous:0;
226
newnode->previous = prv;
228
if (prv) prv->next = newnode;
229
if (nxt) nxt->previous = newnode;
232
x->current = newnode;
234
/* oh, we have a new start! */
240
/* delete from line "start" to line "stop"
241
* if "stop" is negative, delete from "start" to the end
243
static void delete_region(t_msgfile *x, int start, int stop)
246
int newwhere, oldwhere = node_wherearewe(x);
248
/* get the number of lists in the buffer */
249
t_msglist *dummy = x->start;
252
/* go to the end of the buffer */
253
while (dummy && dummy->next) {
258
if ((stop > counter) || (stop == -1)) stop = counter;
259
if ((stop+1) && (start > stop)) return;
260
if (stop == 0) return;
262
newwhere = (oldwhere < start)?oldwhere:( (oldwhere < stop)?start:start+(oldwhere-stop));
265
msgfile_goto(x, start);
267
while (n--) delete_currentnode(x);
269
if (newwhere+1) msgfile_goto(x, newwhere);
274
static int atomcmp(t_atom *this, t_atom *that)
276
if (this->a_type != that->a_type) return 1;
278
switch (this->a_type) {
280
return !(atom_getfloat(this) == atom_getfloat(that));
283
return strcmp(atom_getsymbol(this)->s_name, atom_getsymbol(that)->s_name);
286
return !(this->a_w.w_gpointer == that->a_w.w_gpointer);
296
static void msgfile_binbuf2listbuf(t_msgfile *x, t_binbuf *bbuf)
298
int ac = binbuf_getnatom(bbuf);
299
t_atom *ap = binbuf_getvec(bbuf);
302
if (ap->a_type == A_SEMI) {
305
write_currentnode(x, 1, ap);
310
delete_emptynodes(x);
317
/* ************************************************************************ */
320
static void msgfile_rewind(t_msgfile *x)
322
// while (x->current && x->current->previous) x->current = x->current->previous;
323
x->current = x->start;
326
static void msgfile_end(t_msgfile *x)
328
if (!x->current) return;
329
while (x->current->next) {
330
x->previous= x->current;
331
x->current = x->current->next;
335
static void msgfile_goto(t_msgfile *x, t_float f)
340
if (!x->current) return;
343
while (i-- && x->current->next) {
344
x->previous= x->current;
345
x->current = x->current->next;
348
static void msgfile_skip(t_msgfile *x, t_float f)
353
t_msglist *dummy = x->start;
356
if (!x->current) return;
358
while (dummy->next && dummy!=x->current) {
369
static void msgfile_clear(t_msgfile *x)
371
/* find the beginning */
375
delete_currentnode(x);
379
static void msgfile_delete(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
382
int pos = atom_getfloat(av);
383
int oldwhere = node_wherearewe(x);
386
if (oldwhere > pos) oldwhere--;
387
msgfile_goto(x, pos);
388
delete_currentnode(x);
389
msgfile_goto(x, oldwhere);
391
int pos1 = atom_getfloat(av++);
392
int pos2 = atom_getfloat(av);
394
if ((pos1 < pos2) || (pos2 == -1)) {
395
if (pos2+1) delete_region(x, pos1, pos2+1);
396
else delete_region(x, pos1, -1);
398
delete_region(x, pos1+1, -1);
399
delete_region(x, 0, pos2);
401
} else delete_currentnode(x);
404
static void msgfile_add(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
408
write_currentnode(x, ac, av);
410
static void msgfile_add2(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
413
if (x->current->previous) x->current = x->current->previous;
414
write_currentnode(x, ac, av);
415
if (x->current->next) {
416
x->previous= x->current;
417
x->current = x->current->next;
420
static void msgfile_append(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
423
write_currentnode(x, ac, av);
425
static void msgfile_append2(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
427
if (x->current->thislist) write_currentnode(x, ac, av);
428
else msgfile_append(x, s, ac, av);
430
static void msgfile_insert(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
432
t_msglist *cur = x->current;
433
insert_currentnode(x);
434
write_currentnode(x, ac, av);
437
static void msgfile_insert2(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
439
t_msglist *cur = x->current;
440
if ((x->current) && (x->current->previous)) x->current = x->current->previous;
441
write_currentnode(x, ac, av);
445
static void msgfile_set(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
448
msgfile_add(x, s, ac, av);
451
static void msgfile_replace(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
454
if(x->current->thislist) {
455
freebytes(x->current->thislist, sizeof(x->current->thislist));
457
x->current->thislist = 0;
462
write_currentnode(x, ac, av);
465
static void msgfile_flush(t_msgfile *x)
467
t_msglist *cur = x->start;
468
while (cur && cur->thislist) {
469
outlet_list(x->x_obj.ob_outlet, gensym("list"), cur->n, cur->thislist);
473
static void msgfile_this(t_msgfile *x)
475
if ((x->current) && (x->current->thislist)) {
476
outlet_list(x->x_obj.ob_outlet, gensym("list"), x->current->n, x->current->thislist);
478
outlet_bang(x->x_secondout);
481
static void msgfile_next(t_msgfile *x)
483
if ((x->current) && (x->current->next)) {
484
t_msglist *next = x->current->next;
486
outlet_list(x->x_obj.ob_outlet, gensym("list"), next->n, next->thislist);
487
else outlet_bang(x->x_secondout);
488
} else outlet_bang(x->x_secondout);
490
static void msgfile_prev(t_msgfile *x)
494
if ((x->current) && (x->current->previous)) {
495
prev = x->current->previous;
496
} else if (x->previous) {
501
outlet_list(x->x_obj.ob_outlet, gensym("list"), prev->n, prev->thislist);
502
else outlet_bang(x->x_secondout);
504
} else outlet_bang(x->x_secondout);
507
static void msgfile_bang(t_msgfile *x)
509
if ((x->current) && (x->current->thislist)) {
510
t_msglist*cur=x->current;
511
x->current=cur->next;
513
outlet_list(x->x_obj.ob_outlet, gensym("list"), cur->n, cur->thislist);
515
outlet_bang(x->x_secondout);
519
static void msgfile_find(t_msgfile *x, t_symbol *s, int ac, t_atom *av)
521
t_msglist *found = 0;
522
t_msglist *cur=x->current;
528
t_atom *this = cur->thislist;
538
if ( (strcmp("*", atom_getsymbol(that)->s_name) && atomcmp(that, this)) ) {
556
x->previous= found->previous;
557
outlet_float(x->x_secondout, node_wherearewe(x));
558
if(found->n && found->thislist)
559
outlet_list(x->x_obj.ob_outlet, gensym("list"), found->n, found->thislist);
561
outlet_bang(x->x_secondout);
565
static void msgfile_where(t_msgfile *x)
567
if (x->current && x->current->thislist) outlet_float(x->x_secondout, node_wherearewe(x));
568
else outlet_bang(x->x_secondout);
572
static void msgfile_sort(t_msgfile *x, t_symbol *s0, t_symbol*s1, t_symbol*r)
574
post("sorting not implemented yet: '%s', '%s' -> '%s'", s0->s_name, s1->s_name, r->s_name);
579
t_atom *atombuf = (t_atom *)getbytes(sizeof(t_atom) * argc);
585
sort_buffer(x, argc, argv);
590
step = (step % 2)?(step+1)/2:step/2;
595
while(i--) { /* there might be some optimization in here */
596
for (n=0; n<(argc-step); n++) {
597
if (buf[n] > buf[n+step]) {
598
t_int i_tmp = idx[n];
599
t_float f_tmp = buf[n];
600
buf[n] = buf[n+step];
602
idx[n] = idx[n+step];
613
/* ********************************** */
616
static void msgfile_read2(t_msgfile *x, t_symbol *filename, t_symbol *format)
622
long readlength, length, pos;
623
char filnam[MAXPDSTRING];
624
char buf[MAXPDSTRING], *bufptr, *readbuf;
625
char *charbinbuf=NULL, *cbb;
626
int charbinbuflength=0;
627
char*dirname=canvas_getdir(x->x_canvas)->s_name;
632
t_binbuf *bbuf = binbuf_new();
638
if ((fd = open_via_path(dirname,
639
filename->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0) {
641
if((fd=open(filename->s_name, rmode)) < 0) {
642
pd_error(x, "can't open in %s/%s", dirname, filename->s_name);
645
sprintf(filnam, "%s", filename->s_name);
649
sprintf(filnam, "%s/%s", buf, bufptr);
652
fil=fopen(filnam, "rb");
653
fseek(fil, 0, SEEK_END);
655
pd_error(x, "could not open '%s'", filnam);
659
fseek(fil, 0, SEEK_SET);
661
if (!(readbuf = t_getbytes(length))) {
662
pd_error(x, "msgfile_read: could not reserve %d bytes to read into", length);
667
if (gensym("cr")==format) {
669
} else if (gensym("csv")==format) {
671
} else if (gensym("pd")==format) {
673
} else if (*format->s_name)
674
pd_error(x, "msgfile_read: unknown flag: %s", format->s_name);
692
if ((readlength = fread(readbuf, sizeof(char), length, fil)) < length) {
693
pd_error(x, "msgfile_read: unable to read %s: %d of %d", filnam, readlength, length);
695
t_freebytes(readbuf, length);
702
/* convert separators and eols to what pd expects in a binbuf*/
705
# define MSGFILE_HEADROOM 1024
706
charbinbuflength=2*length+MSGFILE_HEADROOM;
708
charbinbuf=(char*)getbytes(charbinbuflength);
711
for(pos=0; pos<charbinbuflength; pos++)charbinbuf[pos]=0;
715
while (readlength--) {
716
if(pos>=charbinbuflength){
717
pd_error(x, "msgfile: read error (headroom %d too small!)", MSGFILE_HEADROOM);
721
if (*bufptr == separator) {
723
} else if (*bufptr==eol) {
735
/* convert to binbuf */
736
binbuf_text(bbuf, charbinbuf, charbinbuflength);
737
msgfile_binbuf2listbuf(x, bbuf);
741
t_freebytes(readbuf, length);
742
t_freebytes(charbinbuf, charbinbuflength);
744
static void msgfile_read(t_msgfile *x, t_symbol *filename, t_symbol *format)
747
msgfile_read2(x, filename, format);
750
static void msgfile_write(t_msgfile *x, t_symbol *filename, t_symbol *format)
752
char buf[MAXPDSTRING];
753
t_binbuf *bbuf = binbuf_new();
754
t_msglist *cur = x->start;
756
char *mytext = 0, *dumtext;
757
char filnam[MAXPDSTRING];
766
binbuf_add(bbuf, cur->n, cur->thislist);
767
binbuf_addsemi(bbuf);
771
canvas_makefilename(x->x_canvas, filename->s_name,
774
if(format&&gensym("")!=format) {
775
if(gensym("cr")==format) {
777
} else if(gensym("csv")==format) {
779
} else if(gensym("pd")==format) {
781
} else if(format&&format->s_name) {
782
pd_error(x, "msgfile_write: ignoring unknown flag: %s", format->s_name);
801
binbuf_gettext(bbuf, &mytext, &textlen);
808
else if ((*dumtext==';') && (dumtext[1]=='\n'))
814
sys_bashfilename(filename->s_name, filnam);
815
if (!(f = fopen(filnam, "w"))) {
816
pd_error(x, "msgfile : failed to open %s", filnam);
819
if (fwrite(mytext, textlen*sizeof(char), 1, f) < 1) {
820
pd_error(x, "msgfile : failed to write %s", filnam);
829
/* ********************************** */
832
static void msgfile_print(t_msgfile *x)
834
t_msglist *cur = x->start;
836
post("--------- msgfile contents: -----------");
842
startpost("line %d:", j);
843
for (i = 0; i < dum->n; i++) {
844
t_atom *a = dum->thislist + i;
852
static void msgfile_help(t_msgfile *x)
854
post("\n%c msgfile\t:: handle and store files of lists", HEARTSYMBOL);
855
post("goto <n>\t: goto line <n>"
856
"\nrewind\t\t: goto the beginning of the file"
857
"\nend\t\t: goto the end of the file"
858
"\nskip <n>\t: move relatively to current position"
859
"\nbang\t\t: output current line and move forward"
860
"\nprev\t\t: output previous line"
861
"\nthis\t\t: output this line"
862
"\nnext\t\t: output next line"
863
"\nflush\t\t: output all lines");
864
post("set <list>\t: clear the buffer and add <list>"
865
"\nadd <list>\t: add <list> at the end of the file"
866
"\nadd2 <list>\t: append <list> to the last line of the file"
867
"\nappend <list>\t: append <list> at the current position"
868
"\nappend2 <list>\t: append <list> to the current line"
869
"\ninsert <list>\t: insert <list> at the current position"
870
"\ninsert2 <list>\t: append <list> to position [current-1]"
871
"\nreplace <list>\t: replace current line by <list>"
872
"\ndelete [<pos> [<pos2>]]\t: delete lines or regions"
873
"\nclear\t\t: delete the whole buffer");
874
post("where\t\t: output current position"
875
"\nfind <list>\t: search for <list>"
876
"\nread <file> [<format>]\t: read <file> as <format>"
877
"\nwrite <file> [<format>]\t: write <file> as <format>"
878
"\n\t\t: valid <formats> are\t: PD, CR, CSV"
879
"\n\nprint\t\t: show buffer (for debugging)"
880
"\nhelp\t\t: show this help");
881
post("creation: \"msgfile [<format>]\": <format> defines fileaccess-mode(default is PD)");
883
static void msgfile_free(t_msgfile *x)
886
freebytes(x->current, sizeof(t_msglist));
889
static void *msgfile_new(t_symbol *s, int argc, t_atom *argv)
891
t_msgfile *x = (t_msgfile *)pd_new(msgfile_class);
893
/* an empty node indicates the end of our listbuffer */
898
x->mode=PD_MODE; /* that's the default */
900
if ((argc==1) && (argv->a_type == A_SYMBOL)) {
901
t_symbol*mode=atom_getsymbol(argv);
902
if (gensym("cr") == mode) x->mode = CR_MODE;
903
else if (gensym("csv")== mode) x->mode = CSV_MODE;
904
else if (gensym("pd") == mode) x->mode = PD_MODE;
906
pd_error(x, "msgfile: unknown argument %s", argv->a_w.w_symbol->s_name);
910
outlet_new(&x->x_obj, &s_list);
911
x->x_secondout = outlet_new(&x->x_obj, &s_float);
912
x->x_canvas = canvas_getcurrent();
920
void msgfile_setup(void)
922
msgfile_class = class_new(gensym("msgfile"), (t_newmethod)msgfile_new,
923
(t_method)msgfile_free, sizeof(t_msgfile), 0, A_GIMME, 0);
924
class_addmethod(msgfile_class, (t_method)msgfile_goto, gensym("goto"), A_DEFFLOAT, 0);
925
class_addmethod(msgfile_class, (t_method)msgfile_rewind, gensym("rewind"), 0);
926
class_addmethod(msgfile_class, (t_method)msgfile_rewind, gensym("begin"), 0);
927
class_addmethod(msgfile_class, (t_method)msgfile_end, gensym("end"), 0);
929
class_addmethod(msgfile_class, (t_method)msgfile_next, gensym("next"), A_DEFFLOAT, 0);
930
class_addmethod(msgfile_class, (t_method)msgfile_prev, gensym("prev"), A_DEFFLOAT, 0);
932
class_addmethod(msgfile_class, (t_method)msgfile_skip, gensym("skip"), A_DEFFLOAT, 0);
934
class_addmethod(msgfile_class, (t_method)msgfile_set, gensym("set"), A_GIMME, 0);
936
class_addmethod(msgfile_class, (t_method)msgfile_clear, gensym("clear"), 0);
937
class_addmethod(msgfile_class, (t_method)msgfile_delete, gensym("delete"), A_GIMME, 0);
939
class_addmethod(msgfile_class, (t_method)msgfile_add, gensym("add"), A_GIMME, 0);
940
class_addmethod(msgfile_class, (t_method)msgfile_add2, gensym("add2"), A_GIMME, 0);
941
class_addmethod(msgfile_class, (t_method)msgfile_append, gensym("append"), A_GIMME, 0);
942
class_addmethod(msgfile_class, (t_method)msgfile_append2, gensym("append2"), A_GIMME, 0);
943
class_addmethod(msgfile_class, (t_method)msgfile_insert, gensym("insert"), A_GIMME, 0);
944
class_addmethod(msgfile_class, (t_method)msgfile_insert2, gensym("insert2"), A_GIMME, 0);
946
class_addmethod(msgfile_class, (t_method)msgfile_replace, gensym("replace"), A_GIMME, 0);
948
class_addmethod(msgfile_class, (t_method)msgfile_find, gensym("find"), A_GIMME, 0);
950
class_addmethod(msgfile_class, (t_method)msgfile_read, gensym("read"), A_SYMBOL, A_DEFSYM, 0);
951
class_addmethod(msgfile_class, (t_method)msgfile_read2, gensym("read2"), A_SYMBOL, A_DEFSYM, 0);
952
class_addmethod(msgfile_class, (t_method)msgfile_write, gensym("write"), A_SYMBOL, A_DEFSYM, 0);
953
class_addmethod(msgfile_class, (t_method)msgfile_print, gensym("print"), 0);
954
class_addmethod(msgfile_class, (t_method)msgfile_flush, gensym("flush"), 0);
956
class_addbang(msgfile_class, msgfile_bang);
957
class_addmethod(msgfile_class, (t_method)msgfile_this, gensym("this"), 0);
958
class_addmethod(msgfile_class, (t_method)msgfile_where, gensym("where"), 0);
961
class_addmethod(msgfile_class, (t_method)msgfile_sort, gensym("sort"), A_SYMBOL, A_SYMBOL, A_SYMBOL, 0);
963
class_addmethod(msgfile_class, (t_method)msgfile_help, gensym("help"), 0);
965
zexy_register("msgfile");