First commit, about step 8 in tutorial

This commit is contained in:
Timothy Warren 2019-10-10 12:28:46 -04:00
commit 8522d9fb33
5 changed files with 437 additions and 0 deletions

169
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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;
}