aboutsummaryrefslogtreecommitdiff
path: root/src/xrdb_parse.c
blob: 47018106268106c5f0a9f8ed34673f8117f80825 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#ifdef __unix__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "xrdb_parse.h"

extern char **environ;

static int join_path(char buf[], int buf_size, const char *path, const char *name) {
  const char *path_delim = strchr(path, ':');
  if (!path_delim) {
    path_delim = path + strlen(path);
  }
  const int path_len = path_delim - path, name_len = strlen(name);
  if (buf_size < path_len + 1 + name_len + 1) return -1;
  memcpy(buf, path, path_len);
  buf[path_len] = '/';
  memcpy(buf + path_len + 1, name, name_len + 1);
  return 0;
}


static int xrdb_popen (int *pid_ptr) {
  int fd[2];

  if (pipe(fd) != 0) return -1;
  int read_fd = fd[0];
  int write_fd = fd[1];

  int pid = fork();
  if (pid == 0) {
    close(read_fd);
    dup2(write_fd, STDOUT_FILENO);
    close(write_fd);
    char *path = getenv("PATH");
    char xrdb_filename[256];
    while (path) {
      char *xrgb_argv[3] = {"xrdb", "-query", NULL};
      if (join_path(xrdb_filename, 256, path, "xrdb") == 0) {
        execve(xrdb_filename, xrgb_argv, environ);
      }
      path = strchr(path, ':');
      if (path) {
        path++;
      }
    }
  } else {
    *pid_ptr = pid;
    close(write_fd);
    return read_fd;
  }
  return -1;
}


static int xrdb_try_dpi_match(const char *line, int len) {
  if (len >= 8 && strncmp(line, "Xft.dpi:", 8) == 0) {
    for (line += 8; *line && (*line == '\t' || *line == ' '); line++) { }
    int dpi = 0;
    for ( ; *line >= '0' && *line <= '9'; line++) {
      dpi = dpi * 10 + ((int) (*line) - (int) '0');
    }
    return dpi;
  }
  return -1;
}

static void xrdb_complete_reading(int read_fd, char *buf, size_t buf_size) {
  while (1) {
    int nr = read(read_fd, buf, buf_size);
    if (nr <= 0) return;
  }
}

#define XRDB_READ_BUF_SIZE 256
static int xrdb_parse_for_dpi(int read_fd) {
  char buf[XRDB_READ_BUF_SIZE];
  char buf_remaining = 0;
  while (1) {
    int nr = read(read_fd, buf + buf_remaining, XRDB_READ_BUF_SIZE - buf_remaining);
    if (nr > 0) {
      const char * const buf_limit = buf + nr + buf_remaining;
      char *line_start = buf;
      while (line_start < buf_limit) {
        char *nlptr = line_start;
        while (nlptr < buf_limit && *nlptr != '\n') {
          nlptr++;
        }
        if (nlptr < buf_limit) {
          int dpi_read = xrdb_try_dpi_match(line_start, nlptr - line_start);
          if (dpi_read > 0) {
            xrdb_complete_reading(read_fd, buf, XRDB_READ_BUF_SIZE);
            return dpi_read;
          }
          /* doesn't match: position after newline to search again */
          line_start = nlptr + 1;
        } else {
          /* No newline found: copy the remaining part at the beginning of buf
             and read again from file descriptor. */
          buf_remaining = buf_limit - line_start;
          if (buf_remaining >= XRDB_READ_BUF_SIZE) {
            /* Line is too long for the buffer: skip. */
            buf_remaining = 0;
          } else {
            memmove(buf, line_start, buf_remaining);
          }
          break;
        }
      }
    } else {
      if (nr == 0 && buf_remaining > 0) {
        int dpi_read = xrdb_try_dpi_match(buf, buf_remaining);
        if (dpi_read > 0) {
          return dpi_read;
        }
      }
      break;
    }
  }
  return -1;
}

int xrdb_find_dpi() {
  int xrdb_pid;
  int read_fd = xrdb_popen(&xrdb_pid);
  if (read_fd >= 0) {
    int dpi_read = xrdb_parse_for_dpi(read_fd);
    int child_status;
    waitpid(xrdb_pid, &child_status, 0);
    close(read_fd);
    return dpi_read;
  }
  return -1;
}

#endif