rubysl-io-console 2.0.0

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
+ SHA1:
3
+ metadata.gz: 99fe272697f63a29d8f623c64b12f16c4b0408b6
4
+ data.tar.gz: c20e0a5aa90300cfd6f72cf2d8e0cead77347cd3
5
+ SHA512:
6
+ metadata.gz: fc90c683db12c4173c58f28075629f566a60294040a521d623f0a7337bc06fca208ca2814f0d6d8edf01634b52a65f6ab3e27cb0f98f6151f908a31363d61334
7
+ data.tar.gz: ef9a17b5c40fe28bcf1780c5dcfcd7ecc197f201eb8d1d14eae650f9788a546851ada9ab665a7b645bcdae80f14cdb648a8a851720887b27202f55caa4efc123
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
5
+ rvm:
6
+ - 1.9.3
7
+ - rbx-nightly-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-io-console.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,29 @@
1
+ # Rubysl::Io::Console
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-io-console'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-io-console
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,762 @@
1
+ /* -*- c-file-style: "ruby" -*- */
2
+ /*
3
+ * console IO module
4
+ */
5
+ #include "ruby.h"
6
+ #ifdef HAVE_RUBY_IO_H
7
+ #include "ruby/io.h"
8
+ #else
9
+ #include "rubyio.h"
10
+ #endif
11
+
12
+ #ifndef HAVE_RB_IO_T
13
+ typedef OpenFile rb_io_t;
14
+ #endif
15
+
16
+ #ifdef HAVE_UNISTD_H
17
+ #include <unistd.h>
18
+ #endif
19
+ #ifdef HAVE_FCNTL_H
20
+ #include <fcntl.h>
21
+ #endif
22
+ #ifdef HAVE_SYS_IOCTL_H
23
+ #include <sys/ioctl.h>
24
+ #endif
25
+
26
+ #if defined HAVE_TERMIOS_H
27
+ # include <termios.h>
28
+ typedef struct termios conmode;
29
+
30
+ static int
31
+ setattr(int fd, conmode *t)
32
+ {
33
+ while (tcsetattr(fd, TCSAFLUSH, t)) {
34
+ if (errno != EINTR) return 0;
35
+ }
36
+ return 1;
37
+ }
38
+ # define getattr(fd, t) (tcgetattr(fd, t) == 0)
39
+ #elif defined HAVE_TERMIO_H
40
+ # include <termio.h>
41
+ typedef struct termio conmode;
42
+ # define setattr(fd, t) (ioctl(fd, TCSETAF, t) == 0)
43
+ # define getattr(fd, t) (ioctl(fd, TCGETA, t) == 0)
44
+ #elif defined HAVE_SGTTY_H
45
+ # include <sgtty.h>
46
+ typedef struct sgttyb conmode;
47
+ # ifdef HAVE_STTY
48
+ # define setattr(fd, t) (stty(fd, t) == 0)
49
+ # else
50
+ # define setattr(fd, t) (ioctl((fd), TIOCSETP, (t)) == 0)
51
+ # endif
52
+ # ifdef HAVE_GTTY
53
+ # define getattr(fd, t) (gtty(fd, t) == 0)
54
+ # else
55
+ # define getattr(fd, t) (ioctl((fd), TIOCGETP, (t)) == 0)
56
+ # endif
57
+ #elif defined _WIN32
58
+ #include <winioctl.h>
59
+ typedef DWORD conmode;
60
+
61
+ #ifdef HAVE_RB_W32_MAP_ERRNO
62
+ #define LAST_ERROR rb_w32_map_errno(GetLastError())
63
+ #else
64
+ #define LAST_ERROR EBADF
65
+ #endif
66
+ #define SET_LAST_ERROR (errno = LAST_ERROR, 0)
67
+
68
+ static int
69
+ setattr(int fd, conmode *t)
70
+ {
71
+ int x = SetConsoleMode((HANDLE)rb_w32_get_osfhandle(fd), *t);
72
+ if (!x) errno = LAST_ERROR;
73
+ return x;
74
+ }
75
+
76
+ static int
77
+ getattr(int fd, conmode *t)
78
+ {
79
+ int x = GetConsoleMode((HANDLE)rb_w32_get_osfhandle(fd), t);
80
+ if (!x) errno = LAST_ERROR;
81
+ return x;
82
+ }
83
+ #endif
84
+ #ifndef SET_LAST_ERROR
85
+ #define SET_LAST_ERROR (0)
86
+ #endif
87
+
88
+ #ifndef InitVM
89
+ #define InitVM(ext) {void InitVM_##ext(void);InitVM_##ext();}
90
+ #endif
91
+
92
+ static ID id_getc, id_console;
93
+
94
+ typedef struct {
95
+ int vmin;
96
+ int vtime;
97
+ } rawmode_arg_t;
98
+
99
+ static rawmode_arg_t *
100
+ rawmode_opt(int argc, VALUE *argv, rawmode_arg_t *opts)
101
+ {
102
+ rawmode_arg_t *optp = NULL;
103
+ VALUE vopts;
104
+ rb_scan_args(argc, argv, "0:", &vopts);
105
+ if (!NIL_P(vopts)) {
106
+ VALUE vmin = rb_hash_aref(vopts, ID2SYM(rb_intern("min")));
107
+ VALUE vtime = rb_hash_aref(vopts, ID2SYM(rb_intern("time")));
108
+ VALUE v10 = INT2FIX(10);
109
+ if (!NIL_P(vmin)) {
110
+ vmin = rb_funcall3(vmin, '*', 1, &v10);
111
+ opts->vmin = NUM2INT(vmin);
112
+ optp = opts;
113
+ }
114
+ if (!NIL_P(vtime)) {
115
+ vtime = rb_funcall3(vtime, '*', 1, &v10);
116
+ opts->vtime = NUM2INT(vtime);
117
+ optp = opts;
118
+ }
119
+ }
120
+ return optp;
121
+ }
122
+
123
+ static void
124
+ set_rawmode(conmode *t, void *arg)
125
+ {
126
+ #ifdef HAVE_CFMAKERAW
127
+ cfmakeraw(t);
128
+ t->c_lflag &= ~(ECHOE|ECHOK);
129
+ #elif defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
130
+ t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
131
+ t->c_oflag &= ~OPOST;
132
+ t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN);
133
+ t->c_cflag &= ~(CSIZE|PARENB);
134
+ t->c_cflag |= CS8;
135
+ #elif defined HAVE_SGTTY_H
136
+ t->sg_flags &= ~ECHO;
137
+ t->sg_flags |= RAW;
138
+ #elif defined _WIN32
139
+ *t = 0;
140
+ #endif
141
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
142
+ if (arg) {
143
+ const rawmode_arg_t *r = arg;
144
+ if (r->vmin >= 0) t->c_cc[VMIN] = r->vmin;
145
+ if (r->vtime >= 0) t->c_cc[VTIME] = r->vtime;
146
+ }
147
+ #endif
148
+ }
149
+
150
+ static void
151
+ set_cookedmode(conmode *t, void *arg)
152
+ {
153
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
154
+ t->c_iflag |= (BRKINT|ISTRIP|ICRNL|IXON);
155
+ t->c_oflag |= OPOST;
156
+ t->c_lflag |= (ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN);
157
+ #elif defined HAVE_SGTTY_H
158
+ t->sg_flags |= ECHO;
159
+ t->sg_flags &= ~RAW;
160
+ #elif defined _WIN32
161
+ *t |= ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT;
162
+ #endif
163
+ }
164
+
165
+ static void
166
+ set_noecho(conmode *t, void *arg)
167
+ {
168
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
169
+ t->c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
170
+ #elif defined HAVE_SGTTY_H
171
+ t->sg_flags &= ~ECHO;
172
+ #elif defined _WIN32
173
+ *t &= ~ENABLE_ECHO_INPUT;
174
+ #endif
175
+ }
176
+
177
+ static void
178
+ set_echo(conmode *t, void *arg)
179
+ {
180
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
181
+ t->c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL);
182
+ #elif defined HAVE_SGTTY_H
183
+ t->sg_flags |= ECHO;
184
+ #elif defined _WIN32
185
+ *t |= ENABLE_ECHO_INPUT;
186
+ #endif
187
+ }
188
+
189
+ static int
190
+ echo_p(conmode *t)
191
+ {
192
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
193
+ return (t->c_lflag & (ECHO | ECHONL)) != 0;
194
+ #elif defined HAVE_SGTTY_H
195
+ return (t->sg_flags & ECHO) != 0;
196
+ #elif defined _WIN32
197
+ return (*t & ENABLE_ECHO_INPUT) != 0;
198
+ #endif
199
+ }
200
+
201
+ static int
202
+ set_ttymode(int fd, conmode *t, void (*setter)(conmode *, void *), void *arg)
203
+ {
204
+ conmode r;
205
+ if (!getattr(fd, t)) return 0;
206
+ r = *t;
207
+ setter(&r, arg);
208
+ return setattr(fd, &r);
209
+ }
210
+
211
+ #ifdef GetReadFile
212
+ #define GetReadFD(fptr) fileno(GetReadFile(fptr))
213
+ #else
214
+ #define GetReadFD(fptr) ((fptr)->fd)
215
+ #endif
216
+
217
+ #ifdef GetWriteFile
218
+ #define GetWriteFD(fptr) fileno(GetWriteFile(fptr))
219
+ #else
220
+ static inline int
221
+ get_write_fd(const rb_io_t *fptr)
222
+ {
223
+ VALUE wio = fptr->tied_io_for_writing;
224
+ rb_io_t *ofptr;
225
+ if (!wio) return fptr->fd;
226
+ GetOpenFile(wio, ofptr);
227
+ return ofptr->fd;
228
+ }
229
+ #define GetWriteFD(fptr) get_write_fd(fptr)
230
+ #endif
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
+ errno = error;
284
+ rb_sys_fail(0);
285
+ }
286
+ rb_jump_tag(status);
287
+ }
288
+ return result;
289
+ }
290
+
291
+ /*
292
+ * call-seq:
293
+ * io.raw(min: nil, time: nil) {|io| }
294
+ *
295
+ * Yields +self+ within raw mode.
296
+ *
297
+ * STDIN.raw(&:gets)
298
+ *
299
+ * will read and return a line without echo back and line editing.
300
+ *
301
+ * You must require 'io/console' to use this method.
302
+ */
303
+ static VALUE
304
+ console_raw(int argc, VALUE *argv, VALUE io)
305
+ {
306
+ rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
307
+ return ttymode(io, rb_yield, set_rawmode, optp);
308
+ }
309
+
310
+ /*
311
+ * call-seq:
312
+ * io.raw!(min: nil, time: nil)
313
+ *
314
+ * Enables raw mode.
315
+ *
316
+ * If the terminal mode needs to be back, use io.raw { ... }.
317
+ *
318
+ * You must require 'io/console' to use this method.
319
+ */
320
+ static VALUE
321
+ console_set_raw(int argc, VALUE *argv, VALUE io)
322
+ {
323
+ conmode t;
324
+ rb_io_t *fptr;
325
+ int fd;
326
+ rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
327
+
328
+ GetOpenFile(io, fptr);
329
+ fd = GetReadFD(fptr);
330
+ if (!getattr(fd, &t)) rb_sys_fail(0);
331
+ set_rawmode(&t, optp);
332
+ if (!setattr(fd, &t)) rb_sys_fail(0);
333
+ return io;
334
+ }
335
+
336
+ /*
337
+ * call-seq:
338
+ * io.cooked {|io| }
339
+ *
340
+ * Yields +self+ within cooked mode.
341
+ *
342
+ * STDIN.cooked(&:gets)
343
+ *
344
+ * will read and return a line with echo back and line editing.
345
+ *
346
+ * You must require 'io/console' to use this method.
347
+ */
348
+ static VALUE
349
+ console_cooked(VALUE io)
350
+ {
351
+ return ttymode(io, rb_yield, set_cookedmode, NULL);
352
+ }
353
+
354
+ /*
355
+ * call-seq:
356
+ * io.cooked!
357
+ *
358
+ * Enables cooked mode.
359
+ *
360
+ * If the terminal mode needs to be back, use io.cooked { ... }.
361
+ *
362
+ * You must require 'io/console' to use this method.
363
+ */
364
+ static VALUE
365
+ console_set_cooked(VALUE io)
366
+ {
367
+ conmode t;
368
+ rb_io_t *fptr;
369
+ int fd;
370
+
371
+ GetOpenFile(io, fptr);
372
+ fd = GetReadFD(fptr);
373
+ if (!getattr(fd, &t)) rb_sys_fail(0);
374
+ set_cookedmode(&t, NULL);
375
+ if (!setattr(fd, &t)) rb_sys_fail(0);
376
+ return io;
377
+ }
378
+
379
+ static VALUE
380
+ getc_call(VALUE io)
381
+ {
382
+ return rb_funcall2(io, id_getc, 0, 0);
383
+ }
384
+
385
+ /*
386
+ * call-seq:
387
+ * io.getch(min: nil, time: nil) -> char
388
+ *
389
+ * Reads and returns a character in raw mode.
390
+ *
391
+ * You must require 'io/console' to use this method.
392
+ */
393
+ static VALUE
394
+ console_getch(int argc, VALUE *argv, VALUE io)
395
+ {
396
+ rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
397
+ return ttymode(io, getc_call, set_rawmode, optp);
398
+ }
399
+
400
+ /*
401
+ * call-seq:
402
+ * io.noecho {|io| }
403
+ *
404
+ * Yields +self+ with disabling echo back.
405
+ *
406
+ * STDIN.noecho(&:gets)
407
+ *
408
+ * will read and return a line without echo back.
409
+ *
410
+ * You must require 'io/console' to use this method.
411
+ */
412
+ static VALUE
413
+ console_noecho(VALUE io)
414
+ {
415
+ return ttymode(io, rb_yield, set_noecho, NULL);
416
+ }
417
+
418
+ /*
419
+ * call-seq:
420
+ * io.echo = flag
421
+ *
422
+ * Enables/disables echo back.
423
+ * On some platforms, all combinations of this flags and raw/cooked
424
+ * mode may not be valid.
425
+ *
426
+ * You must require 'io/console' to use this method.
427
+ */
428
+ static VALUE
429
+ console_set_echo(VALUE io, VALUE f)
430
+ {
431
+ conmode t;
432
+ rb_io_t *fptr;
433
+ int fd;
434
+
435
+ GetOpenFile(io, fptr);
436
+ fd = GetReadFD(fptr);
437
+ if (!getattr(fd, &t)) rb_sys_fail(0);
438
+ if (RTEST(f))
439
+ set_echo(&t, NULL);
440
+ else
441
+ set_noecho(&t, NULL);
442
+ if (!setattr(fd, &t)) rb_sys_fail(0);
443
+ return io;
444
+ }
445
+
446
+ /*
447
+ * call-seq:
448
+ * io.echo? -> true or false
449
+ *
450
+ * Returns +true+ if echo back is enabled.
451
+ *
452
+ * You must require 'io/console' to use this method.
453
+ */
454
+ static VALUE
455
+ console_echo_p(VALUE io)
456
+ {
457
+ conmode t;
458
+ rb_io_t *fptr;
459
+ int fd;
460
+
461
+ GetOpenFile(io, fptr);
462
+ fd = GetReadFD(fptr);
463
+ if (!getattr(fd, &t)) rb_sys_fail(0);
464
+ return echo_p(&t) ? Qtrue : Qfalse;
465
+ }
466
+
467
+ #if defined TIOCGWINSZ
468
+ typedef struct winsize rb_console_size_t;
469
+ #define getwinsize(fd, buf) (ioctl((fd), TIOCGWINSZ, (buf)) == 0)
470
+ #define setwinsize(fd, buf) (ioctl((fd), TIOCSWINSZ, (buf)) == 0)
471
+ #define winsize_row(buf) (buf)->ws_row
472
+ #define winsize_col(buf) (buf)->ws_col
473
+ #elif defined _WIN32
474
+ typedef CONSOLE_SCREEN_BUFFER_INFO rb_console_size_t;
475
+ #define getwinsize(fd, buf) ( \
476
+ GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), (buf)) || \
477
+ SET_LAST_ERROR)
478
+ #define winsize_row(buf) ((buf)->srWindow.Bottom - (buf)->srWindow.Top + 1)
479
+ #define winsize_col(buf) (buf)->dwSize.X
480
+ #endif
481
+
482
+ #if defined TIOCGWINSZ || defined _WIN32
483
+ #define USE_CONSOLE_GETSIZE 1
484
+ #endif
485
+
486
+ #ifdef USE_CONSOLE_GETSIZE
487
+ /*
488
+ * call-seq:
489
+ * io.winsize -> [rows, columns]
490
+ *
491
+ * Returns console size.
492
+ *
493
+ * You must require 'io/console' to use this method.
494
+ */
495
+ static VALUE
496
+ console_winsize(VALUE io)
497
+ {
498
+ rb_io_t *fptr;
499
+ int fd;
500
+ rb_console_size_t ws;
501
+
502
+ GetOpenFile(io, fptr);
503
+ fd = GetWriteFD(fptr);
504
+ if (!getwinsize(fd, &ws)) rb_sys_fail(0);
505
+ return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
506
+ }
507
+
508
+ /*
509
+ * call-seq:
510
+ * io.winsize = [rows, columns]
511
+ *
512
+ * Tries to set console size. The effect depends on the platform and
513
+ * the running environment.
514
+ *
515
+ * You must require 'io/console' to use this method.
516
+ */
517
+ static VALUE
518
+ console_set_winsize(VALUE io, VALUE size)
519
+ {
520
+ rb_io_t *fptr;
521
+ rb_console_size_t ws;
522
+ #if defined _WIN32
523
+ HANDLE wh;
524
+ int newrow, newcol;
525
+ #endif
526
+ VALUE row, col, xpixel, ypixel;
527
+ #if defined TIOCSWINSZ
528
+ int fd;
529
+ #endif
530
+
531
+ GetOpenFile(io, fptr);
532
+ size = rb_Array(size);
533
+ rb_scan_args((int)RARRAY_LEN(size), RARRAY_PTR(size), "22",
534
+ &row, &col, &xpixel, &ypixel);
535
+ #if defined TIOCSWINSZ
536
+ fd = GetWriteFD(fptr);
537
+ ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
538
+ #define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
539
+ SET(row);
540
+ SET(col);
541
+ SET(xpixel);
542
+ SET(ypixel);
543
+ #undef SET
544
+ if (!setwinsize(fd, &ws)) rb_sys_fail(0);
545
+ #elif defined _WIN32
546
+ wh = (HANDLE)rb_w32_get_osfhandle(GetReadFD(fptr));
547
+ newrow = (SHORT)NUM2UINT(row);
548
+ newcol = (SHORT)NUM2UINT(col);
549
+ if (!getwinsize(GetReadFD(fptr), &ws)) {
550
+ rb_sys_fail("GetConsoleScreenBufferInfo");
551
+ }
552
+ if ((ws.dwSize.X < newcol && (ws.dwSize.X = newcol, 1)) ||
553
+ (ws.dwSize.Y < newrow && (ws.dwSize.Y = newrow, 1))) {
554
+ if (!(SetConsoleScreenBufferSize(wh, ws.dwSize) || SET_LAST_ERROR)) {
555
+ rb_sys_fail("SetConsoleScreenBufferInfo");
556
+ }
557
+ }
558
+ ws.srWindow.Left = 0;
559
+ ws.srWindow.Top = 0;
560
+ ws.srWindow.Right = newcol;
561
+ ws.srWindow.Bottom = newrow;
562
+ if (!(SetConsoleWindowInfo(wh, FALSE, &ws.srWindow) || SET_LAST_ERROR)) {
563
+ rb_sys_fail("SetConsoleWindowInfo");
564
+ }
565
+ #endif
566
+ return io;
567
+ }
568
+ #endif
569
+
570
+ /*
571
+ * call-seq:
572
+ * io.iflush
573
+ *
574
+ * Flushes input buffer in kernel.
575
+ *
576
+ * You must require 'io/console' to use this method.
577
+ */
578
+ static VALUE
579
+ console_iflush(VALUE io)
580
+ {
581
+ rb_io_t *fptr;
582
+ int fd;
583
+
584
+ GetOpenFile(io, fptr);
585
+ fd = GetReadFD(fptr);
586
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
587
+ if (tcflush(fd, TCIFLUSH)) rb_sys_fail(0);
588
+ #endif
589
+ return io;
590
+ }
591
+
592
+ /*
593
+ * call-seq:
594
+ * io.oflush
595
+ *
596
+ * Flushes output buffer in kernel.
597
+ *
598
+ * You must require 'io/console' to use this method.
599
+ */
600
+ static VALUE
601
+ console_oflush(VALUE io)
602
+ {
603
+ rb_io_t *fptr;
604
+ int fd;
605
+
606
+ GetOpenFile(io, fptr);
607
+ fd = GetWriteFD(fptr);
608
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
609
+ if (tcflush(fd, TCOFLUSH)) rb_sys_fail(0);
610
+ #endif
611
+ return io;
612
+ }
613
+
614
+ /*
615
+ * call-seq:
616
+ * io.ioflush
617
+ *
618
+ * Flushes input and output buffers in kernel.
619
+ *
620
+ * You must require 'io/console' to use this method.
621
+ */
622
+ static VALUE
623
+ console_ioflush(VALUE io)
624
+ {
625
+ rb_io_t *fptr;
626
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
627
+ int fd1, fd2;
628
+ #endif
629
+
630
+ GetOpenFile(io, fptr);
631
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
632
+ fd1 = GetReadFD(fptr);
633
+ fd2 = GetWriteFD(fptr);
634
+ if (fd2 != -1 && fd1 != fd2) {
635
+ if (tcflush(fd1, TCIFLUSH)) rb_sys_fail(0);
636
+ if (tcflush(fd2, TCOFLUSH)) rb_sys_fail(0);
637
+ }
638
+ else {
639
+ if (tcflush(fd1, TCIOFLUSH)) rb_sys_fail(0);
640
+ }
641
+ #endif
642
+ return io;
643
+ }
644
+
645
+ /*
646
+ * call-seq:
647
+ * IO.console -> #<File:/dev/tty>
648
+ *
649
+ * Returns an File instance opened console.
650
+ *
651
+ * You must require 'io/console' to use this method.
652
+ */
653
+ static VALUE
654
+ console_dev(VALUE klass)
655
+ {
656
+ VALUE con = 0;
657
+ rb_io_t *fptr;
658
+
659
+ if (klass == rb_cIO) klass = rb_cFile;
660
+ if (rb_const_defined(klass, id_console)) {
661
+ con = rb_const_get(klass, id_console);
662
+ if (TYPE(con) == T_FILE) {
663
+ if ((fptr = RFILE(con)->fptr) && GetReadFD(fptr) != -1)
664
+ return con;
665
+ }
666
+ rb_mod_remove_const(klass, ID2SYM(id_console));
667
+ }
668
+ {
669
+ VALUE args[2];
670
+ #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H || defined HAVE_SGTTY_H
671
+ # define CONSOLE_DEVICE "/dev/tty"
672
+ #elif defined _WIN32
673
+ # define CONSOLE_DEVICE "con$"
674
+ # define CONSOLE_DEVICE_FOR_READING "conin$"
675
+ # define CONSOLE_DEVICE_FOR_WRITING "conout$"
676
+ #endif
677
+ #ifndef CONSOLE_DEVICE_FOR_READING
678
+ # define CONSOLE_DEVICE_FOR_READING CONSOLE_DEVICE
679
+ #endif
680
+ #ifdef CONSOLE_DEVICE_FOR_WRITING
681
+ VALUE out;
682
+ rb_io_t *ofptr;
683
+ #endif
684
+ int fd;
685
+
686
+ #ifdef CONSOLE_DEVICE_FOR_WRITING
687
+ fd = open(CONSOLE_DEVICE_FOR_WRITING, O_WRONLY);
688
+ if (fd < 0) return Qnil;
689
+ rb_update_max_fd(fd);
690
+ args[1] = INT2FIX(O_WRONLY);
691
+ args[0] = INT2NUM(fd);
692
+ out = rb_class_new_instance(2, args, klass);
693
+ #endif
694
+ fd = open(CONSOLE_DEVICE_FOR_READING, O_RDWR);
695
+ if (fd < 0) {
696
+ #ifdef CONSOLE_DEVICE_FOR_WRITING
697
+ rb_io_close(out);
698
+ #endif
699
+ return Qnil;
700
+ }
701
+ rb_update_max_fd(fd);
702
+ args[1] = INT2FIX(O_RDWR);
703
+ args[0] = INT2NUM(fd);
704
+ con = rb_class_new_instance(2, args, klass);
705
+ GetOpenFile(con, fptr);
706
+ rb_ivar_set(con, rb_intern("@path"), rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE_FOR_READING)));
707
+ #ifdef CONSOLE_DEVICE_FOR_WRITING
708
+ GetOpenFile(out, ofptr);
709
+ # ifdef HAVE_RB_IO_GET_WRITE_IO
710
+ rb_ivar_set(out, rb_intern("@path"), rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE_FOR_WRITING)));
711
+ fptr->tied_io_for_writing = out;
712
+ # else
713
+ fptr->f2 = ofptr->f;
714
+ ofptr->f = 0;
715
+ # endif
716
+ rb_ivar_set(out, rb_intern("@sync"), Qtrue);
717
+ #endif
718
+ rb_ivar_set(con, rb_intern("@sync"), Qtrue);
719
+ rb_const_set(klass, id_console, con);
720
+ }
721
+ return con;
722
+ }
723
+
724
+ static VALUE
725
+ io_getch(int argc, VALUE *argv, VALUE io)
726
+ {
727
+ return rb_funcall2(io, rb_intern("getc"), argc, argv);
728
+ }
729
+
730
+ /*
731
+ * IO console methods
732
+ */
733
+ void
734
+ Init_console(void)
735
+ {
736
+ id_getc = rb_intern("getc");
737
+ id_console = rb_intern("Console");
738
+ InitVM(console);
739
+ }
740
+
741
+ void
742
+ InitVM_console(void)
743
+ {
744
+ rb_define_method(rb_cIO, "raw", console_raw, -1);
745
+ rb_define_method(rb_cIO, "raw!", console_set_raw, -1);
746
+ rb_define_method(rb_cIO, "cooked", console_cooked, 0);
747
+ rb_define_method(rb_cIO, "cooked!", console_set_cooked, 0);
748
+ rb_define_method(rb_cIO, "getch", console_getch, -1);
749
+ rb_define_method(rb_cIO, "echo=", console_set_echo, 1);
750
+ rb_define_method(rb_cIO, "echo?", console_echo_p, 0);
751
+ rb_define_method(rb_cIO, "noecho", console_noecho, 0);
752
+ rb_define_method(rb_cIO, "winsize", console_winsize, 0);
753
+ rb_define_method(rb_cIO, "winsize=", console_set_winsize, 1);
754
+ rb_define_method(rb_cIO, "iflush", console_iflush, 0);
755
+ rb_define_method(rb_cIO, "oflush", console_oflush, 0);
756
+ rb_define_method(rb_cIO, "ioflush", console_ioflush, 0);
757
+ rb_define_singleton_method(rb_cIO, "console", console_dev, 0);
758
+ {
759
+ VALUE mReadable = rb_define_module_under(rb_cIO, "Readable");
760
+ rb_define_method(mReadable, "getch", io_getch, -1);
761
+ }
762
+ }
@@ -0,0 +1,20 @@
1
+ require 'mkmf'
2
+
3
+ ok = true
4
+ hdr = nil
5
+ case
6
+ when macro_defined?("_WIN32", "")
7
+ have_func("rb_w32_map_errno", "ruby.h")
8
+ when hdr = %w"termios.h termio.h".find {|h| have_header(h)}
9
+ have_func("cfmakeraw", hdr)
10
+ when have_header(hdr = "sgtty.h")
11
+ %w"stty gtty".each {|f| have_func(f, hdr)}
12
+ else
13
+ ok = false
14
+ end
15
+ have_header("sys/ioctl.h")
16
+ have_func("rb_io_get_write_io", "ruby/io.h")
17
+ have_func("dup3", "unistd.h")
18
+ if ok
19
+ create_makefile("io/console/console")
20
+ end
@@ -0,0 +1 @@
1
+ require "rubysl/io/console"
@@ -0,0 +1,20 @@
1
+ def IO.default_console_size
2
+ [
3
+ ENV["LINES"].to_i.nonzero? || 25,
4
+ ENV["COLUMNS"].to_i.nonzero? || 80,
5
+ ]
6
+ end
7
+
8
+ begin
9
+ require 'io/console'
10
+ rescue LoadError
11
+ class IO
12
+ alias console_size default_console_size
13
+ end
14
+ else
15
+ def IO.console_size
16
+ console.winsize
17
+ rescue NoMethodError
18
+ default_console_size
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ require "io/console/console"
2
+ require "rubysl/io/console/version"
@@ -0,0 +1,7 @@
1
+ module RubySL
2
+ module IO
3
+ module Console
4
+ VERSION = "2.0.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ require './lib/rubysl/io/console/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "rubysl-io-console"
6
+ spec.version = RubySL::IO::Console::VERSION
7
+ spec.authors = ["Brian Shirai"]
8
+ spec.email = ["brixen@gmail.com"]
9
+ spec.description = %q{Ruby standard library console.}
10
+ spec.summary = %q{Ruby standard library console.}
11
+ spec.homepage = "https://github.com/rubysl/rubysl-io-console"
12
+ spec.license = "BSD"
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.extensions = ["ext/rubysl/io/console/extconf.rb"]
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "mspec", "~> 1.5"
23
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubysl-io-console
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Shirai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ description: Ruby standard library console.
56
+ email:
57
+ - brixen@gmail.com
58
+ executables: []
59
+ extensions:
60
+ - ext/rubysl/io/console/extconf.rb
61
+ extra_rdoc_files: []
62
+ files:
63
+ - .gitignore
64
+ - .travis.yml
65
+ - Gemfile
66
+ - LICENSE
67
+ - README.md
68
+ - Rakefile
69
+ - ext/rubysl/io/console/console.c
70
+ - ext/rubysl/io/console/extconf.rb
71
+ - lib/io/console.rb
72
+ - lib/io/console/size.rb
73
+ - lib/rubysl/io/console.rb
74
+ - lib/rubysl/io/console/version.rb
75
+ - rubysl-io-console.gemspec
76
+ homepage: https://github.com/rubysl/rubysl-io-console
77
+ licenses:
78
+ - BSD
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.0.7
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Ruby standard library console.
100
+ test_files: []