From 8e97b754aaef36293efea7e777593306621948e0 Mon Sep 17 00:00:00 2001
From: Xaver K <klemenschits@iue.tuwien.ac.at>
Date: Fri, 17 Apr 2020 19:27:16 +0200
Subject: [PATCH] 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.
---
 example-notebook.ipynb                  | 21 +++-----
 jupyter_c_kernel/kernel.py              | 12 ++++-
 jupyter_c_kernel/resources/stdio_wrap.h | 72 ++++++++++++++++++++-----
 3 files changed, 78 insertions(+), 27 deletions(-)

diff --git a/example-notebook.ipynb b/example-notebook.ipynb
index dcd2844..95b78b5 100644
--- a/example-notebook.ipynb
+++ b/example-notebook.ipynb
@@ -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
 }
diff --git a/jupyter_c_kernel/kernel.py b/jupyter_c_kernel/kernel.py
index 99243fc..6194234 100644
--- a/jupyter_c_kernel/kernel.py
+++ b/jupyter_c_kernel/kernel.py
@@ -71,7 +71,9 @@ class RealTimeSubprocess(subprocess.Popen):
                 contents = contents.replace(self.__class__.inputRequest, '')
                 if(len(contents) > 0):
                     self._write_to_stdout(contents)
-                readLine = self._read_from_stdin()
+                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())
@@ -104,6 +106,7 @@ class CKernel(Kernel):
         super(CKernel, self).__init__(*args, **kwargs)
         self._allow_stdin = True
         self.readOnlyFileSystem = False
+        self.linkMaths = True # always link math library
         self.wAll = True # show all warnings by default
         self.wError = False # but keep comipiling for warnings
         self.files = []
@@ -148,7 +151,12 @@ class CKernel(Kernel):
                                   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 = ['-std=c89', '-pedantic', '-fPIC', '-shared', '-rdynamic'] + cflags
+        # cflags = ['-std=iso9899:199409', '-pedantic', '-fPIC', '-shared', '-rdynamic'] + cflags
+        # cflags = ['-std=c99', '-pedantic', '-fPIC', '-shared', '-rdynamic'] + cflags
+        cflags = ['-std=c11', '-pedantic', '-fPIC', '-shared', '-rdynamic'] + cflags
+        if self.linkMaths:
+            cflags = cflags + ['-lm']
         if self.wError:
             cflags = cflags + ['-Werror']
         if self.wAll:
diff --git a/jupyter_c_kernel/resources/stdio_wrap.h b/jupyter_c_kernel/resources/stdio_wrap.h
index 8a365d8..e079947 100644
--- a/jupyter_c_kernel/resources/stdio_wrap.h
+++ b/jupyter_c_kernel/resources/stdio_wrap.h
@@ -5,8 +5,20 @@
 #include <stdarg.h>
 #include <string.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__ */
+
 /* Need input buffer to know whether we need another input request */
-char inputBuff[1<<10] = "";
+static char inputBuff[1<<10] = "";
 
 /* read remaining input into buffer so it can be used in next call */
 void readIntoBuffer() {
@@ -19,8 +31,8 @@ void readIntoBuffer() {
   inputBuff[length] = '\0';
 }
 
-/* Define the functions to overload the old ones */
-int scanf_wrap(const char *format, ...) {
+/* check whether input request is needed */
+char checkInputRequest() {
   /* unget chars in buffer */
   char doRequest = 1;
   char leadingNewline = 1;
@@ -38,21 +50,49 @@ int scanf_wrap(const char *format, ...) {
     }
   }
 
+  return doRequest;
+}
+
+/* Define the 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 only safe way is reimplementing the whole function. */
+
+/* Read formatted input from stdin into argument list ARG.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern int vscanf (const char *__restrict __format, _G_va_list __arg)
+     __attribute__ ((__format__ (__scanf__, 1, 0))) __wur;
+#endif /* C89_SUPPORT */
+
+int scanf_wrap(const char *format, ...) {
+  char doRequest = checkInputRequest();
+
   if(doRequest) {
     printf("<inputRequest>");
     fflush(stdout);
   }
-  va_list arglist;
-  va_start( arglist, format );
-  int result = vscanf( format, arglist );
-  va_end( arglist );
 
-  /* Put the remaining input into the input buffer */
-  readIntoBuffer();
+  {
+    va_list arglist;
+    int result;
+    va_start( arglist, format );
+    result = vscanf( format, arglist );
+    va_end( arglist );
 
-  return result;
+    /* Put the remaining input into the input buffer */
+    readIntoBuffer();
+
+    return result;
+  }
 }
 
+
 int getchar_wrap(){
   /* check if there is still something in the input buffer*/
   char input = 0;
@@ -64,9 +104,10 @@ int getchar_wrap(){
     input = getchar();
     readIntoBuffer();
   } else {
+    int i = 1;
+
     input = inputBuff[0];
     /* shift all chars one to the left */
-    int i = 1;
     for(; i < 100; ++i){
       inputBuff[i-1] = inputBuff[i];
       if(inputBuff[i] == '\0') {
@@ -79,8 +120,15 @@ int getchar_wrap(){
 }
 
 /* Replace all the necessary input functions
-  Need double hashes in case there are no __VA_ARGS__*/
+  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__)
+#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()