shithub: zuke

Download patch

ref: ab23e1cbffa4bea97a99d58fea58b13e1958c9f8
parent: ca81298ff007e930fb84a57df875664b6a81fe0d
author: Sigrid Solveig Haflínudóttir <[email protected]>
date: Thu Jan 14 07:10:38 EST 2021

rework playlist plumbing

--- a/README.md
+++ b/README.md
@@ -36,9 +36,6 @@
 
 	audio/mkplist /n/music | audio/zuke
 
-*NOTE*: don't forget to pass the *full* path to `audio/mkplist`, not a
-relative one.
-
 ## Plumbing
 
 Plumbing music files and playlists is supported via "audio" port.  New
@@ -45,7 +42,7 @@
 files can't be added to the current playlist just yet.
 
 	type	is	text
-	data	matches	'.+\.(mp3|MP3|ogg|OGG|flac|FLAC|wav|WAV|au|AU|mid|MID|mus|MUS|m3u|M3U|pls|PLS|plist)$'
+	data	matches	'.+\.(mp3|MP3|ogg|OGG|flac|FLAC|wav|WAV|au|AU|mid|MID|mus|MUS|m3u|M3U|pls|PLS|it|IT|plist)$'
 	arg	isfile	$0
 	plumb	to	audio
 	plumb	start	window -scroll play $file
--- a/zuke.c
+++ b/zuke.c
@@ -15,6 +15,7 @@
 #define CLAMP(x,min,max) MAX(min, MIN(max, x))
 
 typedef struct Player Player;
+typedef struct Playlist Playlist;
 
 enum
 {
@@ -42,15 +43,20 @@
 	int pcur;
 };
 
+struct Playlist
+{
+	Meta *m;
+	int n;
+	char *raw;
+	int rawsz;
+};
+
 int mainstacksize = 32768;
 
 static int debug;
 static int audio = -1;
-static Meta *pl;
-static int plnum;
-static char *plraw;
-static int plrawsize;
 static int volume;
+static Playlist *pl;
 static Player *playernext;
 static Player *playercurr;
 static vlong byteswritten;
@@ -164,9 +170,9 @@
 	if(mincolwidth[0] == 0){
 		for(i = 0; cols[i] != 0; i++)
 			mincolwidth[i] = 1;
-		for(n = 0; n < plnum; n++){
+		for(n = 0; n < pl->n; n++){
 			for(i = 0; cols[i] != 0; i++){
-				if((x = stringwidth(f, getcol(&pl[n], cols[i]))) > mincolwidth[i])
+				if((x = stringwidth(f, getcol(pl->m+n, cols[i]))) > mincolwidth[i])
 					mincolwidth[i] = x;
 			}
 		}
@@ -194,7 +200,7 @@
 static Meta *
 getmeta(int i)
 {
-	return &pl[shuffle != nil ? shuffle[i] : i];
+	return &pl->m[shuffle != nil ? shuffle[i] : i];
 }
 
 static void
@@ -216,7 +222,7 @@
 	lockdisplay(display);
 	updatescrollsz();
 	left = screen->r.min.x;
-	if(scrollsz < plnum) /* adjust for scrollbar */
+	if(scrollsz < pl->n) /* adjust for scrollbar */
 		left += Scrollwidth + 1;
 
 	if(full){
@@ -224,7 +230,7 @@
 		yield();
 
 		adjustcolumns();
-		if(scrollsz < plnum){ /* scrollbar */
+		if(scrollsz < pl->n){ /* scrollbar */
 			p.x = sp.x = screen->r.min.x + Scrollwidth;
 			p.y = screen->r.min.y;
 			sp.y = screen->r.max.y;
@@ -236,7 +242,7 @@
 			if(scroll < 1)
 				scrollcenter = 0;
 			else
-				scrollcenter = (Dy(screen->r)-Scrollheight*5/4)*scroll / (plnum - scrollsz);
+				scrollcenter = (Dy(screen->r)-Scrollheight*5/4)*scroll / (pl->n - scrollsz);
 			r.min.y += scrollcenter + Scrollheight/4;
 			r.max.y = r.min.y + Scrollheight;
 			draw(screen, r, colors[Dblow].im, nil, ZP);
@@ -257,7 +263,7 @@
 		p.y = screen->r.min.y + 2;
 		yield();
 
-		for(i = scroll; i < plnum; i++, p.y += f->height){
+		for(i = scroll; i < pl->n; i++, p.y += f->height){
 			if(i < 0)
 				continue;
 			if(p.y > screen->r.max.y)
@@ -503,7 +509,7 @@
 		return nil;
 
 done:
-	if(pcur < plnum-1 && playernext == nil && loadnext)
+	if(pcur < pl->n-1 && playernext == nil && loadnext)
 		playernext = newplayer(pcur+1, 0);
 
 	return player;
@@ -642,7 +648,7 @@
 		if(c != Cseekrel || boffset >= getmeta(pcurplaying)->duration/1000*Bps){
 next:
 			playercurr = nil;
-			playercurr = newplayer((player->pcur+1) % plnum, 1);
+			playercurr = newplayer((player->pcur+1) % pl->n, 1);
 			start(playercurr);
 			goto stop;
 		}
@@ -690,35 +696,55 @@
 {
 	int i;
 
-	for(i = 0; i < plnum; i++)
-		printmeta(&out, pl+i);
+	for(i = 0; i < pl->n; i++)
+		printmeta(&out, pl->m+i);
 }
 
 static void
-readplistnew(void)
+freeplist(Playlist *pl)
 {
+	if(pl != nil){
+		free(pl->m);
+		free(pl->raw);
+	}
+	free(pl);
+}
+
+static Playlist *
+readplistnew(char *raw)
+{
 	char *s, *e, *a[5];
+	Playlist *pl;
 	Meta *m;
 	int plsz;
 
 	m = nil;
+	if((pl = calloc(1, sizeof(*pl))) == nil){
+nomem:
+		freeplist(pl);
+		werrstr("no memory");
+		return nil;
+	}
+
 	plsz = 0;
-	for(s = plraw;; s = e){
+	pl->raw = raw;
+	for(s = pl->raw;; s = e){
 		if((e = strchr(s, '\n')) == nil)
 			break;
-		if(plsz - plnum < 32){
+		if(plsz - pl->n < 32){
 			plsz = (plsz + 32)*2;
-			if((pl = realloc(pl, sizeof(Meta)*plsz)) == nil)
-				sysfatal("memory");
-			memset(pl+plnum, 0, sizeof(Meta)*(plsz-plnum));
+			if((m = realloc(pl->m, sizeof(Meta)*plsz)) == nil)
+				goto nomem;
+			pl->m = m;
+			memset(pl->m+pl->n, 0, sizeof(Meta)*(plsz-pl->n));
 		}
-		m = pl + plnum;
+		m = pl->m + pl->n;
 		s += 2;
 		*e++ = 0;
 		switch(s[-2]){
 		case 0:
 			if(m->path != nil)
-				plnum++;
+				pl->n++;
 			m = nil;
 			break;
 		case Pimage:
@@ -745,28 +771,35 @@
 		}
 	}
 	if(m != nil && m->path != nil)
-		plnum++;
+		pl->n++;
+
+	return pl;
 }
 
-static void
+static Playlist *
 readplist(int fd)
 {
 	Meta *m;
+	Playlist *pl;
 	char *s, *e, *endrec;
 	int i, n, sz, alloc, tagsz, intval;
 
-	plnum = 0;
-	pl = nil;
-
 	s = nil;
 	for(alloc = sz = 0;;){
 		alloc += 65536;
-		if((s = realloc(s, alloc)) == nil)
-			sysfatal("no memory");
+		if((e = realloc(s, alloc)) == nil){
+nomem:
+			werrstr("no memory");
+			free(s);
+			return nil;
+		}
+		s = e;
 		for(n = 0; sz < alloc; sz += n){
 			n = readn(fd, s+sz, alloc-sz);
-			if(n < 0)
-				sysfatal("%r");
+			if(n < 0){
+				free(s);
+				return nil;
+			}
 			if(n == 0)
 				break;
 		}
@@ -773,37 +806,56 @@
 		if(n == 0)
 			break;
 	}
+	s[sz-1] = 0;
 
-	plraw = s;
-	plrawsize = sz;
-	plraw[plrawsize-1] = 0;
-	if(sz < 4 || s[0] != '#' || s[1] != ' ' || !isdigit(s[2]) || (s = memchr(plraw, '\n', sz)) == nil){
-		readplistnew();
-		return;
+	if(sz < 4 || s[0] != '#' || s[1] != ' ' || !isdigit(s[2]) || (s = memchr(s, '\n', sz)) == nil){
+		if((pl = readplistnew(s)) == nil)
+			return nil;
+		goto end;
 	}
 
+	if((pl = calloc(1, sizeof(*pl))) == nil)
+		goto nomem;
+	if((pl->m = calloc(pl->n, sizeof(Meta))) == nil){
+		werrstr("no memory");
+error:
+		freeplist(pl);
+		return nil;
+	}
+	pl->raw = s;
+	pl->rawsz = sz;
+	pl->n = atoi(pl->raw+2);
+
 	s++; /* at the start of the first record */
 
-	plnum = atoi(plraw+2);
-	pl = calloc(plnum, sizeof(Meta));
-	for(i = 0; i < plnum; i++, s = endrec){
-		if(plraw+plrawsize < s+10)
-			sysfatal("truncated playlist");
-		if(s[0] != '#' || s[1] != ' ' || !isdigit(s[2]))
-			sysfatal("invalid record");
-		if((n = strtol(s+2, &e, 10)) < 0 || n > plnum)
-			sysfatal("invalid track index");
-		if(pl[n].path != nil)
-			sysfatal("duplicate track index");
+	for(i = 0; i < pl->n; i++, s = endrec){
+		if(pl->raw+pl->rawsz < s+10){
+			werrstr("truncated playlist");
+			goto error;
+		}
+		if(s[0] != '#' || s[1] != ' ' || !isdigit(s[2])){
+			werrstr("invalid record");
+			goto error;
+		}
+		if((n = strtol(s+2, &e, 10)) < 0 || n > pl->n){
+			werrstr("invalid track index");
+			goto error;
+		}
+		if(pl->m[n].path != nil){
+			werrstr("duplicate track index");
+			goto error;
+		}
 
 		s[-1] = 0;
 		sz = strtol(e, &s, 10);
 		*s++ = 0; /* skip '\n' */
-		if(s+sz > plraw+plrawsize)
-			sysfatal("truncated playlist");
+		if(s+sz > pl->raw+pl->rawsz){
+			werrstr("truncated playlist");
+			goto error;
+		}
 		s[sz-1] = 0; /* '\n'→'\0' to mark the end of the record */
 		endrec = s+sz;
-		m = &pl[n];
+		m = pl->m+n;
 
 		for(;;){
 			if(s[0] == Pimage){
@@ -824,8 +876,10 @@
 				s = strchr(s+2, '\n') + 1;
 			}else{
 				tagsz = strtol(s+1, &e, 10);
-				if(e+tagsz >= plraw+plrawsize)
-					sysfatal("truncated playlist");
+				if(e+tagsz >= pl->raw+pl->rawsz){
+					werrstr("truncated playlist");
+					goto error;
+				}
 				e++; /* point to tag value */
 				e[tagsz] = 0; /* '\n'→'\0' to mark the end of the tag value */
 				if(s[0] == Palbum) m->album = e;
@@ -833,14 +887,28 @@
 				else if(s[0] == Ptitle) m->title = e;
 				else if(s[0] == Pdate) m->date = e;
 				else if(s[0] == Ptrack) m->track = e;
-				else sysfatal("unknown tag type %c", s[0]);
+				else{
+					werrstr("unknown tag type %c", s[0]);
+					goto error;
+				}
 				s = e + tagsz + 1;
 			}
 			s[-1] = 0;
 		}
-		if(m->filefmt == nil)
-			sysfatal("old playlist format?\nplease re-run audio/mkplist");
+		if(m->filefmt == nil){
+			fprint(2, "old playlist format? please re-run audio/mkplist or use zuke -G (see man page)\n");
+			werrstr("unknown file format");
+			goto error;
+		}
 	}
+
+end:
+	if(pl->n < 1){
+		werrstr("empty playlist");
+		goto error;
+	}
+
+	return pl;
 }
 
 static void
@@ -867,7 +935,7 @@
 		return;
 
 	cycle = 1;
-	for(i = pcur+inc; i >= 0 && i < plnum;){
+	for(i = pcur+inc; i >= 0 && i < pl->n;){
 		m = getmeta(i);
 		for(a = 0; a < m->numartist; a++){
 			if(cistrstr(m->artist[a], buf) != nil)
@@ -882,15 +950,15 @@
 onemore:
 		i += inc;
 	}
-	if(i >= 0 && i < plnum){
+	if(i >= 0 && i < pl->n){
 		pcur = i;
 		recenter();
 		redraw(1);
 	}else if(cycle && i+inc < 0){
 		cycle = 0;
-		i = plnum;
+		i = pl->n;
 		goto onemore;
-	}else if(cycle && i+inc >= plnum){
+	}else if(cycle && i+inc >= pl->n){
 		cycle = 0;
 		i = -1;
 		goto onemore;
@@ -950,11 +1018,11 @@
 	int i, m, xi, a, c, pcurnew, pcurplayingnew;
 
 	if(shuffle == nil){
-		if(plnum < 2)
+		if(pl->n < 2)
 			return;
 
-		m = plnum;
-		if(plnum < 4){
+		m = pl->n;
+		if(pl->n < 4){
 			a = 1;
 			c = 3;
 			m = 7;
@@ -969,12 +1037,12 @@
 			c = 3 + nrand((m-2)/2)*2; /* 3 ≤ c < m-1 && c mod 2 = 1 */
 		}
 
-		shuffle = malloc(plnum*sizeof(*shuffle));
+		shuffle = malloc(pl->n*sizeof(*shuffle));
 		xi = pcurplaying < 0 ? pcur : pcurplaying;
 		pcurplayingnew = -1;
 		pcurnew = 0;
-		for(i = 0; i < plnum;){
-			if(xi < plnum){
+		for(i = 0; i < pl->n;){
+			if(xi < pl->n){
 				if(pcur == xi)
 					pcurnew = i;
 				if(pcurplaying == xi)
@@ -994,7 +1062,7 @@
 	}
 
 	stop(playernext);
-	if(pcur < plnum-1)
+	if(pcur < pl->n-1)
 		playernext = newplayer(pcur+1, 0);
 }
 
@@ -1002,6 +1070,7 @@
 plumbaudio(void *)
 {
 	int i, f, pf;
+	Playlist *p;
 	Plumbmsg *m;
 	char *s, *e;
 
@@ -1013,16 +1082,18 @@
 				s = smprint("%s/%s", m->wdir, m->data);
 
 			if((e = strrchr(s, '.')) != nil && strcmp(e, ".plist") == 0 && (pf = open(s, OREAD)) >= 0){
-				stop(playercurr);
-				free(pl);
-				free(plraw);
-				plnum = 0;
-				readplist(pf);
+				p = readplist(pf);
 				close(pf);
+				if(p == nil)
+					continue;
+
+				freeplist(pl);
+				pl = p;
+				memset(mincolwidth, 0, sizeof(mincolwidth)); /* readjust columns */
 				sendul(playc, 0);
 			}else{
-				for(i = 0; i < plnum; i++){
-					if(strcmp(pl[i].path, s) == 0){
+				for(i = 0; i < pl->n; i++){
+					if(strcmp(pl->m[i].path, s) == 0){
 						sendul(playc, i);
 						break;
 					}
@@ -1051,8 +1122,13 @@
 	Rune key;
 	Mouse m;
 	ulong ind;
-	Alt a[] =
-	{
+	enum {
+		Emouse,
+		Eresize,
+		Ekey,
+		Eplay,
+	};
+	Alt a[] = {
 		{ nil, &m, CHANRCV },
 		{ nil, nil, CHANRCV },
 		{ nil, &key, CHANRCV },
@@ -1083,12 +1159,11 @@
 		break;
 	}ARGEND;
 
-	readplist(0);
-	close(0);
-	if(plnum < 1){
-		fprint(2, "empty playlist\n");
-		sysfatal("empty");
+	if((pl = readplist(0)) == nil){
+		fprint(2, "playlist: %r\n");
+		sysfatal("playlist error");
 	}
+	close(0);
 
 	if(nogui){
 		Binit(&out, 1, OWRITE);
@@ -1122,7 +1197,7 @@
 	threadsetname("zuke");
 
 	if(shuffled){
-		pcur = nrand(plnum);
+		pcur = nrand(pl->n);
 		toggleshuffle();
 		recenter();
 	}
@@ -1142,7 +1217,7 @@
 			redraw(0);
 
 		switch(alt(a)){
-		case 0:
+		case Emouse:
 			if(ptinrect(m.xy, seekbar)){
 				seekoff = getmeta(pcurplaying)->duration * (double)(m.xy.x-1-seekbar.min.x) / (double)Dx(seekbar);
 				if(seekoff < 0)
@@ -1161,7 +1236,7 @@
 				redraw(1);
 				break;
 			}else if(m.buttons == 16){
-				scroll = MIN(scroll+scrollsz/4+1, plnum-scrollsz-1);
+				scroll = MIN(scroll+scrollsz/4+1, pl->n-scrollsz-1);
 				redraw(1);
 				break;
 			}
@@ -1181,7 +1256,7 @@
 					redraw(1);
 					break;
 				}else if(m.buttons == 4){
-					scroll = MIN(scroll+n+1, plnum-scrollsz-1);
+					scroll = MIN(scroll+n+1, pl->n-scrollsz-1);
 					redraw(1);
 					break;
 				}else if(m.buttons == 2){
@@ -1199,14 +1274,14 @@
 			}
 
 			if(scrolling){
-				if(scrollsz >= plnum)
+				if(scrollsz >= pl->n)
 					break;
-				scroll = (m.xy.y - screen->r.min.y - Scrollheight/4)*(plnum-scrollsz) / (Dy(screen->r)-Scrollheight/2);
-				scroll = CLAMP(scroll, 0, plnum-scrollsz-1);
+				scroll = (m.xy.y - screen->r.min.y - Scrollheight/4)*(pl->n-scrollsz) / (Dy(screen->r)-Scrollheight/2);
+				scroll = CLAMP(scroll, 0, pl->n-scrollsz-1);
 				redraw(1);
 			}else if(m.buttons == 1 || m.buttons == 2){
 				n += scroll;
-				if(n < plnum){
+				if(n < pl->n){
 					pcur = n;
 					if(m.buttons == 2){
 						stop(playercurr);
@@ -1216,12 +1291,12 @@
 				}
 			}
 			break;
-		case 1: /* resize */
+		case Eresize: /* resize */
 			if(getwindow(display, Refnone) < 0)
 				sysfatal("getwindow: %r");
 			redraw(1);
 			break;
-		case 2:
+		case Ekey:
 			switch(key){
 			case Kleft:
 				seekrel(playercurr, -(double)Seek);
@@ -1248,7 +1323,7 @@
 				pcur += scrollsz;
 				break;
 			case Kend:
-				pcur = plnum-1;
+				pcur = pl->n-1;
 				break;
 			case Khome:
 				pcur = 0;
@@ -1275,7 +1350,7 @@
 				if(playercurr == nil)
 					break;
 				pnew = pcurplaying;
-				if(++pnew >= plnum)
+				if(++pnew >= pl->n)
 					pnew = 0;
 				stop(playercurr);
 				playercurr = newplayer(pnew, 1);
@@ -1288,7 +1363,7 @@
 					break;
 				pnew = pcurplaying;
 				if(--pnew < 0)
-					pnew = plnum-1;
+					pnew = pl->n-1;
 				stop(playercurr);
 				playercurr = newplayer(pnew, 1);
 				start(playercurr);
@@ -1329,18 +1404,21 @@
 				break;
 			}
 			break;
-		case 3:
+		case Eplay:
 			pcur = ind;
-			goto playcur;
+			recenter();
+			if(playercurr != nil)
+				goto playcur;
+			break;
 		}
 
 		if(pcur != oldpcur){
-			pcur = CLAMP(pcur, 0, plnum-1);
+			pcur = CLAMP(pcur, 0, pl->n-1);
 			if(pcur < scroll)
 				scroll = pcur;
 			else if(pcur > scroll + scrollsz)
 				scroll = pcur - scrollsz;
-			scroll = CLAMP(scroll, 0, plnum-scrollsz);
+			scroll = CLAMP(scroll, 0, pl->n-scrollsz);
 
 			if(pcur != oldpcur)
 				redraw(1);