aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/main.c
diff options
context:
space:
mode:
authorLibravatar progandy <code@progandy>2015-12-24 01:22:29 +0100
committerLibravatar progandy <code@progandy>2015-12-24 13:20:37 +0100
commit21541e9e640afc630a32d79655e8764b7ec3cc42 (patch)
tree995206a86421f624a55099159561ebc2ce74cdaa /swaybar/main.c
parentswaybar: fix array indices in i3json_handle (diff)
downloadsway-21541e9e640afc630a32d79655e8764b7ec3cc42.tar.gz
sway-21541e9e640afc630a32d79655e8764b7ec3cc42.tar.zst
sway-21541e9e640afc630a32d79655e8764b7ec3cc42.zip
swaybar: Replace fgets with read and own buffer
Diffstat (limited to 'swaybar/main.c')
-rw-r--r--swaybar/main.c221
1 files changed, 167 insertions, 54 deletions
diff --git a/swaybar/main.c b/swaybar/main.c
index 110904ba..86ccfbb9 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -1,3 +1,4 @@
1#include <fcntl.h>
1#include <stdio.h> 2#include <stdio.h>
2#include <stdlib.h> 3#include <stdlib.h>
3#include <string.h> 4#include <string.h>
@@ -60,9 +61,9 @@ list_t *status_line = NULL;
60list_t *workspaces = NULL; 61list_t *workspaces = NULL;
61int socketfd; 62int socketfd;
62pid_t pid; 63pid_t pid;
63int pipefd[2]; 64int status_read_fd;
64FILE *command;
65char line[1024]; 65char line[1024];
66char line_rest[1024];
66char *output, *status_command; 67char *output, *status_command;
67struct registry *registry; 68struct registry *registry;
68struct window *window; 69struct window *window;
@@ -110,10 +111,11 @@ struct {
110 int bufsize; 111 int bufsize;
111 char *buffer; 112 char *buffer;
112 char *line_start; 113 char *line_start;
114 char *parserpos;
113 bool escape; 115 bool escape;
114 int depth; 116 int depth;
115 int state[I3JSON_MAXDEPTH+1]; 117 int state[I3JSON_MAXDEPTH+1];
116} i3json_state = { 0, NULL, NULL, false, 0, { I3JSON_UNKNOWN } }; 118} i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } };
117 119
118 120
119void swaybar_teardown() { 121void swaybar_teardown() {
@@ -122,8 +124,8 @@ void swaybar_teardown() {
122 registry_teardown(registry); 124 registry_teardown(registry);
123 } 125 }
124 126
125 if (command) { 127 if (status_read_fd) {
126 fclose(command); 128 close(status_read_fd);
127 } 129 }
128 130
129 if (pid) { 131 if (pid) {
@@ -137,8 +139,8 @@ void swaybar_teardown() {
137 } 139 }
138 } 140 }
139 141
140 if (pipefd[0]) { 142 if (status_read_fd) {
141 close(pipefd[0]); 143 close(status_read_fd);
142 } 144 }
143} 145}
144 146
@@ -607,34 +609,124 @@ void parse_json(const char *text) {
607 json_object_put(results); 609 json_object_put(results);
608} 610}
609 611
610int i3json_handle(FILE *file) { 612// Read line from file descriptor, only show the line tail if it is too long.
611 char *c; 613// In non-blocking mode treat "no more data" as a linebreak.
612 int handled = 0; 614// If data after a line break has been read, return it in rest.
613 // handle partially buffered data 615// If rest is non-empty, then use that as the start of the next line.
614 // make sure 1023+1 bytes at the end are free 616int read_line_tail(int fd, char *buf, int nbyte, char *rest) {
615 if (i3json_state.line_start) { 617 if (fd < 0 || !buf || !nbyte) {
616 int len = strlen(i3json_state.line_start); 618 return -1;
617 memmove(i3json_state.buffer, i3json_state.line_start, len+1); 619 }
618 if (i3json_state.bufsize < len+1024) { 620 int l;
619 i3json_state.bufsize += 1024; 621 char *buffer = malloc(nbyte*2+1);
620 i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); 622 char *readpos = buffer;
623 char *lf;
624 // prepend old data to new line if necessary
625 if (rest) {
626 l = strlen(rest);
627 if (l > nbyte) {
628 strcpy(buffer, rest + l - nbyte);
629 readpos += nbyte;
630 } else if (l) {
631 strcpy(buffer, rest);
632 readpos += l;
633 }
634 }
635 // read until a linefeed is found or no more data is available
636 while ((l = read(fd, readpos, nbyte)) > 0) {
637 readpos[l] = '\0';
638 lf = strchr(readpos, '\n');
639 if (lf) {
640 // linefeed found, replace with \0
641 *lf = '\0';
642 // give data from the end of the line, try to fill the buffer
643 if (lf-buffer > nbyte) {
644 strcpy(buf, lf - nbyte + 1);
645 } else {
646 strcpy(buf, buffer);
647 }
648 // we may have read data from the next line, save it to rest
649 if (rest) {
650 rest[0] = '\0';
651 strcpy(rest, lf + 1);
652 }
653 free(buffer);
654 return strlen(buf);
655 } else {
656 // no linefeed found, slide data back.
657 int overflow = readpos - buffer + l - nbyte;
658 if (overflow > 0) {
659 memmove(buffer, buffer + overflow , nbyte + 1);
660 }
621 } 661 }
622 c = i3json_state.buffer+len; 662 }
623 i3json_state.line_start = i3json_state.buffer; 663 if (l < 0) {
624 } else if (!i3json_state.buffer) { 664 free(buffer);
625 i3json_state.buffer = malloc(1024); 665 return l;
626 i3json_state.bufsize = 1024; 666 }
627 c = i3json_state.buffer; 667 readpos[l]='\0';
668 if (rest) {
669 rest[0] = '\0';
670 }
671 if (nbyte < readpos - buffer + l - 1) {
672 memcpy(buf, readpos - nbyte + l + 1, nbyte);
628 } else { 673 } else {
629 c = i3json_state.buffer; 674 strncpy(buf, buffer, nbyte);
675 }
676 buf[nbyte-1] = '\0';
677 free(buffer);
678 return strlen(buf);
679}
680
681// make sure that enough buffer space is available starting from parserpos
682void i3json_ensure_free(int min_free) {
683 int _step = 10240;
684 int r = min_free % _step;
685 if (r) {
686 min_free += _step - r;
687 }
688 if (!i3json_state.buffer) {
689 i3json_state.buffer = malloc(min_free);
690 i3json_state.bufsize = min_free;
691 i3json_state.parserpos = i3json_state.buffer;
692 } else {
693 int len = 0;
694 int pos = 0;
695 if (i3json_state.line_start) {
696 len = strlen(i3json_state.line_start);
697 pos = i3json_state.parserpos - i3json_state.line_start;
698 if (i3json_state.line_start != i3json_state.buffer) {
699 memmove(i3json_state.buffer, i3json_state.line_start, len+1);
700 }
701 } else {
702 len = strlen(i3json_state.buffer);
703 }
704 if (i3json_state.bufsize < len+min_free) {
705 i3json_state.bufsize += min_free;
706 if (i3json_state.bufsize > 1024000) {
707 sway_abort("Status line json too long or malformed.");
708 }
709 i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize);
710 if (!i3json_state.buffer) {
711 sway_abort("Could not allocate json buffer");
712 }
713 }
714 if (i3json_state.line_start) {
715 i3json_state.line_start = i3json_state.buffer;
716 i3json_state.parserpos = i3json_state.buffer + pos;
717 } else {
718 i3json_state.parserpos = i3json_state.buffer;
719 }
630 } 720 }
631 if (!i3json_state.buffer) { 721 if (!i3json_state.buffer) {
632 sway_abort("Could not allocate buffer."); 722 sway_abort("Could not allocate buffer.");
633 } 723 }
634 // get fresh data at the end of the buffer 724}
635 if (!fgets(c, 1023, file)) return -1;
636 c[1023] = '\0';
637 725
726// continue parsing from last parserpos
727int i3json_parse() {
728 char *c = i3json_state.parserpos;
729 int handled = 0;
638 while (*c) { 730 while (*c) {
639 if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) { 731 if (i3json_state.state[i3json_state.depth] == I3JSON_STRING) {
640 if (!i3json_state.escape && *c == '"') { 732 if (!i3json_state.escape && *c == '"') {
@@ -679,9 +771,30 @@ int i3json_handle(FILE *file) {
679 } 771 }
680 ++c; 772 ++c;
681 } 773 }
774 i3json_state.parserpos = c;
682 return handled; 775 return handled;
683} 776}
684 777
778// append data and parse it.
779int i3json_handle_data(char *data) {
780 int len = strlen(data);
781 i3json_ensure_free(len);
782 strcpy(i3json_state.parserpos, data);
783 return i3json_parse();
784}
785
786// read data from fd and parse it.
787int i3json_handle_fd(int fd) {
788 i3json_ensure_free(10240);
789 // get fresh data at the end of the buffer
790 int readlen = read(fd, i3json_state.parserpos, 10239);
791 if (readlen < 0) {
792 return readlen;
793 }
794 i3json_state.parserpos[readlen] = '\0';
795 return i3json_parse();
796}
797
685void poll_for_update() { 798void poll_for_update() {
686 fd_set readfds; 799 fd_set readfds;
687 int activity; 800 int activity;
@@ -698,7 +811,7 @@ void poll_for_update() {
698 dirty = false; 811 dirty = false;
699 FD_ZERO(&readfds); 812 FD_ZERO(&readfds);
700 FD_SET(socketfd, &readfds); 813 FD_SET(socketfd, &readfds);
701 FD_SET(pipefd[0], &readfds); 814 FD_SET(status_read_fd, &readfds);
702 815
703 activity = select(FD_SETSIZE, &readfds, NULL, NULL, NULL); 816 activity = select(FD_SETSIZE, &readfds, NULL, NULL, NULL);
704 if (activity < 0) { 817 if (activity < 0) {
@@ -714,41 +827,40 @@ void poll_for_update() {
714 dirty = true; 827 dirty = true;
715 } 828 }
716 829
717 if (status_command && FD_ISSET(pipefd[0], &readfds)) { 830 if (status_command && FD_ISSET(status_read_fd, &readfds)) {
718 sway_log(L_DEBUG, "Got update from status command."); 831 sway_log(L_DEBUG, "Got update from status command.");
719 int linelen;
720 switch (protocol) { 832 switch (protocol) {
721 case I3BAR: 833 case I3BAR:
722 sway_log(L_DEBUG, "Got i3bar protocol."); 834 sway_log(L_DEBUG, "Got i3bar protocol.");
723 if (i3json_handle(command) > 0) { 835 if (i3json_handle_fd(status_read_fd) > 0) {
724 dirty = true; 836 dirty = true;
725 } 837 }
726 break; 838 break;
727 case TEXT: 839 case TEXT:
728 case UNDEF:
729 sway_log(L_DEBUG, "Got text protocol."); 840 sway_log(L_DEBUG, "Got text protocol.");
730 fgets(line, sizeof(line), command); 841 read_line_tail(status_read_fd, line, sizeof(line), line_rest);
731 linelen = strlen(line) - 1; 842 dirty = true;
732 if (line[linelen] == '\n') { 843 break;
733 line[linelen] = '\0'; 844 case UNDEF:
845 sway_log(L_DEBUG, "Detecting protocol...");
846 if (read_line_tail(status_read_fd, line, sizeof(line), line_rest) < 0) {
847 break;
734 } 848 }
735 dirty = true; 849 dirty = true;
736 if (protocol == UNDEF) { 850 protocol = TEXT;
737 protocol = TEXT; 851 if (line[0] == '{') {
738 if (line[0] == '{') { 852 // detect i3bar json protocol
739 // detect i3bar json protocol 853 json_object *proto = json_tokener_parse(line);
740 json_object *proto = json_tokener_parse(line); 854 json_object *version;
741 json_object *version; 855 if (proto) {
742 if (proto) { 856 if (json_object_object_get_ex(proto, "version", &version)
743 if (json_object_object_get_ex(proto, "version", &version) 857 && json_object_get_int(version) == 1
744 && json_object_get_int(version) == 1 858 ) {
745 ) { 859 sway_log(L_DEBUG, "Switched to i3bar protocol.");
746 sway_log(L_DEBUG, "Switched to i3bar protocol."); 860 protocol = I3BAR;
747 protocol = I3BAR; 861 i3json_handle_data(line_rest);
748 line[0] = '\0';
749 }
750 json_object_put(proto);
751 } 862 }
863 json_object_put(proto);
752 } 864 }
753 } 865 }
754 break; 866 break;
@@ -831,6 +943,7 @@ int main(int argc, char **argv) {
831 bar_ipc_init(desired_output, bar_id); 943 bar_ipc_init(desired_output, bar_id);
832 944
833 if (status_command) { 945 if (status_command) {
946 int pipefd[2];
834 pipe(pipefd); 947 pipe(pipefd);
835 pid = fork(); 948 pid = fork();
836 if (pid == 0) { 949 if (pid == 0) {
@@ -848,8 +961,8 @@ int main(int argc, char **argv) {
848 } 961 }
849 962
850 close(pipefd[1]); 963 close(pipefd[1]);
851 command = fdopen(pipefd[0], "r"); 964 status_read_fd = pipefd[0];
852 setbuf(command, NULL); 965 fcntl(status_read_fd, F_SETFL, O_NONBLOCK);
853 line[0] = '\0'; 966 line[0] = '\0';
854 } 967 }
855 968