1
+ #include < node.h>
2
+ #include < v8.h>
3
+ #include < stdio.h>
4
+ #include < fcntl.h>
5
+ #include < errno.h>
6
+ #include < unistd.h>
7
+ #include < termios.h>
8
+
9
+
10
+ #define RD_EOF -1
11
+ #define RD_EIO -2
12
+
13
+
14
+ using namespace v8 ;
15
+
16
+
17
+ static inline int rd (const int fd)
18
+ {
19
+ unsigned char buffer[4 ];
20
+ ssize_t n;
21
+
22
+ while (1 ) {
23
+ n = read (fd, buffer, 1 );
24
+
25
+ if (n > (ssize_t )0 )
26
+ return buffer[0 ];
27
+
28
+ else if (n == (ssize_t )0 )
29
+ return RD_EOF;
30
+
31
+ else if (n != (ssize_t )-1 )
32
+ return RD_EIO;
33
+
34
+ else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
35
+ return RD_EIO;
36
+ }
37
+ }
38
+
39
+ static inline int wr (const int fd, const char *const data, const size_t bytes)
40
+ {
41
+ const char *head = data;
42
+ const char *const tail = data + bytes;
43
+ ssize_t n;
44
+
45
+ while (head < tail) {
46
+
47
+ n = write (fd, head, (size_t )(tail - head));
48
+
49
+ if (n > (ssize_t )0 )
50
+ head += n;
51
+
52
+ else if (n != (ssize_t )-1 )
53
+ return EIO;
54
+
55
+ else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
56
+ return errno;
57
+ }
58
+
59
+ return 0 ;
60
+ }
61
+
62
+ /* Return a new file descriptor to the current TTY. */
63
+ int current_tty (void )
64
+ {
65
+ const char *dev;
66
+ int fd;
67
+
68
+ dev = ttyname (STDIN_FILENO);
69
+
70
+ if (!dev)
71
+ dev = ttyname (STDOUT_FILENO);
72
+
73
+ if (!dev)
74
+ dev = ttyname (STDERR_FILENO);
75
+
76
+ if (!dev) {
77
+ errno = ENOTTY;
78
+ return -1 ;
79
+ }
80
+
81
+ do {
82
+ fd = open (dev, O_RDWR | O_NOCTTY);
83
+ } while (fd == -1 && errno == EINTR);
84
+
85
+ if (fd == -1 )
86
+ return -1 ;
87
+
88
+ return fd;
89
+ }
90
+
91
+ /* As the tty for current cursor position.
92
+ * This function returns 0 if success, errno code otherwise.
93
+ * Actual errno will be unchanged.
94
+ */
95
+ int cursor_position (const int tty, int *const rowptr, int *const colptr)
96
+ {
97
+ struct termios saved, temporary;
98
+ int retval, result, rows, cols, saved_errno;
99
+
100
+ /* Bad tty? */
101
+ if (tty == -1 )
102
+ return ENOTTY;
103
+
104
+ saved_errno = errno;
105
+
106
+ /* Save current terminal settings. */
107
+ do {
108
+ result = tcgetattr (tty, &saved);
109
+ } while (result == -1 && errno == EINTR);
110
+
111
+ if (result == -1 ) {
112
+ retval = errno;
113
+ errno = saved_errno;
114
+ return retval;
115
+ }
116
+
117
+ /* Get current terminal settings for basis, too. */
118
+ do {
119
+ result = tcgetattr (tty, &temporary);
120
+ } while (result == -1 && errno == EINTR);
121
+
122
+ if (result == -1 ) {
123
+ retval = errno;
124
+ errno = saved_errno;
125
+ return retval;
126
+ }
127
+
128
+
129
+ /* Disable ICANON, ECHO, and CREAD. */
130
+ temporary.c_lflag &= ~ICANON;
131
+ temporary.c_lflag &= ~ECHO;
132
+ temporary.c_cflag &= ~CREAD;
133
+
134
+ /* This loop is only executed once. When broken out,
135
+ * the terminal settings will be restored, and the function
136
+ * will return retval to caller. It's better than goto.
137
+ */
138
+ do {
139
+ /* Set modified settings. */
140
+ do {
141
+ result = tcsetattr (tty, TCSANOW, &temporary);
142
+ } while (result == -1 && errno == EINTR);
143
+
144
+ if (result == -1 ) {
145
+ retval = errno;
146
+ break ;
147
+ }
148
+
149
+ /* Request cursor coordinates from the terminal. */
150
+ retval = wr (tty, " \033 [6n" , 4 );
151
+ if (retval)
152
+ break ;
153
+
154
+ /* Assume coordinate reponse parsing fails. */
155
+ retval = EIO;
156
+
157
+ /* Expect an ESC. */
158
+ result = rd (tty);
159
+ if (result != 27 )
160
+ break ;
161
+
162
+ /* Expect [ after the ESC. */
163
+ result = rd (tty);
164
+ if (result != ' [' )
165
+ break ;
166
+
167
+ /* Parse rows. */
168
+ rows = 0 ;
169
+ result = rd (tty);
170
+ while (result >= ' 0' && result <= ' 9' ) {
171
+ rows = 10 * rows + result - ' 0' ;
172
+ result = rd (tty);
173
+ }
174
+
175
+ if (result != ' ;' )
176
+ break ;
177
+
178
+ /* Parse cols. */
179
+ cols = 0 ;
180
+ result = rd (tty);
181
+ while (result >= ' 0' && result <= ' 9' ) {
182
+ cols = 10 * cols + result - ' 0' ;
183
+ result = rd (tty);
184
+ }
185
+
186
+ if (result != ' R' )
187
+ break ;
188
+
189
+ /* Success! */
190
+
191
+ if (rowptr)
192
+ *rowptr = rows;
193
+
194
+ if (colptr)
195
+ *colptr = cols;
196
+
197
+ retval = 0 ;
198
+
199
+ } while (0 );
200
+
201
+ /* Restore saved terminal settings. */
202
+ do {
203
+ result = tcsetattr (tty, TCSANOW, &saved);
204
+ } while (result == -1 && errno == EINTR);
205
+
206
+ if (result == -1 && !retval)
207
+ retval = errno;
208
+
209
+ /* Done. */
210
+ return retval;
211
+ }
212
+
213
+ void Method (const v8::FunctionCallbackInfo<Value>& args) {
214
+ Isolate* isolate = Isolate::GetCurrent ();
215
+ HandleScope scope (isolate);
216
+
217
+ int ret, fd, row, col;
218
+
219
+ ret = 0 ;
220
+ row = 0 ;
221
+ col = 0 ;
222
+
223
+ fd = current_tty ();
224
+ if (fd == -1 )
225
+ return ;
226
+
227
+ if (cursor_position (fd, &row, &col))
228
+ return ;
229
+
230
+ if (row < 1 || col < 1 )
231
+ return ;
232
+
233
+ Local<Object> pos = Object::New (isolate);
234
+ pos->Set (String::NewFromUtf8 (isolate, " row" ), Number::New (isolate, row));
235
+ pos->Set (String::NewFromUtf8 (isolate, " col" ), Number::New (isolate, col));
236
+ args.GetReturnValue ().Set (pos);
237
+ }
238
+
239
+ void Init (Handle <Object> exports) {
240
+ Isolate* isolate = Isolate::GetCurrent ();
241
+ exports->Set (String::NewFromUtf8 (isolate, " hello" ),
242
+ FunctionTemplate::New (isolate, Method)->GetFunction ());
243
+ }
244
+
245
+ NODE_MODULE (hello, Init)
0 commit comments