First commit, about step 8 in tutorial
This commit is contained in:
commit
8522d9fb33
169
.gitignore
vendored
Normal file
169
.gitignore
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/vim,emacs,composer,jetbrains+all
|
||||
# Edit at https://www.gitignore.io/?templates=vim,emacs,composer,jetbrains+all
|
||||
|
||||
### Composer ###
|
||||
composer.phar
|
||||
/vendor/
|
||||
|
||||
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
|
||||
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
|
||||
# composer.lock
|
||||
|
||||
### Emacs ###
|
||||
# -*- mode: gitignore; -*-
|
||||
*~
|
||||
\#*\#
|
||||
/.emacs.desktop
|
||||
/.emacs.desktop.lock
|
||||
*.elc
|
||||
auto-save-list
|
||||
tramp
|
||||
.\#*
|
||||
|
||||
# Org-mode
|
||||
.org-id-locations
|
||||
*_archive
|
||||
|
||||
# flymake-mode
|
||||
*_flymake.*
|
||||
|
||||
# eshell files
|
||||
/eshell/history
|
||||
/eshell/lastdir
|
||||
|
||||
# elpa packages
|
||||
/elpa/
|
||||
|
||||
# reftex files
|
||||
*.rel
|
||||
|
||||
# AUCTeX auto folder
|
||||
/auto/
|
||||
|
||||
# cask packages
|
||||
.cask/
|
||||
dist/
|
||||
|
||||
# Flycheck
|
||||
flycheck_*.el
|
||||
|
||||
# server auth directory
|
||||
/server/
|
||||
|
||||
# projectiles files
|
||||
.projectile
|
||||
|
||||
# directory configuration
|
||||
.dir-locals.el
|
||||
|
||||
# network security
|
||||
/network-security.data
|
||||
|
||||
|
||||
### JetBrains+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### JetBrains+all Patch ###
|
||||
# Ignores the whole .idea folder and all .iml files
|
||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||
|
||||
.idea/
|
||||
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
Sessionx.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
# End of https://www.gitignore.io/api/vim,emacs,composer,jetbrains+all
|
41
kilo
Executable file
41
kilo
Executable file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/php
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Kilo;
|
||||
|
||||
use FFI;
|
||||
|
||||
require_once __DIR__ . '/src/constants.php';
|
||||
require_once __DIR__ . '/src/functions.php';
|
||||
|
||||
$ffi = FFI::load(__DIR__ . '/src/ffi.h');
|
||||
$original_termios = $ffi->new("struct termios");
|
||||
|
||||
function main(): int
|
||||
{
|
||||
global $ffi;
|
||||
|
||||
enableRawMode();
|
||||
|
||||
// Input Loop
|
||||
do
|
||||
{
|
||||
$input = read_stdin();
|
||||
if ($ffi->iscntrl($input))
|
||||
{
|
||||
printf("%d\n", $input);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%d ('%c')\n", $input, $input);
|
||||
}
|
||||
}
|
||||
while ($input !== 'q');
|
||||
|
||||
disableRawMode();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Init
|
||||
main();
|
130
src/constants.php
Normal file
130
src/constants.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Kilo;
|
||||
|
||||
define('STDIN_FILENO', 0);
|
||||
define('TCSAFLUSH', 2);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ! Termios bitflags
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/* Input modes */
|
||||
define('IGNBRK', (1 << 0)); /* Ignore break condition. */
|
||||
define('BRKINT', (1 << 1)); /* Signal interrupt on break. */
|
||||
define('IGNPAR', (1 << 2)); /* Ignore characters with parity errors. */
|
||||
define('PARMRK', (1 << 3)); /* Mark parity and framing errors. */
|
||||
define('INPCK', (1 << 4)); /* Enable input parity check. */
|
||||
define('ISTRIP', (1 << 5)); /* Strip 8th bit off characters. */
|
||||
define('INLCR', (1 << 6)); /* Map NL to CR on input. */
|
||||
define('IGNCR', (1 << 7)); /* Ignore CR. */
|
||||
define('ICRNL', (1 << 8)); /* Map CR to NL on input. */
|
||||
define('IXON', (1 << 9)); /* Enable start/stop output control. */
|
||||
define('IXOFF', (1 << 10)); /* Enable start/stop input control. */
|
||||
define('IXANY', (1 << 11)); /* Any character will restart after stop. */
|
||||
define('IMAXBEL', (1 << 13)); /* Ring bell when input queue is full. */
|
||||
define('IUCLC', (1 << 14)); /* Translate upper case input to lower case. */
|
||||
|
||||
/* Output modes */
|
||||
define('OPOST', (1 << 0)); /* Perform output processing. */
|
||||
define('ONLCR', (1 << 1)); /* Map NL to CR-NL on output. */
|
||||
define('OXTABS', (1 << 2)); /* Expand tabs to spaces. */
|
||||
define('ONOEOT', (1 << 3)); /* Discard EOT (^D) on output. */
|
||||
define('OCRNL', (1 << 4)); /* Map CR to NL. */
|
||||
define('ONOCR', (1 << 5)); /* Discard CR's when on column 0. */
|
||||
define('ONLRET', (1 << 6)); /* Move to column 0 on NL. */
|
||||
define('NLDLY', (3 << 8)); /* NL delay. */
|
||||
define('NL0', (0 << 8)); /* NL type 0. */
|
||||
define('NL1', (1 << 8)); /* NL type 1. */
|
||||
define('TABDLY', (3 << 10 | 1 << 2)); /* TAB delay. */
|
||||
define('TAB0', (0 << 10)); /* TAB delay type 0. */
|
||||
define('TAB1', (1 << 10)); /* TAB delay type 1. */
|
||||
define('TAB2', (2 << 10)); /* TAB delay type 2. */
|
||||
define('TAB3', (1 << 2)); /* Expand tabs to spaces. */
|
||||
define('CRDLY', (3 << 12)); /* CR delay. */
|
||||
define('CR0', (0 << 12)); /* CR delay type 0. */
|
||||
define('CR1', (1 << 12)); /* CR delay type 1. */
|
||||
define('CR2', (2 << 12)); /* CR delay type 2. */
|
||||
define('CR3', (3 << 12)); /* CR delay type 3. */
|
||||
define('FFDLY', (1 << 14)); /* FF delay. */
|
||||
define('FF0', (0 << 14)); /* FF delay type 0. */
|
||||
define('FF1', (1 << 14)); /* FF delay type 1. */
|
||||
define('BSDLY', (1 << 15)); /* BS delay. */
|
||||
define('BS0', (0 << 15)); /* BS delay type 0. */
|
||||
define('BS1', (1 << 15)); /* BS delay type 1. */
|
||||
define('VTDLY', (1 << 16)); /* VT delay. */
|
||||
define('VT0', (0 << 16)); /* VT delay type 0. */
|
||||
define('VT1', (1 << 16)); /* VT delay type 1. */
|
||||
define('OLCUC', (1 << 17)); /* Translate lower case output to upper case */
|
||||
define('OFILL', (1 << 18)); /* Send fill characters for delays. */
|
||||
define('OFDEL', (1 << 19)); /* Fill is DEL. */
|
||||
|
||||
/* Control modes */
|
||||
define('CIGNORE', (1 << 0)); /* Ignore these control flags. */
|
||||
define('CS5', 0); /* 5 bits per byte. */
|
||||
define('CS6', (1 << 8)); /* 6 bits per byte. */
|
||||
define('CS7', (1 << 9)); /* 7 bits per byte. */
|
||||
define('CS8', (CS6|CS7)); /* 8 bits per byte. */
|
||||
define('CSIZE', (CS5|CS6|CS7|CS8)); /* Number of bits per byte (mask). */
|
||||
define('CSTOPB', (1 << 10)); /* Two stop bits instead of one. */
|
||||
define('CREAD', (1 << 11)); /* Enable receiver. */
|
||||
define('PARENB', (1 << 12)); /* Parity enable. */
|
||||
define('PARODD', (1 << 13)); /* Odd parity instead of even. */
|
||||
define('HUPCL', (1 << 14)); /* Hang up on last close. */
|
||||
define('CLOCAL', (1 << 15)); /* Ignore modem status lines. */
|
||||
define('CRTSCTS', (1 << 16)); /* RTS/CTS flow control. */
|
||||
define('CRTS_IFLOW', CRTSCTS); /* Compatibility. */
|
||||
define('CCTS_OFLOW', CRTSCTS); /* Compatibility. */
|
||||
define('CDTRCTS', (1 << 17)); /* DTR/CTS flow control. */
|
||||
define('MDMBUF', (1 << 20)); /* DTR/DCD flow control. */
|
||||
define('CHWFLOW', (MDMBUF|CRTSCTS|CDTRCTS)); /* All types of flow control. */
|
||||
|
||||
/* Local modes */
|
||||
define('ECHOKE', (1 << 0)); /* Visual erase for KILL. */
|
||||
define('_ECHOE', (1 << 1)); /* Visual erase for ERASE. */
|
||||
define('ECHOE', _ECHOE);
|
||||
define('_ECHOK', (1 << 2)); /* Echo NL after KILL. */
|
||||
define('ECHOK', _ECHOK);
|
||||
define('_ECHO', (1 << 3)); /* Enable echo. */
|
||||
define('ECHO', _ECHO);
|
||||
define('_ECHONL', (1 << 4)); /* Echo NL even if ECHO is off. */
|
||||
define('ECHONL', _ECHONL);
|
||||
define('ECHOPRT', (1 << 5)); /* Hardcopy visual erase. */
|
||||
define('ECHOCTL', (1 << 6)); /* Echo control characters as ^X. */
|
||||
define('_ISIG', (1 << 7)); /* Enable signals. */
|
||||
define('ISIG', _ISIG);
|
||||
define('_ICANON', (1 << 8)); /* Do erase and kill processing. */
|
||||
define('ICANON', _ICANON);
|
||||
define('ALTWERASE', (1 << 9)); /* Alternate WERASE algorithm. */
|
||||
define('_IEXTEN', (1 << 10)); /* Enable DISCARD and LNEXT. */
|
||||
define('IEXTEN', _IEXTEN);
|
||||
define('EXTPROC', (1 << 11)); /* External processing. */
|
||||
define('_TOSTOP', (1 << 22)); /* Send SIGTTOU for background output. */
|
||||
define('TOSTOP', _TOSTOP);
|
||||
define('FLUSHO', (1 << 23)); /* Output being flushed (state). */
|
||||
define('XCASE', (1 << 24)); /* Canonical upper/lower case. */
|
||||
define('NOKERNINFO', (1 << 25)); /* Disable VSTATUS. */
|
||||
define('PENDIN', (1 << 29)); /* Retype pending input (state). */
|
||||
define('_NOFLSH', (1 << 31)); /* Disable flush after interrupt. */
|
||||
define('NOFLSH', _NOFLSH);
|
||||
|
||||
/* Control characters */
|
||||
define('VEOF', 0); /* End-of-file character [ICANON]. */
|
||||
define('VEOL', 1); /* End-of-line character [ICANON]. */
|
||||
define('VEOL2', 2); /* Second EOL character [ICANON]. */
|
||||
define('VERASE', 3); /* Erase character [ICANON]. */
|
||||
define('VWERASE', 4); /* Word-erase character [ICANON]. */
|
||||
define('VKILL', 5); /* Kill-line character [ICANON]. */
|
||||
define('VREPRINT', 6); /* Reprint-line character [ICANON]. */
|
||||
define('VINTR', 8); /* Interrupt character [ISIG]. */
|
||||
define('VQUIT', 9); /* Quit character [ISIG]. */
|
||||
define('VSUSP', 10); /* Suspend character [ISIG]. */
|
||||
define('VDSUSP', 11); /* Delayed suspend character [ISIG]. */
|
||||
define('VSTART', 12); /* Start (X-ON) character [IXON, IXOFF]. */
|
||||
define('VSTOP', 13); /* Stop (X-OFF) character [IXON, IXOFF]. */
|
||||
define('VLNEXT', 14); /* Literal-next character [IEXTEN]. */
|
||||
define('VDISCARD', 15); /* Discard character [IEXTEN]. */
|
||||
define('VMIN', 16); /* Minimum number of bytes read at once [!ICANON]. */
|
||||
define('VTIME', 17); /* Time-out value (tenths of a second) [!ICANON]. */
|
||||
define('VSTATUS', 18); /* Status character [ICANON]. */
|
||||
define('NCCS', 20); /* Value duplicated in <hurd/tioctl.defs>. */
|
57
src/ffi.h
Normal file
57
src/ffi.h
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Interfaces for PHP FFI
|
||||
*
|
||||
* Most of the structure code is cribbed from GLib
|
||||
*
|
||||
* Defines are not (generally) recognized by the FFI integration
|
||||
*/
|
||||
|
||||
// PHP 'constants' for FFI integration
|
||||
// These seem to be the only define statements supported by the FFI integration
|
||||
#define FFI_SCOPE "terminal"
|
||||
#define FFI_LIB "libc.so.6"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//! <termios.h>
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/* Type of terminal control flag masks. */
|
||||
typedef unsigned long int tcflag_t;
|
||||
|
||||
/* Type of control characters. */
|
||||
typedef unsigned char cc_t;
|
||||
|
||||
/* Type of baud rate specifiers. */
|
||||
typedef long int speed_t;
|
||||
|
||||
/* Terminal control structure. */
|
||||
struct termios
|
||||
{
|
||||
/* Input modes. */
|
||||
tcflag_t c_iflag;
|
||||
|
||||
/* Output modes. */
|
||||
tcflag_t c_oflag;
|
||||
|
||||
/* Control modes. */
|
||||
tcflag_t c_cflag;
|
||||
|
||||
/* Local modes. */
|
||||
tcflag_t c_lflag;
|
||||
|
||||
/* Control characters. */
|
||||
cc_t c_cc[20];
|
||||
|
||||
/* Input and output baud rates. */
|
||||
speed_t __ispeed, __ospeed;
|
||||
};
|
||||
|
||||
int tcgetattr (int fd, struct termios *termios_p);
|
||||
int tcsetattr (int fd, int optional_actions, const struct termios *termios_p);
|
||||
|
||||
void cfmakeraw (struct termios *termios_p);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//! <ctype.h>
|
||||
// -----------------------------------------------------------------------------
|
||||
int iscntrl (int);
|
40
src/functions.php
Normal file
40
src/functions.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Kilo;
|
||||
|
||||
use FFI;
|
||||
|
||||
// require_once 'constants.php';
|
||||
|
||||
function enableRawMode(): void
|
||||
{
|
||||
global $ffi;
|
||||
global $original_termios;
|
||||
|
||||
|
||||
// Populate the original terminal settings
|
||||
$ffi->tcgetattr(STDIN_FILENO, FFI::addr($original_termios));
|
||||
|
||||
$termios = clone $original_termios;
|
||||
|
||||
$termios->c_lflag &= ~(_ECHO | ICANON);
|
||||
|
||||
// Turn on raw mode
|
||||
$ffi->tcsetattr(STDIN_FILENO, TCSAFLUSH, FFI::addr($termios));
|
||||
}
|
||||
|
||||
function disableRawMode(): void
|
||||
{
|
||||
global $ffi;
|
||||
global $original_termios;
|
||||
|
||||
$ffi->tcsetattr(STDIN_FILENO, TCSAFLUSH, FFI::addr($original_termios));
|
||||
}
|
||||
|
||||
function read_stdin() {
|
||||
$handle = fopen('php://stdin', 'r');
|
||||
$input = rtrim(fgets($handle, 128));
|
||||
fclose($handle);
|
||||
|
||||
return $input;
|
||||
}
|
Loading…
Reference in New Issue
Block a user