io-console 0.4.7-x64-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d0d7feada83f3628959f3070c85ae4ed74317c0d8c5a7f27c198ab0bc22c3fe5
4
+ data.tar.gz: 744a9bbb6129118c2e24e1065fde4aa2618fc5d14a2d247e188d06802eeb9168
5
+ SHA512:
6
+ metadata.gz: 0a536b75cf29c4d4fdc6e78dc9b3d77380caf9b165ba2a3b55210dbc1051de9883ae8b3b352ab4eafc132da3fbfffa2d354fe81e6b4b943071f75009592bb694
7
+ data.tar.gz: 0d0f3cb369529430127d19584a7a8f701d77b41c57f0ddf4c622e0e21e2288c651d3591dd9cca8de4e149bc7649f92b14be38e92ee47214f35bbfb28348f7edf
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
@@ -0,0 +1,46 @@
1
+ # IO.console
2
+
3
+ Add console capabilities to IO instances.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'io-console'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install io-console
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ require 'io/console'
25
+
26
+ IO.console -> #<File:/dev/tty>
27
+ IO.console(sym, *args)
28
+ ```
29
+
30
+ Returns an File instance opened console.
31
+
32
+ If `sym` is given, it will be sent to the opened console with `args` and the result will be returned instead of the console IO itself.
33
+
34
+ ## Development
35
+
36
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
37
+
38
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
39
+
40
+ ## Contributing
41
+
42
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/io-console.
43
+
44
+ ## License
45
+
46
+ The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause).
@@ -0,0 +1,1002 @@
1
+ /* -*- c-file-style: "ruby" -*- */
2
+ /*
3
+ * console IO module
4
+ */
5
+ #include "ruby.h"
6
+ #include "ruby/io.h"
7
+
8
+ #ifdef HAVE_UNISTD_H
9
+ #include <unistd.h>
10
+ #endif
11
+ #ifdef HAVE_FCNTL_H
12
+ #include <fcntl.h>
13
+ #endif
14
+ #ifdef HAVE_SYS_IOCTL_H
15
+ #include <sys/ioctl.h>
16
+ #endif
17
+
18
+ #if defined HAVE_TERMIOS_H
19
+ # include <termios.h>
20
+ typedef struct termios conmode;
21
+
22
+ static int
23
+ setattr(int fd, conmode *t)
24
+ {
25
+ while (tcsetattr(fd, TCSAFLUSH, t)) {
26
+ if (errno != EINTR) return 0;
27
+ }
28
+ return 1;
29
+ }
30
+ # define getattr(fd, t) (tcgetattr(fd, t) == 0)
31
+ #elif defined HAVE_TERMIO_H
32
+ # include <termio.h>
33
+ typedef struct termio conmode;
34
+ # define setattr(fd, t) (ioctl(fd, TCSETAF, t) == 0)
35
+ # define getattr(fd, t) (ioctl(fd, TCGETA, t) == 0)
36
+ #elif defined HAVE_SGTTY_H
37
+ # include <sgtty.h>
38
+ typedef struct sgttyb conmode;
39
+ # ifdef HAVE_STTY
40
+ # define setattr(fd, t) (stty(fd, t) == 0)
41
+ # else
42
+ # define setattr(fd, t) (ioctl((fd), TIOCSETP, (t)) == 0)
43
+ # endif
44
+ # ifdef HAVE_GTTY
45
+ # define getattr(fd, t) (gtty(fd, t) == 0)
46
+ # else
47
+ # define getattr(fd, t) (ioctl((fd), TIOCGETP, (t)) == 0)
48
+ # endif
49
+ #elif defined _WIN32
50
+ #include <winioctl.h>
51
+ typedef DWORD conmode;
52
+
53
+ #define LAST_ERROR rb_w32_map_errno(GetLastError())
54
+ #define SET_LAST_ERROR (errno = LAST_ERROR, 0)
55
+
56
+ static int
57
+ setattr(int fd, conmode *t)
58
+ {
59
+ int x = SetConsoleMode((HANDLE)rb_w32_get_osfhandle(fd), *t);
60
+ if (!x) errno = LAST_ERROR;
61
+ return x;
62
+ }
63
+
64
+ static int
65
+ getattr(int fd, conmode *t)
66
+ {
67
+ int x = GetConsoleMode((HANDLE)rb_w32_get_osfhandle(fd), t);
68
+ if (!x) errno = LAST_ERROR;
69
+ return x;
70
+ }
71
+ #endif
72
+ #ifndef SET_LAST_ERROR
73
+ #define SET_LAST_ERROR (0)
74
+ #endif
75
+
76
+ static ID id_getc, id_console, id_close, id_min, id_time;
77
+ #if ENABLE_IO_GETPASS
78
+ static ID id_gets;
79
+ #endif
80
+
81
+ #ifndef HAVE_RB_F_SEND
82
+ static ID id___send__;
83
+
84
+ static VALUE
85
+ rb_f_send(int argc, VALUE *argv, VALUE recv)
86
+ {
87
+ VALUE sym = argv[0];
88
+ ID vid = rb_check_id(&sym);
89
+ if (vid) {
90
+ --argc;
91
+ ++argv;
92
+ }
93
+ else {
94
+ vid = id___send__;
95
+ }
96
+ return rb_funcallv(recv, vid, argc, argv);
97
+ }
98
+ #endif
99
+
100
+ typedef struct {
101
+ int vmin;
102
+ int vtime;
103
+ } rawmode_arg_t;
104
+
105
+ static rawmode_arg_t *
106
+ rawmode_opt(int argc, VALUE *argv, rawmode_arg_t *opts)
107
+ {
108
+ rawmode_arg_t *optp = NULL;
109
+ VALUE vopts;
110
+ rb_scan_args(argc, argv, "0:", &vopts);
111
+ if (!NIL_P(vopts)) {
112
+ VALUE vmin = rb_hash_aref(vopts, ID2SYM(id_min));
113
+ VALUE vtime = rb_hash_aref(vopts, ID2SYM(id_time));
114
+ /* default values by `stty raw` */
115
+ opts->vmin = 1;
116
+ opts->vtime = 0;
117
+ if (!NIL_P(vmin)) {
118
+ opts->vmin = NUM2INT(vmin);
119
+ optp = opts;
120
+ }
121
+ if (!NIL_P(vtime)) {
122
+ VALUE v10 = INT2FIX(10);
123
+ vtime = rb_funcall3(vtime, '*', 1, &v10);
124
+ opts->vtime = NUM2INT(vtime);
125
+ optp = opts;
126
+ }
127
+ }
128
+ return optp;
129
+ }
130
+
131
+ static void
132
+ set_rawmode(conmode *t, void *arg)
133
+ {
134
+ #ifdef HAVE_CFMAKERAW
135
+ cfmakeraw(t);
136
+ t->c_lflag &= ~(ECHOE|ECHOK);
137
+ #elif defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
138
+ t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
139
+ t->c_oflag &= ~OPOST;
140
+ t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN);
141
+ t->c_cflag &= ~(CSIZE|PARENB);
142
+ t->c_cflag |= CS8;
143
+ #elif defined HAVE_SGTTY_H
144
+ t->sg_flags &= ~ECHO;
145
+ t->sg_flags |= RAW;
146
+ #elif defined _WIN32
147
+ *t = 0;
148
+ #endif
149
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
150
+ if (arg) {
151
+ const rawmode_arg_t *r = arg;
152
+ if (r->vmin >= 0) t->c_cc[VMIN] = r->vmin;
153
+ if (r->vtime >= 0) t->c_cc[VTIME] = r->vtime;
154
+ }
155
+ #endif
156
+ }
157
+
158
+ static void
159
+ set_cookedmode(conmode *t, void *arg)
160
+ {
161
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
162
+ t->c_iflag |= (BRKINT|ISTRIP|ICRNL|IXON);
163
+ t->c_oflag |= OPOST;
164
+ t->c_lflag |= (ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN);
165
+ #elif defined HAVE_SGTTY_H
166
+ t->sg_flags |= ECHO;
167
+ t->sg_flags &= ~RAW;
168
+ #elif defined _WIN32
169
+ *t |= ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT;
170
+ #endif
171
+ }
172
+
173
+ static void
174
+ set_noecho(conmode *t, void *arg)
175
+ {
176
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
177
+ t->c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
178
+ #elif defined HAVE_SGTTY_H
179
+ t->sg_flags &= ~ECHO;
180
+ #elif defined _WIN32
181
+ *t &= ~ENABLE_ECHO_INPUT;
182
+ #endif
183
+ }
184
+
185
+ static void
186
+ set_echo(conmode *t, void *arg)
187
+ {
188
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
189
+ t->c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL);
190
+ #elif defined HAVE_SGTTY_H
191
+ t->sg_flags |= ECHO;
192
+ #elif defined _WIN32
193
+ *t |= ENABLE_ECHO_INPUT;
194
+ #endif
195
+ }
196
+
197
+ static int
198
+ echo_p(conmode *t)
199
+ {
200
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
201
+ return (t->c_lflag & (ECHO | ECHONL)) != 0;
202
+ #elif defined HAVE_SGTTY_H
203
+ return (t->sg_flags & ECHO) != 0;
204
+ #elif defined _WIN32
205
+ return (*t & ENABLE_ECHO_INPUT) != 0;
206
+ #endif
207
+ }
208
+
209
+ static int
210
+ set_ttymode(int fd, conmode *t, void (*setter)(conmode *, void *), void *arg)
211
+ {
212
+ conmode r;
213
+ if (!getattr(fd, t)) return 0;
214
+ r = *t;
215
+ setter(&r, arg);
216
+ return setattr(fd, &r);
217
+ }
218
+
219
+ #define GetReadFD(fptr) ((fptr)->fd)
220
+
221
+ static inline int
222
+ get_write_fd(const rb_io_t *fptr)
223
+ {
224
+ VALUE wio = fptr->tied_io_for_writing;
225
+ rb_io_t *ofptr;
226
+ if (!wio) return fptr->fd;
227
+ GetOpenFile(wio, ofptr);
228
+ return ofptr->fd;
229
+ }
230
+ #define GetWriteFD(fptr) get_write_fd(fptr)
231
+
232
+ #define FD_PER_IO 2
233
+
234
+ static VALUE
235
+ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void *arg)
236
+ {
237
+ rb_io_t *fptr;
238
+ int status = -1;
239
+ int error = 0;
240
+ int fd[FD_PER_IO];
241
+ conmode t[FD_PER_IO];
242
+ VALUE result = Qnil;
243
+
244
+ GetOpenFile(io, fptr);
245
+ fd[0] = GetReadFD(fptr);
246
+ if (fd[0] != -1) {
247
+ if (set_ttymode(fd[0], t+0, setter, arg)) {
248
+ status = 0;
249
+ }
250
+ else {
251
+ error = errno;
252
+ fd[0] = -1;
253
+ }
254
+ }
255
+ fd[1] = GetWriteFD(fptr);
256
+ if (fd[1] != -1 && fd[1] != fd[0]) {
257
+ if (set_ttymode(fd[1], t+1, setter, arg)) {
258
+ status = 0;
259
+ }
260
+ else {
261
+ error = errno;
262
+ fd[1] = -1;
263
+ }
264
+ }
265
+ if (status == 0) {
266
+ result = rb_protect(func, io, &status);
267
+ }
268
+ GetOpenFile(io, fptr);
269
+ if (fd[0] != -1 && fd[0] == GetReadFD(fptr)) {
270
+ if (!setattr(fd[0], t+0)) {
271
+ error = errno;
272
+ status = -1;
273
+ }
274
+ }
275
+ if (fd[1] != -1 && fd[1] != fd[0] && fd[1] == GetWriteFD(fptr)) {
276
+ if (!setattr(fd[1], t+1)) {
277
+ error = errno;
278
+ status = -1;
279
+ }
280
+ }
281
+ if (status) {
282
+ if (status == -1) {
283
+ rb_syserr_fail(error, 0);
284
+ }
285
+ rb_jump_tag(status);
286
+ }
287
+ return result;
288
+ }
289
+
290
+ /*
291
+ * call-seq:
292
+ * io.raw(min: nil, time: nil) {|io| }
293
+ *
294
+ * Yields +self+ within raw mode.
295
+ *
296
+ * STDIN.raw(&:gets)
297
+ *
298
+ * will read and return a line without echo back and line editing.
299
+ *
300
+ * The parameter +min+ specifies the minimum number of bytes that
301
+ * should be received when a read operation is performed. (default: 1)
302
+ *
303
+ * The parameter +time+ specifies the timeout in _seconds_ with a
304
+ * precision of 1/10 of a second. (default: 0)
305
+ *
306
+ * Refer to the manual page of termios for further details.
307
+ *
308
+ * You must require 'io/console' to use this method.
309
+ */
310
+ static VALUE
311
+ console_raw(int argc, VALUE *argv, VALUE io)
312
+ {
313
+ rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
314
+ return ttymode(io, rb_yield, set_rawmode, optp);
315
+ }
316
+
317
+ /*
318
+ * call-seq:
319
+ * io.raw!(min: nil, time: nil)
320
+ *
321
+ * Enables raw mode.
322
+ *
323
+ * If the terminal mode needs to be back, use io.raw { ... }.
324
+ *
325
+ * See IO#raw for details on the parameters.
326
+ *
327
+ * You must require 'io/console' to use this method.
328
+ */
329
+ static VALUE
330
+ console_set_raw(int argc, VALUE *argv, VALUE io)
331
+ {
332
+ conmode t;
333
+ rb_io_t *fptr;
334
+ int fd;
335
+ rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
336
+
337
+ GetOpenFile(io, fptr);
338
+ fd = GetReadFD(fptr);
339
+ if (!getattr(fd, &t)) rb_sys_fail(0);
340
+ set_rawmode(&t, optp);
341
+ if (!setattr(fd, &t)) rb_sys_fail(0);
342
+ return io;
343
+ }
344
+
345
+ /*
346
+ * call-seq:
347
+ * io.cooked {|io| }
348
+ *
349
+ * Yields +self+ within cooked mode.
350
+ *
351
+ * STDIN.cooked(&:gets)
352
+ *
353
+ * will read and return a line with echo back and line editing.
354
+ *
355
+ * You must require 'io/console' to use this method.
356
+ */
357
+ static VALUE
358
+ console_cooked(VALUE io)
359
+ {
360
+ return ttymode(io, rb_yield, set_cookedmode, NULL);
361
+ }
362
+
363
+ /*
364
+ * call-seq:
365
+ * io.cooked!
366
+ *
367
+ * Enables cooked mode.
368
+ *
369
+ * If the terminal mode needs to be back, use io.cooked { ... }.
370
+ *
371
+ * You must require 'io/console' to use this method.
372
+ */
373
+ static VALUE
374
+ console_set_cooked(VALUE io)
375
+ {
376
+ conmode t;
377
+ rb_io_t *fptr;
378
+ int fd;
379
+
380
+ GetOpenFile(io, fptr);
381
+ fd = GetReadFD(fptr);
382
+ if (!getattr(fd, &t)) rb_sys_fail(0);
383
+ set_cookedmode(&t, NULL);
384
+ if (!setattr(fd, &t)) rb_sys_fail(0);
385
+ return io;
386
+ }
387
+
388
+ static VALUE
389
+ getc_call(VALUE io)
390
+ {
391
+ return rb_funcallv(io, id_getc, 0, 0);
392
+ }
393
+
394
+ /*
395
+ * call-seq:
396
+ * io.getch(min: nil, time: nil) -> char
397
+ *
398
+ * Reads and returns a character in raw mode.
399
+ *
400
+ * See IO#raw for details on the parameters.
401
+ *
402
+ * You must require 'io/console' to use this method.
403
+ */
404
+ static VALUE
405
+ console_getch(int argc, VALUE *argv, VALUE io)
406
+ {
407
+ rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
408
+ return ttymode(io, getc_call, set_rawmode, optp);
409
+ }
410
+
411
+ /*
412
+ * call-seq:
413
+ * io.noecho {|io| }
414
+ *
415
+ * Yields +self+ with disabling echo back.
416
+ *
417
+ * STDIN.noecho(&:gets)
418
+ *
419
+ * will read and return a line without echo back.
420
+ *
421
+ * You must require 'io/console' to use this method.
422
+ */
423
+ static VALUE
424
+ console_noecho(VALUE io)
425
+ {
426
+ return ttymode(io, rb_yield, set_noecho, NULL);
427
+ }
428
+
429
+ /*
430
+ * call-seq:
431
+ * io.echo = flag
432
+ *
433
+ * Enables/disables echo back.
434
+ * On some platforms, all combinations of this flags and raw/cooked
435
+ * mode may not be valid.
436
+ *
437
+ * You must require 'io/console' to use this method.
438
+ */
439
+ static VALUE
440
+ console_set_echo(VALUE io, VALUE f)
441
+ {
442
+ conmode t;
443
+ rb_io_t *fptr;
444
+ int fd;
445
+
446
+ GetOpenFile(io, fptr);
447
+ fd = GetReadFD(fptr);
448
+ if (!getattr(fd, &t)) rb_sys_fail(0);
449
+ if (RTEST(f))
450
+ set_echo(&t, NULL);
451
+ else
452
+ set_noecho(&t, NULL);
453
+ if (!setattr(fd, &t)) rb_sys_fail(0);
454
+ return io;
455
+ }
456
+
457
+ /*
458
+ * call-seq:
459
+ * io.echo? -> true or false
460
+ *
461
+ * Returns +true+ if echo back is enabled.
462
+ *
463
+ * You must require 'io/console' to use this method.
464
+ */
465
+ static VALUE
466
+ console_echo_p(VALUE io)
467
+ {
468
+ conmode t;
469
+ rb_io_t *fptr;
470
+ int fd;
471
+
472
+ GetOpenFile(io, fptr);
473
+ fd = GetReadFD(fptr);
474
+ if (!getattr(fd, &t)) rb_sys_fail(0);
475
+ return echo_p(&t) ? Qtrue : Qfalse;
476
+ }
477
+
478
+ #if defined TIOCGWINSZ
479
+ typedef struct winsize rb_console_size_t;
480
+ #define getwinsize(fd, buf) (ioctl((fd), TIOCGWINSZ, (buf)) == 0)
481
+ #define setwinsize(fd, buf) (ioctl((fd), TIOCSWINSZ, (buf)) == 0)
482
+ #define winsize_row(buf) (buf)->ws_row
483
+ #define winsize_col(buf) (buf)->ws_col
484
+ #elif defined _WIN32
485
+ typedef CONSOLE_SCREEN_BUFFER_INFO rb_console_size_t;
486
+ #define getwinsize(fd, buf) ( \
487
+ GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), (buf)) || \
488
+ SET_LAST_ERROR)
489
+ #define winsize_row(buf) ((buf)->srWindow.Bottom - (buf)->srWindow.Top + 1)
490
+ #define winsize_col(buf) (buf)->dwSize.X
491
+ #endif
492
+
493
+ #if defined TIOCGWINSZ || defined _WIN32
494
+ #define USE_CONSOLE_GETSIZE 1
495
+ #endif
496
+
497
+ #ifdef USE_CONSOLE_GETSIZE
498
+ /*
499
+ * call-seq:
500
+ * io.winsize -> [rows, columns]
501
+ *
502
+ * Returns console size.
503
+ *
504
+ * You must require 'io/console' to use this method.
505
+ */
506
+ static VALUE
507
+ console_winsize(VALUE io)
508
+ {
509
+ rb_io_t *fptr;
510
+ int fd;
511
+ rb_console_size_t ws;
512
+
513
+ GetOpenFile(io, fptr);
514
+ fd = GetWriteFD(fptr);
515
+ if (!getwinsize(fd, &ws)) rb_sys_fail(0);
516
+ return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
517
+ }
518
+
519
+ /*
520
+ * call-seq:
521
+ * io.winsize = [rows, columns]
522
+ *
523
+ * Tries to set console size. The effect depends on the platform and
524
+ * the running environment.
525
+ *
526
+ * You must require 'io/console' to use this method.
527
+ */
528
+ static VALUE
529
+ console_set_winsize(VALUE io, VALUE size)
530
+ {
531
+ rb_io_t *fptr;
532
+ rb_console_size_t ws;
533
+ #if defined _WIN32
534
+ HANDLE wh;
535
+ int newrow, newcol;
536
+ BOOL ret;
537
+ #endif
538
+ VALUE row, col, xpixel, ypixel;
539
+ const VALUE *sz;
540
+ int fd;
541
+ long sizelen;
542
+
543
+ GetOpenFile(io, fptr);
544
+ size = rb_Array(size);
545
+ if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) {
546
+ rb_raise(rb_eArgError,
547
+ "wrong number of arguments (given %ld, expected 2 or 4)",
548
+ sizelen);
549
+ }
550
+ sz = RARRAY_CONST_PTR(size);
551
+ row = sz[0], col = sz[1], xpixel = ypixel = Qnil;
552
+ if (sizelen == 4) xpixel = sz[2], ypixel = sz[3];
553
+ fd = GetWriteFD(fptr);
554
+ #if defined TIOCSWINSZ
555
+ ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
556
+ #define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
557
+ SET(row);
558
+ SET(col);
559
+ SET(xpixel);
560
+ SET(ypixel);
561
+ #undef SET
562
+ if (!setwinsize(fd, &ws)) rb_sys_fail(0);
563
+ #elif defined _WIN32
564
+ wh = (HANDLE)rb_w32_get_osfhandle(fd);
565
+ #define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
566
+ SET(row);
567
+ SET(col);
568
+ #undef SET
569
+ if (!NIL_P(xpixel)) (void)NUM2UINT(xpixel);
570
+ if (!NIL_P(ypixel)) (void)NUM2UINT(ypixel);
571
+ if (!GetConsoleScreenBufferInfo(wh, &ws)) {
572
+ rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
573
+ }
574
+ ws.dwSize.X = newcol;
575
+ ret = SetConsoleScreenBufferSize(wh, ws.dwSize);
576
+ ws.srWindow.Left = 0;
577
+ ws.srWindow.Top = 0;
578
+ ws.srWindow.Right = newcol-1;
579
+ ws.srWindow.Bottom = newrow-1;
580
+ if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
581
+ rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
582
+ }
583
+ /* retry when shrinking buffer after shrunk window */
584
+ if (!ret && !SetConsoleScreenBufferSize(wh, ws.dwSize)) {
585
+ rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
586
+ }
587
+ /* remove scrollbar if possible */
588
+ if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
589
+ rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
590
+ }
591
+ #endif
592
+ return io;
593
+ }
594
+ #endif
595
+
596
+ /*
597
+ * call-seq:
598
+ * io.iflush
599
+ *
600
+ * Flushes input buffer in kernel.
601
+ *
602
+ * You must require 'io/console' to use this method.
603
+ */
604
+ static VALUE
605
+ console_iflush(VALUE io)
606
+ {
607
+ rb_io_t *fptr;
608
+ int fd;
609
+
610
+ GetOpenFile(io, fptr);
611
+ fd = GetReadFD(fptr);
612
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
613
+ if (tcflush(fd, TCIFLUSH)) rb_sys_fail(0);
614
+ #endif
615
+ (void)fd;
616
+ return io;
617
+ }
618
+
619
+ /*
620
+ * call-seq:
621
+ * io.oflush
622
+ *
623
+ * Flushes output buffer in kernel.
624
+ *
625
+ * You must require 'io/console' to use this method.
626
+ */
627
+ static VALUE
628
+ console_oflush(VALUE io)
629
+ {
630
+ rb_io_t *fptr;
631
+ int fd;
632
+
633
+ GetOpenFile(io, fptr);
634
+ fd = GetWriteFD(fptr);
635
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
636
+ if (tcflush(fd, TCOFLUSH)) rb_sys_fail(0);
637
+ #endif
638
+ (void)fd;
639
+ return io;
640
+ }
641
+
642
+ /*
643
+ * call-seq:
644
+ * io.ioflush
645
+ *
646
+ * Flushes input and output buffers in kernel.
647
+ *
648
+ * You must require 'io/console' to use this method.
649
+ */
650
+ static VALUE
651
+ console_ioflush(VALUE io)
652
+ {
653
+ rb_io_t *fptr;
654
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
655
+ int fd1, fd2;
656
+ #endif
657
+
658
+ GetOpenFile(io, fptr);
659
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
660
+ fd1 = GetReadFD(fptr);
661
+ fd2 = GetWriteFD(fptr);
662
+ if (fd2 != -1 && fd1 != fd2) {
663
+ if (tcflush(fd1, TCIFLUSH)) rb_sys_fail(0);
664
+ if (tcflush(fd2, TCOFLUSH)) rb_sys_fail(0);
665
+ }
666
+ else {
667
+ if (tcflush(fd1, TCIOFLUSH)) rb_sys_fail(0);
668
+ }
669
+ #endif
670
+ return io;
671
+ }
672
+
673
+ static VALUE
674
+ console_beep(VALUE io)
675
+ {
676
+ rb_io_t *fptr;
677
+ int fd;
678
+
679
+ GetOpenFile(io, fptr);
680
+ fd = GetWriteFD(fptr);
681
+ #ifdef _WIN32
682
+ (void)fd;
683
+ MessageBeep(0);
684
+ #else
685
+ if (write(fd, "\a", 1) < 0)
686
+ rb_sys_fail(0);
687
+ #endif
688
+ return io;
689
+ }
690
+
691
+ #if defined _WIN32
692
+ static VALUE
693
+ console_goto(VALUE io, VALUE x, VALUE y)
694
+ {
695
+ rb_io_t *fptr;
696
+ int fd;
697
+ COORD pos;
698
+
699
+ GetOpenFile(io, fptr);
700
+ fd = GetWriteFD(fptr);
701
+ pos.X = NUM2UINT(x);
702
+ pos.Y = NUM2UINT(y);
703
+ if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
704
+ rb_syserr_fail(LAST_ERROR, 0);
705
+ }
706
+ return io;
707
+ }
708
+
709
+ static VALUE
710
+ console_cursor_pos(VALUE io)
711
+ {
712
+ rb_io_t *fptr;
713
+ int fd;
714
+ rb_console_size_t ws;
715
+
716
+ GetOpenFile(io, fptr);
717
+ fd = GetWriteFD(fptr);
718
+ if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
719
+ rb_syserr_fail(LAST_ERROR, 0);
720
+ }
721
+ return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.X), UINT2NUM(ws.dwCursorPosition.Y));
722
+ }
723
+
724
+ static VALUE
725
+ console_cursor_set(VALUE io, VALUE cpos)
726
+ {
727
+ cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary");
728
+ if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate");
729
+ return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1));
730
+ }
731
+
732
+ #include "win32_vk.inc"
733
+
734
+ static VALUE
735
+ console_key_pressed_p(VALUE io, VALUE k)
736
+ {
737
+ int vk = -1;
738
+
739
+ if (FIXNUM_P(k)) {
740
+ vk = NUM2UINT(k);
741
+ }
742
+ else {
743
+ const struct vktable *t;
744
+ const char *kn;
745
+ if (SYMBOL_P(k)) {
746
+ k = rb_sym2str(k);
747
+ kn = RSTRING_PTR(k);
748
+ }
749
+ else {
750
+ kn = StringValuePtr(k);
751
+ }
752
+ t = console_win32_vk(kn, RSTRING_LEN(k));
753
+ if (!t || (vk = (short)t->vk) == -1) {
754
+ rb_raise(rb_eArgError, "unknown virtual key code: % "PRIsVALUE, k);
755
+ }
756
+ }
757
+ return GetKeyState(vk) & 0x80 ? Qtrue : Qfalse;
758
+ }
759
+ #else
760
+ # define console_goto rb_f_notimplement
761
+ # define console_cursor_pos rb_f_notimplement
762
+ # define console_cursor_set rb_f_notimplement
763
+ # define console_key_pressed_p rb_f_notimplement
764
+ #endif
765
+
766
+ /*
767
+ * call-seq:
768
+ * IO.console -> #<File:/dev/tty>
769
+ * IO.console(sym, *args)
770
+ *
771
+ * Returns an File instance opened console.
772
+ *
773
+ * If +sym+ is given, it will be sent to the opened console with
774
+ * +args+ and the result will be returned instead of the console IO
775
+ * itself.
776
+ *
777
+ * You must require 'io/console' to use this method.
778
+ */
779
+ static VALUE
780
+ console_dev(int argc, VALUE *argv, VALUE klass)
781
+ {
782
+ VALUE con = 0;
783
+ rb_io_t *fptr;
784
+ VALUE sym = 0;
785
+
786
+ rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS);
787
+ if (argc) {
788
+ Check_Type(sym = argv[0], T_SYMBOL);
789
+ }
790
+ if (klass == rb_cIO) klass = rb_cFile;
791
+ if (rb_const_defined(klass, id_console)) {
792
+ con = rb_const_get(klass, id_console);
793
+ if (!RB_TYPE_P(con, T_FILE) ||
794
+ (!(fptr = RFILE(con)->fptr) || GetReadFD(fptr) == -1)) {
795
+ rb_const_remove(klass, id_console);
796
+ con = 0;
797
+ }
798
+ }
799
+ if (sym) {
800
+ if (sym == ID2SYM(id_close) && argc == 1) {
801
+ if (con) {
802
+ rb_io_close(con);
803
+ rb_const_remove(klass, id_console);
804
+ con = 0;
805
+ }
806
+ return Qnil;
807
+ }
808
+ }
809
+ if (!con) {
810
+ VALUE args[2];
811
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H || defined HAVE_SGTTY_H
812
+ # define CONSOLE_DEVICE "/dev/tty"
813
+ #elif defined _WIN32
814
+ # define CONSOLE_DEVICE "con$"
815
+ # define CONSOLE_DEVICE_FOR_READING "conin$"
816
+ # define CONSOLE_DEVICE_FOR_WRITING "conout$"
817
+ #endif
818
+ #ifndef CONSOLE_DEVICE_FOR_READING
819
+ # define CONSOLE_DEVICE_FOR_READING CONSOLE_DEVICE
820
+ #endif
821
+ #ifdef CONSOLE_DEVICE_FOR_WRITING
822
+ VALUE out;
823
+ rb_io_t *ofptr;
824
+ #endif
825
+ int fd;
826
+
827
+ #ifdef CONSOLE_DEVICE_FOR_WRITING
828
+ fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
829
+ if (fd < 0) return Qnil;
830
+ rb_update_max_fd(fd);
831
+ args[1] = INT2FIX(O_WRONLY);
832
+ args[0] = INT2NUM(fd);
833
+ out = rb_class_new_instance(2, args, klass);
834
+ #endif
835
+ fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0);
836
+ if (fd < 0) {
837
+ #ifdef CONSOLE_DEVICE_FOR_WRITING
838
+ rb_io_close(out);
839
+ #endif
840
+ return Qnil;
841
+ }
842
+ rb_update_max_fd(fd);
843
+ args[1] = INT2FIX(O_RDWR);
844
+ args[0] = INT2NUM(fd);
845
+ con = rb_class_new_instance(2, args, klass);
846
+ GetOpenFile(con, fptr);
847
+ fptr->pathv = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE));
848
+ #ifdef CONSOLE_DEVICE_FOR_WRITING
849
+ GetOpenFile(out, ofptr);
850
+ ofptr->pathv = fptr->pathv;
851
+ fptr->tied_io_for_writing = out;
852
+ ofptr->mode |= FMODE_SYNC;
853
+ #endif
854
+ fptr->mode |= FMODE_SYNC;
855
+ rb_const_set(klass, id_console, con);
856
+ }
857
+ if (sym) {
858
+ return rb_f_send(argc, argv, con);
859
+ }
860
+ return con;
861
+ }
862
+
863
+ /*
864
+ * call-seq:
865
+ * io.getch(min: nil, time: nil) -> char
866
+ *
867
+ * See IO#getch.
868
+ */
869
+ static VALUE
870
+ io_getch(int argc, VALUE *argv, VALUE io)
871
+ {
872
+ return rb_funcallv(io, id_getc, argc, argv);
873
+ }
874
+
875
+ #if ENABLE_IO_GETPASS
876
+ static VALUE
877
+ puts_call(VALUE io)
878
+ {
879
+ return rb_io_write(io, rb_default_rs);
880
+ }
881
+
882
+ static VALUE
883
+ getpass_call(VALUE io)
884
+ {
885
+ return ttymode(io, rb_io_gets, set_noecho, NULL);
886
+ }
887
+
888
+ static void
889
+ prompt(int argc, VALUE *argv, VALUE io)
890
+ {
891
+ if (argc > 0 && !NIL_P(argv[0])) {
892
+ VALUE str = argv[0];
893
+ StringValueCStr(str);
894
+ rb_check_safe_obj(str);
895
+ rb_io_write(io, str);
896
+ }
897
+ }
898
+
899
+ static VALUE
900
+ str_chomp(VALUE str)
901
+ {
902
+ if (!NIL_P(str)) {
903
+ str = rb_funcallv(str, rb_intern("chomp!"), 0, 0);
904
+ }
905
+ return str;
906
+ }
907
+
908
+ /*
909
+ * call-seq:
910
+ * io.getpass(prompt=nil) -> string
911
+ *
912
+ * Reads and returns a line without echo back.
913
+ * Prints +prompt+ unless it is +nil+.
914
+ *
915
+ * You must require 'io/console' to use this method.
916
+ */
917
+ static VALUE
918
+ console_getpass(int argc, VALUE *argv, VALUE io)
919
+ {
920
+ VALUE str, wio;
921
+
922
+ rb_check_arity(argc, 0, 1);
923
+ wio = rb_io_get_write_io(io);
924
+ if (wio == io && io == rb_stdin) wio = rb_stderr;
925
+ prompt(argc, argv, wio);
926
+ str = rb_ensure(getpass_call, io, puts_call, wio);
927
+ return str_chomp(str);
928
+ }
929
+
930
+ /*
931
+ * call-seq:
932
+ * io.getpass(prompt=nil) -> string
933
+ *
934
+ * See IO#getpass.
935
+ */
936
+ static VALUE
937
+ io_getpass(int argc, VALUE *argv, VALUE io)
938
+ {
939
+ VALUE str;
940
+
941
+ rb_check_arity(argc, 0, 1);
942
+ prompt(argc, argv, io);
943
+ str = str_chomp(rb_funcallv(io, id_gets, 0, 0));
944
+ puts_call(io);
945
+ return str;
946
+ }
947
+ #endif
948
+
949
+ /*
950
+ * IO console methods
951
+ */
952
+ void
953
+ Init_console(void)
954
+ {
955
+ #undef rb_intern
956
+ id_getc = rb_intern("getc");
957
+ #if ENABLE_IO_GETPASS
958
+ id_gets = rb_intern("gets");
959
+ #endif
960
+ id_console = rb_intern("console");
961
+ id_close = rb_intern("close");
962
+ id_min = rb_intern("min");
963
+ id_time = rb_intern("time");
964
+ #ifndef HAVE_RB_F_SEND
965
+ id___send__ = rb_intern("__send__");
966
+ #endif
967
+ InitVM(console);
968
+ }
969
+
970
+ void
971
+ InitVM_console(void)
972
+ {
973
+ rb_define_method(rb_cIO, "raw", console_raw, -1);
974
+ rb_define_method(rb_cIO, "raw!", console_set_raw, -1);
975
+ rb_define_method(rb_cIO, "cooked", console_cooked, 0);
976
+ rb_define_method(rb_cIO, "cooked!", console_set_cooked, 0);
977
+ rb_define_method(rb_cIO, "getch", console_getch, -1);
978
+ rb_define_method(rb_cIO, "echo=", console_set_echo, 1);
979
+ rb_define_method(rb_cIO, "echo?", console_echo_p, 0);
980
+ rb_define_method(rb_cIO, "noecho", console_noecho, 0);
981
+ rb_define_method(rb_cIO, "winsize", console_winsize, 0);
982
+ rb_define_method(rb_cIO, "winsize=", console_set_winsize, 1);
983
+ rb_define_method(rb_cIO, "iflush", console_iflush, 0);
984
+ rb_define_method(rb_cIO, "oflush", console_oflush, 0);
985
+ rb_define_method(rb_cIO, "ioflush", console_ioflush, 0);
986
+ rb_define_method(rb_cIO, "beep", console_beep, 0);
987
+ rb_define_method(rb_cIO, "goto", console_goto, 2);
988
+ rb_define_method(rb_cIO, "cursor", console_cursor_pos, 0);
989
+ rb_define_method(rb_cIO, "cursor=", console_cursor_set, 1);
990
+ rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1);
991
+ #if ENABLE_IO_GETPASS
992
+ rb_define_method(rb_cIO, "getpass", console_getpass, -1);
993
+ #endif
994
+ rb_define_singleton_method(rb_cIO, "console", console_dev, -1);
995
+ {
996
+ VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable");
997
+ rb_define_method(mReadable, "getch", io_getch, -1);
998
+ #if ENABLE_IO_GETPASS
999
+ rb_define_method(mReadable, "getpass", io_getpass, -1);
1000
+ #endif
1001
+ }
1002
+ }