Rizin
unix-like reverse engineering framework and cli tools
cutf8.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2015-2021 pancake <pancake@nopcode.org>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 // Copypasta from http://www.linuxquestions.org/questions/programming-9/get-cursor-position-in-c-947833/
5 #include <rz_cons.h>
6 #include <rz_windows.h>
7 
8 #if __UNIX__
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <termios.h>
12 #include <errno.h>
13 
14 #define RD_EOF (-1)
15 #define RD_EIO (-2)
16 
17 /* select utf8 terminal detection method */
18 #define UTF8_DETECT_ENV 1
19 #define UTF8_DETECT_LOCALE 0
20 #define UTF8_DETECT_CURSOR 0
21 
22 #if UTF8_DETECT_CURSOR
23 static inline int rd(const int fd) {
24  unsigned char buffer[4];
25  ssize_t n;
26 
27  for (;;) {
28  n = read(fd, buffer, 1);
29  if (n > (ssize_t)0) {
30  return buffer[0];
31  }
32  if (n == (ssize_t)-1) {
33  return RD_EOF;
34  }
35  if (n == (ssize_t)0) {
36  return RD_EOF;
37  }
38  if (n != (ssize_t)-1) {
39  return RD_EIO;
40  }
41  if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
42  return RD_EIO;
43  }
44  }
45 }
46 #endif
47 
48 /* Return a new file descriptor to the current TTY.
49  */
50 int current_tty(void) {
51 #if __WINDOWS__
52  return 0;
53 #elif __ANDROID__
54  return 1;
55 #else
56  int fd;
57  const char *dev = ttyname(STDERR_FILENO);
58 #if 0
59  if (!dev)
60  dev = ttyname(STDIN_FILENO);
61  if (!dev)
62  dev = ttyname(STDERR_FILENO);
63 #endif
64  if (!dev) {
65  errno = ENOTTY;
66  return -1;
67  }
68 
69  do {
70  fd = open(dev, O_RDWR | O_NOCTTY);
71  } while (fd == -1 && errno == EINTR);
72  if (fd == -1) {
73  return -1;
74  }
75  return fd;
76 #endif
77 }
78 
79 #if UTF8_DETECT_CURSOR
80 /* As the tty for current cursor position.
81  * This function returns 0 if success, errno code otherwise.
82  * Actual errno will be unchanged.
83  */
84 static int cursor_position(const int tty, int *const rowptr, int *const colptr) {
85  struct termios saved, temporary;
86  int ret, res, rows, cols, saved_errno;
87 
88  /* Bad tty? */
89  if (tty == -1)
90  return ENOTTY;
91 
92  saved_errno = errno;
93 
94  /* Save current terminal settings. */
95  res = tcgetattr(tty, &saved);
96  if (res == -1) {
97  ret = errno;
98  errno = saved_errno;
99  return ret;
100  }
101 
102  /* Get current terminal settings for basis, too. */
103  res = tcgetattr(tty, &temporary);
104  if (res == -1) {
105  ret = errno;
106  errno = saved_errno;
107  return ret;
108  }
109 
110  /* Disable ICANON, ECHO, and CREAD. */
111  temporary.c_lflag &= ~ICANON;
112  temporary.c_lflag &= ~ECHO;
113  temporary.c_cflag &= ~CREAD;
114 
115  /* This loop is only executed once. When broken out,
116  * the terminal settings will be restored, and the function
117  * will return ret to caller. It's better than goto.
118  */
119  do {
120  /* Set modified settings. */
121  res = tcsetattr(tty, TCSANOW, &temporary);
122  if (res == -1) {
123  ret = errno;
124  break;
125  }
126 
127  /* Request cursor coordinates from the terminal. */
128  ret = write(tty, "\033[6n", 4);
129  if (ret)
130  break;
131 
132  /* Assume coordinate response parsing fails. */
133  ret = EIO;
134 
135  // Read until ESC is found. previous chars, should be
136  // refeeded to the terminal after finishing
137  // that makes typing a command before the prompt
138  // appears results in no data in it.
139 
140  /* Expect an ESC. */
141  for (;;) {
142  res = rd(tty);
143  if (res == 27 || res < 1)
144  break;
145  // else store_skipped_data_from_stdin_here
146  }
147 
148  /* Expect [ after the ESC. */
149  res = rd(tty);
150  if (res != '[')
151  break;
152 
153  /* Parse rows. */
154  rows = 0;
155  res = rd(tty);
156  while (IS_DIGIT(res)) {
157  rows = 10 * rows + res - '0';
158  res = rd(tty);
159  }
160 
161  if (res != ';')
162  break;
163 
164  /* Parse cols. */
165  cols = 0;
166  res = rd(tty);
167  if (res == -1)
168  break;
169  while (IS_DIGIT(res)) {
170  cols = 10 * cols + res - '0';
171  res = rd(tty);
172  }
173 
174  if (res != 'R')
175  break;
176 
177  /* Success! */
178  if (rowptr)
179  *rowptr = rows;
180  if (colptr)
181  *colptr = cols;
182  ret = 0;
183 
184  } while (0);
185 
186  /* Restore saved terminal settings. */
187  res = tcsetattr(tty, TCSANOW, &saved);
188  if (res == -1 && !ret)
189  ret = errno;
190 
191  /* Done. */
192  return ret;
193 }
194 #endif
195 
196 // \brief Checks if the console supports and is set to UTF-8-compatible locale
197 RZ_API bool rz_cons_is_utf8(void) {
198  bool ret = false;
199 #if UTF8_DETECT_ENV
200  const char *keys[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL };
201  const char **key = keys;
202  for (; *key; key++) {
203  char *val = rz_sys_getenv(*key);
204  if (val) {
205  rz_str_case(val, false);
206  ret = strstr(val, "utf-8") || strstr(val, "utf8");
207  free(val);
208  break;
209  }
210  }
211 #endif
212 #if UTF8_DETECT_LOCALE
213 #include <locale.h>
214  const char *ctype = setlocale(LC_CTYPE, NULL);
215  if ((ctype != NULL) && (ctype = strchr(ctype, '.')) && ctype++ &&
216  (rz_str_casecmp(ctype, "UTF-8") == 0 || rz_str_casecmp(ctype, "UTF8") == 0)) {
217  return true;
218  }
219 #endif
220 #if UTF8_DETECT_CURSOR
221  int row = 0, col = 0;
222  int row2 = 0, col2 = 0;
223  int fd = current_tty();
224  if (fd == -1)
225  return false;
226  if (cursor_position(fd, &row, &col)) {
227  close(fd);
228  return false;
229  }
230  rz_xwrite(1, "\xc3\x89\xc3\xa9", 4);
231  if (cursor_position(fd, &row2, &col2)) {
232  close(fd);
233  return false;
234  }
235  close(fd);
236  rz_xwrite(1, "\r \r", 6);
237  return ((col2 - col) == 2);
238 #endif
239  return ret;
240 }
241 #else
243 #if __WINDOWS__
244  return GetConsoleOutputCP() == CP_UTF8;
245 #else
246  return true;
247 #endif
248 }
249 #endif
#define rd()
ut16 val
Definition: armass64_const.h:6
#define RZ_API
#define NULL
Definition: cris-opc.c:27
RZ_API bool rz_cons_is_utf8(void)
Definition: cutf8.c:242
static static fork write
Definition: sflib.h:33
static static fork const void static count close
Definition: sflib.h:33
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void static offset struct stat static buf void long static basep static whence static length const void static len key
Definition: sflib.h:118
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc static sig const char static mode static oldfd struct tms static buf static getgid static geteuid const char static filename static arg static mask dev
Definition: sflib.h:88
int n
Definition: mipsasm.c:19
static struct @218 keys[]
RZ_API int rz_str_casecmp(const char *dst, const char *orig)
Definition: str.c:121
RZ_API void rz_str_case(char *str, bool up)
Definition: str.c:341
#define IS_DIGIT(x)
Definition: rz_str_util.h:11
RZ_API char * rz_sys_getenv(const char *key)
Get the value of an environment variable named key or NULL if none exists.
Definition: sys.c:483
#define rz_xwrite(fd, buf, count)
Definition: rz_types.h:642
#define O_NOCTTY
Definition: sftypes.h:491
#define EINTR
Definition: sftypes.h:114
#define ICANON
Definition: sftypes.h:971
#define EIO
Definition: sftypes.h:115
#define TCSANOW
Definition: sftypes.h:998
#define CREAD
Definition: sftypes.h:944
#define ECHO
Definition: sftypes.h:973
#define O_RDWR
Definition: sftypes.h:488
#define ENOTTY
Definition: sftypes.h:135
#define EAGAIN
Definition: sftypes.h:121
int ssize_t
Definition: sftypes.h:39
Definition: buffer.h:15
uv_tty_t tty
Definition: main.c:7
#define STDERR_FILENO
Definition: private.h:45
#define STDIN_FILENO
Definition: private.h:37
static const z80_opcode fd[]
Definition: z80_tab.h:997
int read(izstream &zs, T *x, Items items)
Definition: zstream.h:115