Compare commits

...

40 Commits

Author SHA1 Message Date
marjohnsen
e88cb5f36b
Add void to empty parameter list (#9) 2025-01-12 14:04:56 +01:00
Jiho Park
5af96beec6
Fix language info name to c to get correct syntax highlighting. (#8) 2024-05-20 20:25:15 +02:00
Xaver K
ca79b349d9
Added support for changing the standard using a magic. (#5) 2022-10-26 18:04:01 +02:00
Xaver K
ddce20b4ca
Fixed crash when magic line is empty. (#4) 2022-10-26 17:29:32 +02:00
Xaver K
66cbe9b2ca Set bufferedOutput=True since that is the usual command line behaviour. Updated README. 2021-02-25 12:50:14 +01:00
Xaver K
4f8d436439 Fixed scanf for C89. Small improvements for newer standards. 2020-05-12 12:28:40 +02:00
Xaver K
dddbfe9e85 Ditched C89 support for number of chars read by scanf. 2020-05-11 19:53:02 +02:00
Xaver K
449e682b18 Introduced buffered output option to replicate terminal behaviour. 2020-05-11 19:03:22 +02:00
Xaver K
7c39e852ef Fixed delayed stderr output. 2020-05-08 15:53:37 +02:00
Xaver K
8e97b754aa
Support for other standards than c11 (#3)
* Finished C89/C90/C95/C99 support. Version can only be switched in kernel or with magics. Might be worth having separate kernels for each standard.

* Added option to link maths library. On by default.

* Fixed empty input error. Now input loops until there is input with length > 0.
2020-04-17 19:27:16 +02:00
Xaver K
f8e99ec24c Fixed combined getchar and scanf input to mimic command line. 2020-04-09 13:23:56 +02:00
Xaver K
ecbea115c7 Cleanup. 2020-04-03 14:07:04 +02:00
Xaver K
3429b362ba Added input buffer in stdio_wrap.h to mimic command line behaviour. Added wait on output to avoid race condition with program termination. 2020-04-03 13:48:32 +02:00
Xaver K
bddb7ca6b0
Static server (#2)
* Wrap input and file output functions for a read only static server.

* Added -y to docker apt-get in docker file. 

*  wError/wAll as options in Kernel, defaults only to wAll.
2020-03-27 12:27:14 +01:00
Xaver K
8b69d8354d Added stdio_wrap.h to MANIFEST to have it copied during install. 2020-03-27 11:26:35 +01:00
Xaver K
ff9d635f89
Stdin using notebook raw_input (#1)
* Added stdin input via the notebook frontend

* Added support for single line code snippets.

* Updated README. 

* Updated Dockerfile. 

* Changed file cleanup to happen right after execution, rather than before kernel shutdown.
2020-03-22 12:52:45 +01:00
Brendan Rius
c9a327f77c
Merge pull request #46 from QuLogic/sdist-info
Add some metdata into sdist
2018-06-03 22:21:48 +02:00
Brendan Rius
4233523364
Merge branch 'master' into sdist-info 2018-06-03 22:21:38 +02:00
Brendan Rius
2c2ce0126f
Merge pull request #49 from ntwuxc/patch-1
Update setup.py
2018-06-03 22:19:37 +02:00
mycode
749be19e4d
Update setup.py 2018-06-03 08:13:32 +08:00
Elliott Sales de Andrade
eb6aacf4ce Add license classifier for PyPI. 2018-03-15 02:21:13 -04:00
Elliott Sales de Andrade
1b93007021 Include readme and license in sdist. 2018-03-15 02:14:35 -04:00
Brendan Rius
ca942437f6
Merge pull request #40 from halfhorst/master
add resources/ as package data
2018-01-24 11:01:54 +01:00
Cody Horst
60cbeb8021 no editable pip installation 2018-01-23 23:04:53 -08:00
Cody Horst
d29d9e8167 alter package structure, moving resources under parent module. 2018-01-23 22:34:24 -08:00
Cody Horst
d02720cc84 update master.c path to correspond to where pip install will put it 2017-12-08 20:17:56 -08:00
Cody Horst
f0fbd41b96 add resources as package data. Probably bork current Dockerfile 2017-12-08 19:34:26 -08:00
Brendan Rius
c0d71813a5 Bump to 1.2.1 2017-08-13 17:03:43 +02:00
Brendan Rius
1b7f9d4dd6 Update README.md 2017-08-13 16:25:27 +02:00
Brendan Rius
c7a966954c Merge branch 'master' of github.com:brendan-rius/jupyter-c-kernel 2017-08-13 16:04:05 +02:00
Brendan Rius
a360b41cb6 Fix install script to use name 'c' 2017-08-13 15:56:03 +02:00
Brendan Rius
3839e6f87b Update README.md
Remove useless VCR contributing rules
2017-08-11 00:23:21 +02:00
Brendan Rius
e65c6b1a79 Merge pull request #32 from jb08/patch-1
Update README typo and clarification
2017-08-10 22:53:11 +02:00
Jason B
2ffb724f39 Update README typo and clarification
fixes typo, and provides example for "URL containing the token".
2017-08-10 14:00:51 -05:00
Brendan Rius
a90e1318c6 Updated README 2017-08-10 17:29:41 +02:00
Brendan Rius
3f584f7a22 Updated README 2017-08-10 17:22:37 +02:00
Brendan Rius
cba22be395 Fix docker build 2017-08-10 17:13:34 +02:00
Brendan Rius
9ec152dda1 Remove easy install script as installation method changed 2017-08-10 17:05:45 +02:00
Brendan Rius
28604bc230 Bump to 1.2.0 2017-08-10 17:01:40 +02:00
Brendan Rius
c08039f6ad Change install procedure 2017-08-10 17:00:32 +02:00
12 changed files with 607 additions and 101 deletions

View File

@ -1,15 +1,19 @@
FROM jupyter/minimal-notebook
MAINTAINER Brendan Rius <ping@brendan-rius.com>
MAINTAINER Xaver Klemenschits <klemenschits@iue.tuwien.ac.at>
USER root
RUN mkdir /jupyter
# Install vim and ssh
RUN apt-get update
RUN apt-get install -y vim openssh-client
WORKDIR /jupyter
WORKDIR /tmp
COPY ./ jupyter_c_kernel/
RUN pip install -e jupyter_c_kernel/
RUN jupyter-kernelspec install jupyter_c_kernel/c_spec/
RUN pip install --no-cache-dir -e jupyter_c_kernel/ > piplog.txt
RUN cd jupyter_c_kernel && install_c_kernel --user > installlog.txt
WORKDIR /home/$NB_USER/
USER $NB_USER

3
MANIFEST.in Normal file
View File

@ -0,0 +1,3 @@
include jupyter_c_kernel/resources/master.c
include jupyter_c_kernel/resources/stdio_wrap.h
include README.md LICENSE.txt

View File

@ -1,34 +1,63 @@
# Minimal C kernel for Jupyter
# C kernel for Jupyter
This project was forked from [https://github.com/brendan-rius/jupyter-c-kernel](brendan-rius/jupyter-c-kernel) as that project seems to have been abandoned. (PR is pending)
This project includes fixes to many issues reported in [https://github.com/brendan-rius/jupyter-c-kernel](brendan-rius/jupyter-c-kernel), as well as the following additional features:
* Option for buffered output to mimic command line behaviour (useful for teaching, default is on)
* Command line input via `scanf` and `getchar`
* Support for `C89`/`ANSI C` (all newer versions were already supported and still are)
Following limitations compared to command line execution exist:
* Input is always buffered due to limitations of the jupyter interface
* When using `-ansi` or `-std=C89`, glibc still has to support at least `C99` for the interfacing with jupyter (this should not be an issue on an OS made after 2000)
## Use with Docker (recommended)
* `docker pull brendanrius/jupyter-c-kernel`
* `docker run -d -p 8888:8888 brendanrius/jupyter-c-kernel`
* Go to [http://localhost:8888](http://localhost:8888) (or your VM address if you are using Docker Machine)
* `docker pull xaverklemenschits/jupyter-c-kernel`
* `docker run -p 8888:8888 xaverklemenschits/jupyter-c-kernel`
* Copy the given URL containing the token, and browse to it. For instance:
```bash
Copy/paste this URL into your browser when you connect for the first time,
to login with a token:
http://localhost:8888/?token=66750c80bd0788f6ba15760aadz53beb9a9fb4cf8ac15ce8
```
## Manual installation
* Make sure you have the following requirements installed:
Works only on Linux and OS X. Windows is not supported yet. If you want to use this project on Windows, please use Docker.
* Make sure you have the following requirements installed:
* gcc
* jupyter
* python 3
* pip
### Step-by-step:
* `pip install jupyter-c-kernel`
* `git clone https://github.com/brendan-rius/jupyter-c-kernel.git`
* `cd jupyter-c-kernel`
* `jupyter-kernelspec install c_spec/`
* `jupyter-notebook`. Enjoy!
### Step-by-step
### Easy installation for Unix user:
* `wget -O - https://raw.githubusercontent.com/brendan-rius/jupyter-c-kernel/master/install.sh | sh`
```bash
git clone https://github.com/XaverKlemenschits/jupyter-c-kernel.git
cd jupyter-c-kernel
pip install -e . # for system install: sudo install .
cd jupyter_c_kernel && install_c_kernel --user # for sys install: sudo install_c_kernel
# now you can start the notebook
jupyter notebook
```
## Example of notebook
![Example of notebook](example-notebook.png?raw=true "Example of notebook")
## Custom compilation flags
You can use custom compilation flags like so:
![Custom compulation flag](custom_flags.png?raw=true "Example of notebook using custom compilation flags")
Here, the `-lm` flag is passed so you can use the math library.
## Contributing
The docker image installs the kernel in editable mode, meaning that you can
@ -36,22 +65,18 @@ change the code in real-time in Docker. For that, just run the docker box like
that:
```bash
git clone https://github.com/brendan-rius/jupyter-c-kernel.git
git clone https://github.com/XaverKlemenschits/jupyter-c-kernel.git
cd jupyter-c-kernel
docker run -v $(pwd):/jupyter/jupyter_c_kernel/ -p 8888:8888 brendanrius/jupyter-c-kernel
docker build -t myName/jupyter .
docker run -v $(pwd):/tmp/jupyter_c_kernel/ -p 8888:8888 myName/jupyter
```
This clones the source, run the kernel, and binds the current folder (the one
you just cloned) to eh corresponding folder in Docker.
you just cloned) to the corresponding folder in Docker.
Now, if you change the source, it will be reflected in [http://localhost:8888](http://localhost:8888)
instantly. Do not forget to click "restart" the kernel on the page as it does
not auto-restart.
### Version control
Create branches named `issue-X` where `X` is the number of the issue.
Rebase instead of merge.
## License
[MIT](LICENSE.txt)

View File

@ -1,11 +0,0 @@
{
"argv": [
"python3",
"-m",
"jupyter_c_kernel",
"-f",
"{connection_file}"
],
"display_name": "C",
"language": "c"
}

BIN
custom_flags.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -10,9 +10,7 @@
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"name": "stderr",
@ -32,6 +30,7 @@
"\n",
"int main() {\n",
" printf(\"Hello world\\n\");\n",
" return 0;\n",
"}"
]
},
@ -52,9 +51,7 @@
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"name": "stderr",
@ -98,9 +95,7 @@
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"name": "stderr",
@ -142,14 +137,14 @@
"kernelspec": {
"display_name": "C",
"language": "c",
"name": "c_kernel"
"name": "c"
},
"language_info": {
"file_extension": "c",
"file_extension": ".c",
"mimetype": "text/plain",
"name": "c"
"name": "text/x-c++src"
}
},
"nbformat": 4,
"nbformat_minor": 0
"nbformat_minor": 1
}

View File

@ -1,30 +0,0 @@
echo " ___ _ _____ _ __ _ "
echo " |_ | | | / __ \ | | / / | |"
echo " | |_ _ _ __ _ _| |_ ___ _ __ | / \/ | |/ / ___ _ __ _ __ ___| |"
echo " | | | | | '_ \| | | | __/ _ \ '__| | | | \ / _ \ '__| '_ \ / _ \ |"
echo "/\__/ / |_| | |_) | |_| | || __/ | | \__/\ | |\ \ __/ | | | | | __/ |"
echo "\____/ \__._| .__/ \__. |\__\___|_| \____/ \_| \_/\___|_| |_| |_|\___|_|"
echo " | | __/ | "
echo " |_| |___/ "
tag_name="$1"
repository="${2:-https://github.com/brendan-rius/jupyter-c-kernel.git}"
repo_name="jupyter-c-kernel"
set -x
echo ":: Installing python module C kernel."
pip install $repo_name; echo "Done. "
echo ":: Cloning Jupyter C-kernel... "
git clone $repository $repo_name; echo "Done. "
cd $repo_name
if [ ! -z "$tag_name" ]; then
echo ":: Using tag $tag_name"
git checkout "$tag_name"; echo "Done."
fi
echo ":: Installing kernel specification"
jupyter-kernelspec install c_spec/ ; echo "Done."
echo ":: Removing repository"
cd ..
rm -rf jupyter-c-kernel/
echo "Completed! Installation successful. You can type jupyter-notebook and be happy"

View File

@ -0,0 +1,81 @@
#!/usr/bin/env python
import json
import os
import sys
import argparse
from jupyter_client.kernelspec import KernelSpecManager
from IPython.utils.tempdir import TemporaryDirectory
kernel_json = {
"argv": [
"python3",
"-m",
"jupyter_c_kernel",
"-f",
"{connection_file}"
],
"display_name": "C",
"language": "c"
}
def install_my_kernel_spec(user=True, prefix=None):
with TemporaryDirectory() as td:
os.chmod(td, 0o755) # Starts off as 700, not user readable
with open(os.path.join(td, 'kernel.json'), 'w') as f:
json.dump(kernel_json, f, sort_keys=True)
# TODO: Copy resources once they're specified
print('Installing IPython kernel spec')
KernelSpecManager().install_kernel_spec(td, 'c', user=user, prefix=prefix)
def _is_root():
try:
return os.geteuid() == 0
except AttributeError:
return False # assume not an admin on non-Unix platforms
def main(argv=[]):
parser = argparse.ArgumentParser(
description='Install KernelSpec for C Kernel'
)
prefix_locations = parser.add_mutually_exclusive_group()
prefix_locations.add_argument(
'--user',
help='Install KernelSpec in user homedirectory',
action='store_false' if _is_root() else 'store_true'
)
prefix_locations.add_argument(
'--sys-prefix',
help='Install KernelSpec in sys.prefix. Useful in conda / virtualenv',
action='store_true',
dest='sys_prefix'
)
prefix_locations.add_argument(
'--prefix',
help='Install KernelSpec in this prefix',
default=None
)
args = parser.parse_args()
if args.sys_prefix:
prefix = sys.prefix
user = None
elif args.user:
prefix = None
user = True
else:
prefix = args.prefix
user = None
install_my_kernel_spec(user=user, prefix=prefix)
if __name__ == '__main__':
main(argv=sys.argv)

View File

@ -14,7 +14,9 @@ class RealTimeSubprocess(subprocess.Popen):
A subprocess that allows to read its stdout and stderr in real time
"""
def __init__(self, cmd, write_to_stdout, write_to_stderr):
inputRequest = "<inputRequest>"
def __init__(self, cmd, write_to_stdout, write_to_stderr, read_from_stdin):
"""
:param cmd: the command to execute
:param write_to_stdout: a callable that will be called with chunks of data from stdout
@ -22,8 +24,9 @@ class RealTimeSubprocess(subprocess.Popen):
"""
self._write_to_stdout = write_to_stdout
self._write_to_stderr = write_to_stderr
self._read_from_stdin = read_from_stdin
super().__init__(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
super().__init__(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0)
self._stdout_queue = Queue()
self._stdout_thread = Thread(target=RealTimeSubprocess._enqueue_output, args=(self.stdout, self._stdout_queue))
@ -58,12 +61,28 @@ class RealTimeSubprocess(subprocess.Popen):
size -= 1
return res
stdout_contents = read_all_from_queue(self._stdout_queue)
if stdout_contents:
self._write_to_stdout(stdout_contents)
stderr_contents = read_all_from_queue(self._stderr_queue)
if stderr_contents:
self._write_to_stderr(stderr_contents)
self._write_to_stderr(stderr_contents.decode())
stdout_contents = read_all_from_queue(self._stdout_queue)
if stdout_contents:
contents = stdout_contents.decode()
# if there is input request, make output and then
# ask frontend for input
start = contents.find(self.__class__.inputRequest)
if(start >= 0):
contents = contents.replace(self.__class__.inputRequest, '')
if(len(contents) > 0):
self._write_to_stdout(contents)
readLine = ""
while(len(readLine) == 0):
readLine = self._read_from_stdin()
# need to add newline since it is not captured by frontend
readLine += "\n"
self.stdin.write(readLine.encode())
else:
self._write_to_stdout(contents)
class CKernel(Kernel):
@ -72,24 +91,41 @@ class CKernel(Kernel):
language = 'c'
language_version = 'C11'
language_info = {'name': 'c',
'mimetype': 'text/plain',
'mimetype': 'text/x-csrc',
'file_extension': '.c'}
banner = "C kernel.\n" \
"Uses gcc, compiles in C11, and creates source code files and executables in temporary folder.\n"
main_head = "#include <stdio.h>\n" \
"#include <math.h>\n" \
"int main(){\n"
main_foot = "\nreturn 0;\n}"
def __init__(self, *args, **kwargs):
super(CKernel, self).__init__(*args, **kwargs)
self._allow_stdin = True
self.readOnlyFileSystem = False
self.bufferedOutput = True
self.linkMaths = True # always link math library
self.wAll = True # show all warnings by default
self.wError = False # but keep comipiling for warnings
self.standard = "c11" # default standard if none is specified
self.files = []
mastertemp = tempfile.mkstemp(suffix='.out')
os.close(mastertemp[0])
self.master_path = mastertemp[1]
filepath = path.join(path.dirname(path.realpath(__file__)), '..', 'resources', 'master.c')
self.resDir = path.join(path.dirname(path.realpath(__file__)), 'resources')
filepath = path.join(self.resDir, 'master.c')
subprocess.call(['gcc', filepath, '-std=c11', '-rdynamic', '-ldl', '-o', self.master_path])
def cleanup_files(self):
"""Remove all the temporary files created by the kernel"""
# keep the list of files create in case there is an exception
# before they can be deleted as usual
for file in self.files:
os.remove(file)
if(os.path.exists(file)):
os.remove(file)
os.remove(self.master_path)
def new_temp_file(self, **kwargs):
@ -107,13 +143,27 @@ class CKernel(Kernel):
def _write_to_stderr(self, contents):
self.send_response(self.iopub_socket, 'stream', {'name': 'stderr', 'text': contents})
def _read_from_stdin(self):
return self.raw_input()
def create_jupyter_subprocess(self, cmd):
return RealTimeSubprocess(cmd,
lambda contents: self._write_to_stdout(contents.decode()),
lambda contents: self._write_to_stderr(contents.decode()))
self._write_to_stdout,
self._write_to_stderr,
self._read_from_stdin)
def compile_with_gcc(self, source_filename, binary_filename, cflags=None, ldflags=None):
cflags = ['-std=c11', '-fPIC', '-shared', '-rdynamic'] + cflags
cflags = ['-pedantic', '-fPIC', '-shared', '-rdynamic'] + cflags
if self.linkMaths:
cflags = cflags + ['-lm']
if self.wError:
cflags = cflags + ['-Werror']
if self.wAll:
cflags = cflags + ['-Wall']
if self.readOnlyFileSystem:
cflags = ['-DREAD_ONLY_FILE_SYSTEM'] + cflags
if self.bufferedOutput:
cflags = ['-DBUFFERED_OUTPUT'] + cflags
args = ['gcc', source_filename] + cflags + ['-o', binary_filename] + ldflags
return self.create_jupyter_subprocess(args)
@ -123,9 +173,16 @@ class CKernel(Kernel):
'ldflags': [],
'args': []}
actualCode = ''
for line in code.splitlines():
if line.startswith('//%'):
key, value = line[3:].split(":", 2)
magicSplit = line[3:].split(":", 2)
if(len(magicSplit) < 2):
self._write_to_stderr("[C kernel] Magic line starting with '//%' is missing a semicolon, ignoring.")
continue
key, value = magicSplit
key = key.strip().lower()
if key in ['ldflags', 'cflags']:
@ -136,12 +193,45 @@ class CKernel(Kernel):
for argument in re.findall(r'(?:[^\s,"]|"(?:\\.|[^"])*")+', value):
magics['args'] += [argument.strip('"')]
return magics
# always add empty line, so line numbers don't change
actualCode += '\n'
# keep lines which did not contain magics
else:
actualCode += line + '\n'
# add default standard if cflags does not contain one
if not any(item.startswith('-std=') for item in magics["cflags"]):
magics["cflags"] += ["-std=" + self.standard]
return magics, actualCode
# check whether int main() is specified, if not add it around the code
# also add common magics like -lm
def _add_main(self, magics, code):
# remove comments
tmpCode = re.sub(r"//.*", "", code)
tmpCode = re.sub(r"/\*.*?\*/", "", tmpCode, flags=re.M|re.S)
x = re.search(r"int\s+main\s*\(", tmpCode)
if not x:
code = self.main_head + code + self.main_foot
magics['cflags'] += ['-lm']
return magics, code
def do_execute(self, code, silent, store_history=True,
user_expressions=None, allow_stdin=False):
user_expressions=None, allow_stdin=True):
magics = self._filter_magics(code)
magics, code = self._filter_magics(code)
magics, code = self._add_main(magics, code)
# replace stdio with wrapped version
headerDir = "\"" + self.resDir + "/stdio_wrap.h" + "\""
code = code.replace("<stdio.h>", headerDir)
code = code.replace("\"stdio.h\"", headerDir)
with self.new_temp_file(suffix='.c') as source_file:
source_file.write(code)
@ -155,14 +245,28 @@ class CKernel(Kernel):
self._write_to_stderr(
"[C kernel] GCC exited with code {}, the executable will not be executed".format(
p.returncode))
# delete source files before exit
os.remove(source_file.name)
os.remove(binary_file.name)
return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [],
'user_expressions': {}}
p = self.create_jupyter_subprocess([self.master_path, binary_file.name] + magics['args'])
while p.poll() is None:
p.write_contents()
# wait for threads to finish, so output is always shown
p._stdout_thread.join()
p._stderr_thread.join()
p.write_contents()
# now remove the files we have just created
os.remove(source_file.name)
os.remove(binary_file.name)
if p.returncode != 0:
self._write_to_stderr("[C kernel] Executable exited with code {}".format(p.returncode))
return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}}

View File

@ -0,0 +1,329 @@
#ifndef STDIO_WRAP_H
#define STDIO_WRAP_H
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
/* Figure out used C standard.
__STDC_VERSION__ is not always defined until C99.
If it is not defined, set standard to C89.
It is safest to set it by hand, to make sure */
#ifdef __STDC_VERSION__
#if __STDC_VERSION__ <= 199409L
#define C89_SUPPORT
#endif /* __STDC_VERSION__ <= 199409L */
#else /* __STDC_VERSION__ */
#define C89_SUPPORT
#endif /* __STDC_VERSION__ */
/* output functions to replicate terminal behaviour */
#ifdef BUFFERED_OUTPUT
/* buffer for all output */
/* TODO allocate this dynamically */
static char outputBuff[1<<10] = "";
static char attachedOutputFlush = 0;
void flush_all_output() {
printf("%s", outputBuff);
fflush(stdout);
outputBuff[0] = '\0';
}
/* Flush all output on exit */
void attachOutputFlush() {
if(attachedOutputFlush == 0){
int error = atexit(flush_all_output);
if(error != 0) {
fprintf(stderr, "ERROR: Could not set exit function! Error %d\n", error);
}
attachedOutputFlush = 1;
}
}
/* this function is called to check whether there
is a '\n' in the output that should be flushed */
void flush_until_newline() {
long i = 0;
long length = strlen(outputBuff);
for(; i < length; ++i) {
if(outputBuff[i] == '\n') {
char *printBuff = malloc(i+2);
strncpy(printBuff, outputBuff, i+1);
printBuff[i+1] = '\0';
printf("%s", printBuff);
free(printBuff);
/* now remove the printed string from the buffer
and start again */
{
long a = 0;
++i;
/* +1 to include \0 */
for(; i < length + 1; ++a, ++i) {
outputBuff[a] = outputBuff[i];
}
i = 0;
length = strlen(outputBuff);
}
}
}
}
/* for printf, print all to a string.
Then cycle through all chars and see if \n is
written. If there is one, flush the output, otherwise
write to buffer */
int printf_wrap(const char *format, ...) {
/* append output to buffer */
va_list arglist;
int result;
va_start( arglist, format );
result = vsprintf(outputBuff + strlen(outputBuff), format, arglist);
va_end( arglist );
/* Now flush if there is a reason to */
flush_until_newline();
attachOutputFlush();
return result;
}
int putchar_wrap(int c) {
long length = strlen(outputBuff);
outputBuff[length] = (char)c;
outputBuff[length+1] = '\0';
if(c == '\n') {
flush_until_newline();
}
attachOutputFlush();
return c;
}
int fflush_wrap(FILE* stream) {
if(stream == stdout) {
flush_all_output();
}
return fflush(stream);
}
int fclose_wrap(FILE* stream) {
if(stream == stdout) {
flush_all_output();
}
return fclose(stream);
}
#endif /* BUFFERED_OUTPUT */
/* Need input buffer to know whether we need another input request */
/* TODO allocate this dynamically */
static char inputBuff[1<<10] = "";
static long scanf_wrap_number_read = 0;
/* read remaining input into buffer so it can be used in next call */
void readIntoBuffer(void) {
long length = strlen(inputBuff);
char nextChar = 0;
while((nextChar = getchar()) != '\n' && nextChar != EOF){
inputBuff[length++] = nextChar;
}
inputBuff[length++] = '\n';
inputBuff[length] = '\0';
}
/* check whether input request is needed */
char checkInputRequest(void) {
const long length = strlen(inputBuff);
long i = 0;
for(; i < length && isspace(inputBuff[i]); ++i);
return i == length;
}
/* Define the input functions to overload the old ones */
/* Wrapping of scanf depends on standard */
#ifdef C89_SUPPORT
/* Need to define vscanf for c89.
TODO: This is a bit risky, since the underlying glibc does not
have to include this if it is old. If it does not, linking will fail.
The robust way would be readin via sscanf. */
/* Read formatted input from stdin into argument list ARG.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern int vsscanf (const char *__restrict __s,
const char *__restrict __format, _G_va_list __arg)
__THROW __attribute__ ((__format__ (__scanf__, 2, 0)));
/* replace all % with %* to suppress read in and do test run */
long find_scanf_length(const char *format) {
const long length = strlen(format);
/* allow for maximum of 50 format specifiers */
char *formatString = malloc(length + 53);
long index = 0;
long formatIndex = 0;
for(; index < length; ++index, ++formatIndex) {
formatString[formatIndex] = format[index];
if(format[index] == '%' &&
(index + 1 < length && format[index + 1] != '%')) {
formatString[++formatIndex] = '*';
}
}
/* add number readin */
formatString[formatIndex++] = '%';
formatString[formatIndex++] = 'n';
formatString[formatIndex] = '\0';
/* now run and record how many characters were read */
{
int readLength = 0;
sscanf(inputBuff, formatString, &readLength);
free(formatString);
return readLength;
}
}
#endif /* C89_SUPPORT */
int scanf_wrap(const char *format, ...) {
char doRequest = checkInputRequest();
char *formatString = 0;
if(doRequest) {
#ifdef BUFFERED_OUTPUT
flush_all_output();
#endif
printf("<inputRequest>");
fflush(stdout);
/* read everything from stdin into buffer */
readIntoBuffer();
}
/* add %n to format string to get number of written chars */
{
const long length = strlen(format);
formatString = malloc(length + 3);
strcpy(formatString, format);
#ifndef C89_SUPPORT
formatString[length] = '%';
formatString[length + 1] = 'n';
formatString[length + 2] = '\0';
#else /* C89_SUPPORT */
formatString[length] = '\0';
/* In C89 we need to find how far scanf will read, by hand */
scanf_wrap_number_read = find_scanf_length(format);
#endif /* C89_SUPPORT */
}
{
va_list arglist;
int result;
va_start(arglist, format);
result = vsscanf(inputBuff, formatString, arglist);
va_end(arglist);
/* now move inputBuff up */
{
const long length = strlen(inputBuff);
long index = scanf_wrap_number_read;
long a = 0;
/* +1 to include \0 */
for(; index < length + 1; ++a, ++index) {
inputBuff[a] = inputBuff[index];
}
}
free(formatString);
return result;
}
}
int getchar_wrap(void){
/* check if there is still something in the input buffer*/
char input = 0;
long length = strlen(inputBuff);
if(length <= 0) {
#ifdef BUFFERED_OUTPUT
flush_all_output();
#endif
printf("<inputRequest>");
fflush(stdout);
readIntoBuffer();
}
input = inputBuff[0];
{
long i = 1;
long length = strlen(inputBuff) + 1;
/* shift all chars one to the left */
for(; i < length; ++i){
inputBuff[i-1] = inputBuff[i];
if(inputBuff[i] == '\0') {
break;
}
}
}
return input;
}
/* Replace all the necessary input functions
depending on the language version used */
#ifndef C89_SUPPORT
/* Need double hashes in case there are no __VA_ARGS__*/
#define scanf(format, ...) scanf_wrap(format, ##__VA_ARGS__, &scanf_wrap_number_read)
#else /* C89_SUPPORT */
/* Since there are no variadic macros in C89, this is the only way
although it is horrible */
#define scanf scanf_wrap
#endif /* C89_SUPPORT */
#define getchar() getchar_wrap()
/* Output defines */
#ifdef BUFFERED_OUTPUT
#define printf printf_wrap
#define putchar putchar_wrap
#define fflush fflush_wrap
#define fclose fclose_wrap
#endif /* BUFFERED_OUTPUT */
/* Replace FILE write operations for read-only systems */
#ifdef READ_ONLY_FILE_SYSTEM
/* Define wrapping functions */
/* Output that the fopen succeeded and return some valid pointer */
FILE *fopen_wrap(const char *filename, const char *modes) {
static long stream = 0x1FFFF0000;
#ifdef SHOW_FILE_IO_VERBOSE
printf("\x01b[42m");
printf("\"%s\" opened in mode \"%s\"\n", filename, modes);
printf("\x01b[0m");
#endif /* SHOW_FILE_IO_VERBOSE */
return (FILE*)stream++;
}
int fprintf_wrap(FILE* stream, const char* format, ...) {
printf("\x01b[42m");
printf("%p:", stream);
printf("\x01b[0m");
va_list arglist;
va_start( arglist, format );
int result = vprintf(format, arglist);
va_end( arglist );
return result;
}
/* Replace all the necessary input functions */
#define fopen(file, mode) fopen_wrap(file, mode)
#define fprintf(stream, format, ...) fprintf_wrap(stream, format, ##__VA_ARGS__)
#endif /* READ_ONLY_FILE_SYSTEM */
#endif /* STDIO_WRAP_H */

View File

@ -1,12 +1,18 @@
from setuptools import setup
setup(name='jupyter_c_kernel',
version='1.1.0',
version='1.2.1',
description='Minimalistic C kernel for Jupyter',
author='Brendan Rius',
author_email='ping@brendan-rius.com',
url='https://github.com/brendanrius/jupyter-c-kernel/',
download_url='https://github.com/brendanrius/jupyter-c-kernel/tarball/1.1.0',
license='MIT',
classifiers=[
'License :: OSI Approved :: MIT License',
],
url='https://github.com/brendan-rius/jupyter-c-kernel/',
download_url='https://github.com/brendan-rius/jupyter-c-kernel/tarball/1.2.1',
packages=['jupyter_c_kernel'],
keywords=['jupyter', 'kernel', 'c']
scripts=['jupyter_c_kernel/install_c_kernel'],
keywords=['jupyter', 'notebook', 'kernel', 'c'],
include_package_data=True
)