Add support for attempting returns from panic
[akaros.git] / kern / src / printfmt.c
index a3bd013..b64625e 100644 (file)
@@ -1,17 +1,14 @@
 // Stripped-down primitive printf-style formatting routines,
 // used in common by printf, sprintf, fprintf, etc.
 // This code is also used by both the kernel and user programs.
-
-#ifdef __SHARC__
-#pragma nosharc
-#endif
-
 #include <ros/common.h>
 #include <error.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
 #include <kthread.h>
+#include <syscall.h>
+#include <ns.h>
 
 /* Print a number (base <= 16) in reverse order,
  * using specified putch function and associated pointer putdat. */
@@ -39,26 +36,18 @@ void printnum(void (*putch)(int, void**), void **putdat,
        }
 }
 
-// Main function to format and print a string.
-#ifdef __DEPUTY__
-void printfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...);
-#else
 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...);
-#endif
 
-#ifdef __DEPUTY__
-void vprintfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, va_list ap)
-#else
 void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap)
-#endif
 {
-       register const char *NTS p;
-       const char *NTS last_fmt;
+       register const char *p;
+       const char *last_fmt;
        register int ch, err;
        unsigned long long num;
        int base, lflag, width, precision, altflag;
        char padc;
        uint8_t *mac, *ip, *mask;
+       struct Gas *g;
        int i;
        uint32_t *lp;
 
@@ -132,22 +121,16 @@ void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_li
                        lflag++;
                        goto reswitch;
 
+               // chan
+               case 'C':
+                       printchan(putch, putdat, va_arg(ap, void*));
+                       break;
+
                // character
                case 'c':
                        putch(va_arg(ap, int), putdat);
                        break;
 
-               // error message
-               case 'e':
-                       err = va_arg(ap, int);
-                       if (err < 0)
-                               err = -err;
-                       if (err >= NUMERRORS)
-                               printfmt(putch, putdat, "error %d", err);
-                       else
-                               printfmt(putch, putdat, "%s", error_string[err]);
-                       break;
-
                case 'E': // ENET MAC
                        if ((mac = va_arg(ap, uint8_t *)) == NULL){
                                char *s = "00:00:00:00:00:00";
@@ -161,7 +144,7 @@ void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_li
                        if ((lp = va_arg(ap, uint32_t *)) != NULL){
                                uint32_t hostfmt;
                                for(i = 0; i < 4; i++){
-                                       hnputl(&lp[i], &hostfmt);
+                                       hnputl(&hostfmt, lp[i]);
                                        printfmt(putch, putdat, "%08lx", hostfmt);
                                }
                        }
@@ -244,6 +227,10 @@ void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_li
                        base = 16;
                        goto number;
 
+               // qid
+               case 'Q':
+                       printqid(putch, putdat, va_arg(ap, void*));
+                       break;
                number:
                        printnum(putch, putdat, num, base, width, padc);
                        break;
@@ -264,52 +251,52 @@ void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_li
        }
 }
 
-#ifdef __DEPUTY__
-void printfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...)
-#else
 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...)
-#endif
 {
        va_list ap;
 
        va_start(ap, fmt);
        vprintfmt(putch, putdat, fmt, ap);
        va_end(ap);
-       check_poison("printfmt");
 }
 
 typedef struct sprintbuf {
-       char *BND(__this,ebuf) buf;
-       char *SNT ebuf;
+       char *buf;
+       char *ebuf;
        int cnt;
 } sprintbuf_t;
 
-static void sprintputch(int ch, sprintbuf_t *NONNULL *NONNULL b)
+static void sprintputch(int ch, sprintbuf_t **b)
 {
-       (*b)->cnt++;
        if ((*b)->buf < (*b)->ebuf)
                *((*b)->buf++) = ch;
+       (*b)->cnt++;
 }
 
-int vsnprintf(char *buf, int n, const char *fmt, va_list ap)
+int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap)
 {
        sprintbuf_t b;// = {buf, buf+n-1, 0};
-       sprintbuf_t *COUNT(1) NONNULL bp = &b;
-
-       if (buf == NULL || n < 1)
-               return -EINVAL;
+       sprintbuf_t *bp = &b;
+
+       /* this isn't quite the snprintf 'spec', but errors aren't helpful */
+       assert(buf);
+       /* We might get large, 'negative' values for code that repeatedly calls
+        * snprintf(), e.g.:
+        *              len += snprintf(buf + len, bufsz - len, "foo");
+        *              len += snprintf(buf + len, bufsz - len, "bar");
+        * If len > bufsz, that will appear as a large value.  This is not quite the
+        * glibc semantics (we aren't returning the size we would have printed), but
+        * it short circuits the rest of the function and avoids potential errors in
+        * the putch() functions. */
+       if (!n || (n > INT32_MAX))
+               return 0;
 
        b.buf = NULL; // zra : help out the Deputy optimizer a bit
        b.ebuf = buf+n-1;
        b.cnt = 0;
        b.buf = buf;
 
-       // print the string to the buffer
-       #ifdef __DEPUTY__
-       vprintfmt((void*)sprintputch, (sprintbuf_t *NONNULL*NONNULL)&bp, fmt, ap);
-       #else
        vprintfmt((void*)sprintputch, (void*)&bp, fmt, ap);
-       #endif
 
        // null terminate the buffer
        *b.buf = '\0';
@@ -317,7 +304,7 @@ int vsnprintf(char *buf, int n, const char *fmt, va_list ap)
        return b.cnt;
 }
 
-int snprintf(char *buf, int n, const char *fmt, ...)
+int snprintf(char *buf, size_t n, const char *fmt, ...)
 {
        va_list ap;
        int rc;
@@ -326,27 +313,32 @@ int snprintf(char *buf, int n, const char *fmt, ...)
        rc = vsnprintf(buf, n, fmt, ap);
        va_end(ap);
 
-       check_poison("snprintf");
        return rc;
 }
 
-/* convenience function: do a print, return the pointer to the end. */
+/* Convenience function: do a print, return the pointer to the null at the end.
+ *
+ * Unlike snprintf(), when we overflow, this doesn't return the 'end' where we
+ * would have written to.  Instead, we'll return 'end - 1', which is the last
+ * byte, and enforce the null-termination.  */
 char *seprintf(char *buf, char *end, const char *fmt, ...)
 {
        va_list ap;
        int rc;
-       int n = end - buf;
-
-       if (n <= 0)
-               return buf;
+       size_t n = end - buf;
 
        va_start(ap, fmt);
        rc = vsnprintf(buf, n, fmt, ap);
        va_end(ap);
-       check_poison("seprintf");
 
-       if (rc >= 0)
-               return buf + rc;
-       else
+       /* Some error - leave them where they were. */
+       if (rc < 0)
                return buf;
+       /* Overflow - put them at the end */
+       if (rc >= n) {
+               *(end - 1) = '\0';
+               return end - 1;
+       }
+       assert(buf[rc] == '\0');
+       return buf + rc;
 }